Wednesday, 10 December 2008

Java Collections By John Zukowski - Part4

Today i started reading Java Collections by John Zukowski.

Nice book written by John Zukowski.

Few important quotations i wanted to share from the next 2 chapters (7-8).

1) Framework Basics

The Collections Framework consists of three parts: interfaces, implementations, and algorithms. The interfaces are the abstract data types that the framework supports. The implementations are the concrete versions of these interfaces. The algorithms are the predefined actions that can be defined on either the interfaces or their implementations.

2) It's a collection of elements without duplicates. If you need an ordered Set, go to the SortedSet where you can define a sorting order.

3) Maps are collections of key−value pairs instead of elements. Because maps have two parts for each element, they are in their own interface hierarchy instead of inheriting from Collection. A Map is your basic collection of possibly sorted, or possibly not, key−value pairs. To ensure that the keys in the map are sorted, you can use a SortedMap.

4) Framework Implementations

Once you have a grasp of the interfaces that make up the framework, it's time to move on to the concrete implementations of these interfaces. Keeping the two concepts separate and understanding them well allows you to write code like the following:

List list = new ...();

You can change implementations when business conditions require a change without modifying any of the rest of your code.

5) All implementations are unsynchronized. While you can add synchronized access, you are not forced to be synchronized when you don't need it.

All implementations offer fail−fast iterators. If an underlying collection is modified while you are iterating through its elements, this will be detected upon the next access and cause the iteration to stop immediately by throwing a ConcurrentModificationException.

All implementations are serializable and cloneable, allowing you to save and copy collections freely.All implementations support having null elements. This was not an option with the historical collections.

The implementations rely on a concept of optional methods in interfaces. If an implementation doesn't support an operation, such as adding or removing elements, the implementation throws an UnsupportedOperationException when you call a method that hasn't been fully implemented.

6) Adding Single Elements

Adding a single element involves calling the add() method:

public boolean add(Object element)

The add() method takes a single argument, which is the element to be added. At the interface level for Collection, the handling of duplicates and positioning is undefined. Assuming the method doesn't throw an exception, the element is guaranteed to be in the collection after the method returns. If the collection is modified as a result of the call, true is returned. If it isn't modified, false is returned.As far as exceptions go, an UnsupportedOperationException may be thrown if the add operation isn't supported by the collection, possibly because it was made read−only. You should never get a NullPointerException though, as you can add null to the collection.

7) Adding Another Collection

You can add a group of elements from another collection with the addAll() method:

public boolean addAll(Collection c)

Each element in the passed−in collection will be added to the current collection. If the underlying collection changes, true is returned. If no elements are added, false is returned. Again, what happens with duplicates and ordering is unspecified. Here again, if adding elements is unsupported, you'll get an UnsupportedOperationException thrown.

8) Checking for Existence

The contains() method reports if a specific element is within the collection:

public boolean contains(Object element)

If found, contains() returns true, if not, contains() returns false. As with remove(), to determine if an element is in the collection, the element's equals() method is used for comparison.·

9) Finding Elements

If, instead of fetching elements in the collection, you only desire to know whether a specific element or set of elements is in the collection, you can find out through this next set of methods.

Checking for Existence

The contains() method reports if a specific element is within the collection:

public boolean contains(Object element)

If found, contains() returns true, if not, contains() returns false. As with remove(), to determine if an element
is in the collection, the element's equals() method is used for comparison.

10) Checking Size

To find out how many elements are in a collection, use the size() method:

public int size()

To check for no elements in a collection, the isEmpty() method can be used:

public boolean isEmpty()

You can think of the isEmpty() method as returning the following value:

return (size() == 0);

11) Copying and Cloning Collections

The Collection interface itself is neither Cloneable nor Serializable. You'll find all the system−defined,concrete implementations implementing both interfaces, but at the Collection interface−level, neither is implemented. If you need to make a copy of a collection, your best bet is to just pass the Collection off to the constructor of another collection and you've effectively cloned the collection. Another manner of copying elements out of a collection is through the toArray() methods:

public Object[] toArray()
public Object[] toArray(Object[] a)

The first toArray() method will return an Object array containing all the elements in the collection. The position in this array is not meant to imply any meaning from the ordering, it's just that elements in an array need a position. Because this method returns an Object [ ], every time you need to get an element out of the array, you need to cast it to the appropriate type. When all elements of a collection are of the same type, it is easier for you to work with an array of that specific type.

This is where the second version of toArray() comes in handy: Object[] toArray(Object[] a). With this version,the toArray() method consults with the passed−in array to determine the return type (and size). If the passed−in array is large enough to contain all the elements in the collection [collection.size() <= a.length], the elements are placed in the array and returned. If the array is too small, a new array of the same type will be created, sized to the current number of elements in the collection as reported by size(), and used to store all the elements. It is this new array that is returned in this second case, not the original array. If the array passed in is too large, the toArray() method replaces with null the element one beyond the last element copied (a[collection.size()] = null). This may be useful if you know there are no null elements in the collection and don't want to ask the collection its size.

Warning: If the elements of the collection are not assignment−compatible with the array type, an ArrayStoreException will be thrown.

12) ConcurrentModificationException

The ConcurrentModificationException has to do with the fail−fast nature of the collection iterators. If an underlying collection is modified outside the remove() method of the Iterator while you are iterating through its elements, this will be detected upon the next access and a ConcurrentModificationException will be thrown, thereby causing the iteration to stop.

To demonstrate, the following program causes a ConcurrentModificationException to be thrown. It starts by walking through the elements of an Iterator but adds an element after printing the first one. As this modifies the underlying collection, the iterator is immediately invalidated such that the second call to next() fails miserably (however, hasNext() still succeeds):

import java.util.*;
public class FailExample {
public static void main (String args[]) {
List list = new ArrayList(Arrays.asList(args));
Iterator i = list.iterator();
while (i.hasNext()) {
System.out.println(i.next());
list.add("Add");
}
}
}

Running the program with at least two elements on the command line will produce similar results:

13) The Set interface is the first type of Collection we'll discuss. This interface represents a group of elements without duplicates. There is nothing in the interface definition that forces the group to not have duplicates; it is the actual implementations that enforce this part of the definition. The elements of the group have no perceived order, though specific implementations of the interface may place an order on those items.

14) Creating a HashSet

The HashSet class provides four constructors broken into two sets. The first three constructors create empty sets of varying sizes:

public HashSet()
public HashSet(int initialCapacity)
public HashSet(int initialCapacity, int loadFactor)

If unspecified, the initial set size for storing elements will be the default size of a HashMap, which happens to be eleven or 101, depending upon what Java version you are using. When the set capacity reaches full and a new element is added, the internal structure will double in size before adding the new element (and copying in the old elements). If you don't like the 75%−full aspect, you can provide the constructor with a custom load factor.

15) Adding Another Collection

You can add a group of elements from another collection to the set with the addAll() method:

public boolean addAll(Collection c)

Each element in the collection passed in will be added to the current set via the equivalent of calling the add() method on each element. If the underlying set changes, true is returned. If no elements are added, false is returned. As with add(), if equal elements are in both sets, true is returned with the new element replacing the old element in the set.

An UnsupportedOperationException will be thrown when working with a set that doesn't support adding elements.

16) Removing Single Elements

Use the remove() method if you wish to remove a single element at a time:

public boolean remove(Object element)

Determining whether the element is in the set is done via the equals() method of the element. If the element is found, the element is removed from the set and true is returned. If not found, false is returned. If removal is not supported, you'll get an UnsupportedOperationException thrown whether the element is present or not.

17) Removing Another Collection

The third way to remove elements is with removeAll():

public boolean removeAll(Collection c)

The removeAll() method takes a Collection as an argument and removes from the set all instances of each element in the Collection passed in. The Collection passed in can be a Set or some other Collection implementation.

18) Checking Size

To find out how many elements are in a set, use the size() method:

public int size()

To combine the process of getting the size and checking for no elements in the set, use the isEmpty() method instead:

public boolean isEmpty()

You can think of the isEmpty() method as returning the following value, however, the use of isEmpty() is faster:

return (size() == 0);

19) Copying and Cloning Sets

There are many ways to duplicate a HashSet. You can clone it, serialize it, copy it, or simply call the previously shown copy constructor.

Hash sets are Cloneable and have a public clone() method:

20) Checking for Equality

The HashSet class defines equality through its equals() method:

public boolean equals(Object o)

A HashSet is equal to another object if the other object implements the Set interface, has the same size(), and contains all the same elements.

Calling the equals() method on a HashSet effectively calls the equals() method on each element within the set—or at least until one reports that there is no equivalent element in the passed−in set.

21) Creating a TreeSet

The TreeSet class provides four constructors broken into two sets. The first two constructors create empty tree sets:

public TreeSet()
public TreeSet(Comparator comp)

In order to maintain an ordering, elements added to a tree set must provide some way for the tree to order them. If the elements implement the Comparable interface, the first constructor is sufficient. If, however, the objects aren't comparable or you don't like the default ordering provided, you can pass along a custom Comparator to the constructor that will be used to keep elements ordered. Once the TreeSet is created, you
cannot change the comparator.

22) Adding Elements

To add a single element, call the add() method:

public boolean add(Object element)

The add() method for TreeSet is not that different from the add() method for HashSet. The key difference is that the element to add must implement the Comparable interface or the TreeSet constructor must have been passed a Comparator. If both of these are not true then a ClassCastException will be thrown. Not only must the single element be comparable, but each element must be comparable to every other element in the set. In other words, you can't have something like a Date and a Long in the same TreeSet without writing your own Comparator.

23) Comparing

The comparator() method allows you to get the current Comparator for the tree:

public Comparator comparator()

While this method isn't frequently called directly, if you're interested in finding out what comparator a TreeSet is using, you can ask, and then possibly create another tree with the same comparator. The method returns null if the natural ordering of the set elements is used.

24) Retrieving the Ends

You can use the first() and last() methods of TreeSet to get the elements at the ends of the tree:

public Object first()
public Object last()

The ends of the tree are defined by element comparing. Given that elements are added in order, either natural or otherwise, the end points naturally fall out of that. The first() element is the one that is ordered first in the set, while last() is last. A NoSuchElementException will be thrown by either method if there are no elements in the set.

25) Fetching Elements

The other way to fetch elements from a TreeSet is with the iterator() method:

public Iterator iterator()

Since the elements of a tree set are ordered, the order of the elements returned will be the order in which they are in the tree.

If we change our earlier example to use a TreeSet instead of a HashSet:

String elements[] = {"Irish Setter", "Poodle", "English Setter","Gordon Setter", "Pug"};
Set set = new TreeSet(Arrays.asList(elements));
Iterator iter = set.iterator();
while (iter.hasNext()) {
System.out.println(iter.next());
}

About the Author

John Zukowski has been involved with the Java platform since it was just called Java, 11 years and running, since 1995. He is actively working with SavaJe Technologies to finish up the JavaOne 2006 device of show: the Jasper S20 mobile phone. He currently writes a monthly column for Sun’s Core Java Technologies Tech Tips and Technology Fundamentals Newsletter. He has contributed content to numerous other sites, including jGuru, DevX, Intel, and JavaWorld. He has written many other popular titles on Java, including Java AWT Reference (O’Reilly), Mastering Java 2 (Sybex), Borlands’ JBuilder: No Experience Required (Sybex), Learn Java with JBuilder 6 (Apress), Java Collections (Apress), and The Definitive Guide to Swing (Apress).

No comments: