|
Before Collections made its most welcome debut, the standard methods for grouping Java objects were via the array, the Vector, and the Hashtable. All three of these collections have different methods and syntax for accessing members: arrays use the square bracket ([]) symbols, Vector uses the elementAt method, and Hashtable uses get and put methods. These differences have long led programmers down the path to inconsistency in implementing their own collections -- some emulate the Vector access methods and some emulate the Enumeration interface. In Java performance programming, Part 2, we looked into the basics of casting -- what it is, what it costs, and some ways you can reduce your use of it. This month, we'll cover the related issue of collections in Java, looking first at the basic collections support supplied in the original versions of Java, then moving on to the vastly improved support added in JDK 1.2. Looking ahead, we'll also examine the current state of the Java Community Process proposal for generic types in Java -- including both the benefits we're likely to see from this proposal and the areas in which it perhaps falls short. When Java was born, the only data structure support built into the core libraries involved arrays, vectors (essentially dynamically-sized arrays), and hash tables (for key-value pairs). Support for such key data structures as balanced trees, generic bags of objects, and priority queues didn't exist, but could certainly be created. Thus, many developers added their own structures to the libraries. Caching is a time-honored method for improving the performance of an application. Instead of constantly creating and destroying objects as needed, a cache holds on to them and reuses them when appropriate. The Web browser you're running right now uses caching in a number of ways. For example, the JavaWorld logo at the top of this page appears at the top of all the articles. But your browser doesn't reload the image as you travel from one article to the next. The browser caches the image and reuses it when needed. For my actual keys, I'll generate multicharacter strings by using lowercase letters (a to z). For example, a collection of all four-character strings would generate a collection of 26 x 26 x 26 x 26 = 456976 four-character strings. The values will simply be an integer counter that increments by one as each string is added to the Map. I'll query that Map for the summation of integer values for those strings that contain any of the substrings ie, xy, or pq. I've elected to use a Hashtable object to hold the collection for the start of the tests. Editor's Note: In last week's excerpt from Chapter 5 of Java Generics and Collections, authors Maurice Naftalin and Philip Wadler considered the situation of having a code-base that doesn't use generics, and how you can migrate to generics without having to cut completely over in one release. They portray the situation with simple examples of "library" and "client" code, and then consider the migration of the library to generics while leaving the client non-genericized. In this second part of the excerpt, they move on to the trickier case: genericizing the client, while leaving the library alone. Editor's Note: In their new book Java Generics and Collections, authors Maurice Naftalin and Philip Wadler offer the thorough introduction to the syntax and semantics of Java 5.0 generics that you'd expect from an in-depth book on the topic. But they go a step further by considering the real-world, practical concerns of using generics in your work. Unless you're starting a project from scratch in Java 5.0, odds are you have a legacy code-base that does not currently use generics. Is bulk-converting it to use generics in one release a realistic option? Assuming it's not, you need to consider your options for a gradual introduction. Fortunately, the implementation of generics makes this eminently practical, as they describe in Chapter 5, "Evolution, Not Revolution," which we are excerpting over the course of the next two weeks on ONJava. Editor's Note: In last week's excerpt from Chapter 5 of Java Generics and Collections, authors Maurice Naftalin and Philip Wadler considered the situation of having a code-base that doesn't use generics, and how you can migrate to generics without having to cut completely over in one release. They portray the situation with simple examples of "library" and "client" code, and then consider the migration of the library to generics while leaving the client non-genericized. In this second part of the excerpt, they move on to the trickier case: genericizing the client, while leaving the library alone. This last loop relies on the normal situation, where List objects cannot change in size while they are being run without an exception of some sort occuring. So, since the loop size remains the same, you can simply count the accessed elements without testing at each iteration whether the end of the list has been reached. This last loop is generally faster than the one in Example 2. In the context of the RandomAccess interface, the first loop using List.get() should be faster than both of the loops that use Iterator.next() for a list to implement RandomAccess. Editor's note: Sometimes the most interesting discussions begin when someone says, "This may be a stupid question, but ...." If the person asking the question has taken the time to think about the problem before asking, the question is often not stupid at all. The uncertainty points out an ambiguity in the specs, holes in the docs, or a search for how more experienced programmers might address a particular problem. From time to time, we will print one of the "(Not So) Stupid Questions" we receive and invite our readers to answer the question in the feedback section. Although the previous chapter covered object initialization in great detail, it didn't quite cover all ways to initialize objects in Java, because it didn't cover all ways to create objects in Java. Aside from the new operator, which was the focus of the last chapter, Java offers two other ways to create objects: clone(), which is described in this chapter, and newInstance(), which is described in Part IV. In the InnerClasses/ex2 directory, change the implementation of the collection in CoffeeCup from ArrayList to LinkedList. In the CoffeeCupIterator inner class, implement the remove() method so that it actually removes the element rather than throwing UnsupportedOperationException. Change the Example2 program so that it iterates through the collection twice. The first time you iterate, remove all PaperClips. The second time you iterate, just print out the return value of toString() invoked on all the objects in the collection. For the most part, again, that's it. Inheritance doesn't have any effect on updates, deletes, or activation depth, just the query aspect of objects. But recall that the Java platform offers two forms of inheritance: implementation inheritance, via the ubiquitous extends clause, and interface inheritance, via implements. If db4o supports extends, then it also must support implements, which makes for a pretty powerful query capability, as you'll see. This tutorial takes you on an extended tour of the Java Collections Framework, introduced with the Java? 2 platform, which provides a well-designed set of interfaces and classes for storing and manipulating groups of data. Author and consultant John Zukowski expertly teaches you how to use the concrete collection implementations, how to apply sorting and searching through collections, and how to use read-only and thread-safe collections. A complete set of exercises follows the tutorial to let you test your knowledge. The Collections framework introduced iterators for traversing a list or other collection, which optimizes the process of iterating through the elements in a collection. However, the iterators implemented in the java.util Collections classes are fail-fast, which means that if one thread changes a collection while another thread is traversing it through an Iterator, the next Iterator.hasNext() or Iterator.next() call will throw ConcurrentModificationException. Just as with the previous example, if you want to prevent ConcurrentModificationException, you must lock the entire List while you are iterating by wrapping it with a synchronized block that synchronizes on the List l. (Alternatively, you can invoke List.toArray() and iterate on the array without synchronization, but this could be expensive if the list is large.) In this article, I continue my introduction to the storage and manipulation of structured objects in db4o, starting with a look at multiplicity relationships, where objects hold collections of objects as fields. (A collection in this case refers to both Collection classes like ArrayList and the standard language arrays.) You will see that db4o handles multiplicity without much difficulty. You'll also become more familiar with db4o's handling of cascading updates and activation depth. The Java Collections Framework has evolved considerably since its initial release with the Java 2 platform, Version 1.2. With the Java SE 5 release, generics enhanced the framework, and the introduction of java.util.concurrent added direct concurrency support (see Resources). With Java SE 6, the framework adds better bidirectional collection access. This article introduces you to all of these aspects of the collections library and helps you to take advantage of the most popular concurrency-related features. In July's installment of Java theory and practice ("Concurrent collections classes"), we reviewed scalability bottlenecks and discussed how to achieve higher concurrency and throughput in shared data structures. Sometimes, the best way to learn is to examine the work of the experts, so this month we're going to look at the implementation of ConcurrentHashMap from Doug Lea's util.concurrent package. A version of ConcurrentHashMap optimized for the new Java Memory Model (JMM), which is being specified by JSR 133, will be included in the java.util.concurrent package in JDK 1.5; the version in util.concurrent has been audited for thread-safety under both the old and new memory models. Recall that Vectors had some potentially thorny issues relating to their use with threads. Whereas ArrayLists can be used in a way similar to Vectors, they are designed for use within a single thread. Vectors are able to be synchronized; however; there are certain side effects to deal with, including performance issues. These wrappers hold a single value. Obviously, there are times when you are interested in working with multiple values—this is where the term collections enters the discussion. In this article, you will start the exploration of how objects relate to collections. This is a direct progression from the discussion of primitives. You will start by covering arrays and will consider the advantages and disadvantages of the original concept of an array. Next, you will cover how collections such as ArrayLists provide additional functionality. Here is where the primitive wrappers come in play. If the Vector does not like to accept raw primitives, you need to send it an Object. However, if you really want a Vector of integers, you have to wrap the integers in Integer objects. Look at the code in Listing 5. With the new collections framework included in the Java 2 platform, developers finally have at their fingertips a good suite of data structure implementations and interfaces from which further classes can be built. In the next part of our lesson (The Java Collections Framework: Implementations), we'll examine the new classes that form the collections framework, such as linked lists and trees. We'll also look at how you can safely use collections in a multithreaded environment and how to convert from a java.util.concurrent.ConcurrentSkipListSet is a thread-safe implementation of the SortedSet interface. Multiple threads can add and remove objects at the same time and work correctly. However, individual search, add, and remove operations take longer for a ConcurrentSkipListSet than for a TreeSet, so you usually should not use ConcurrentSkipListSet unless concurrency is an issue. In part one of this article, we covered the basic interfaces provided by the collections framework. Next, we'll examine the data structure implementations that the new collections framework provides, and some examples of their usage. Some implementations of Collection allow duplicate elements, and others do not. Implementations of the List interface (such as ArrayList) allow duplicate elements. Implements of Set and SortedSet (such as TreeSet) do not allow duplicate elements. This was illustrated in a previous lesson entitled Data Structures in Java: Part 5, The Core Collection Interfaces. Instances of some classes are considered equal if they contain the same values. The java.lang.Integer and java.lang.String classes are examples of this. Classes of this sort should override the equals method with an implementation that returns true if two instances contain the same value. For example, the Integer classs equals method will return true when comparing two different Integer objects that contain the value 27. Sun introduced a much-improved framework for working with collections of objects in Java 2. It was pretty clear that the Vector and Hashtable workhorse collections in JDK 1.1 and earlier were not well-suited to all tasks. The main thrust of the framework, however, is not to provide a better set of concrete collection class implementations ? which it does ? but instead to provide a standard interface for collections. I won't spend too long on the theory of the Java collection framework. There are many Java books that cover this topic very well. Suffice to say that Java collections are part of a framework that provides an abstraction for the storage and manipulation of different types of objects. The highest level of the Java collection framework is an interface called Collection WeakHashMap uses weak references to hold its keys, making it one of the few classes able to respond to the fluctuating memory requirements of the JVM. This can make WeakHashMap unpredictable at times, unless you know exactly what you are doing with it. In the following sections I examine how best to use WeakHashMap, and why WeakHashMap behaves the way it does. The standard java package, java.util, comes with a group of useful data structure classes (e.g. Stack, LinkedList, HashSet, and TreeSet classes). Unfortunately the Queue class is not implemented within this standard package. In this article we discuss three approaches to implement a Queue class. |
| w___w_w___.___j_ava_2_s__.c__om__ | Contact Us |
| Copyright 2009 - 12 Demo Source and Support. All rights reserved. |
| All other trademarks are property of their respective owners. |