Java for C# Programmers 10: Collections

Collections

Collections in Java involve several inconsistencies which seem quite odd for a C# developer. To understand some of the oddities of Java collections, you should keep in mind that Java is an old framework and at some points in its evolution, some new paradigms have been introduced and some old paradigms have been changed.

Before Java 5, you had collections that had Object as contained type. Later Java versions have generic collections and generic collection-interfaces. I’ll talk only about the generics here, but am leaving away the <brackets> mostly.

Collection Interfaces

Modern Java collection handling is based completely on generic interfaces. The basic interface for collections is Collection.

From Collection derived interfaces are

List, Set, Queue, Deque, SortedSet, NavigableSet and the thread-safe interfaces concurrent.BlockingQueue, concurrent.BlockingDeque, concurrent.TransferQueue.

The following table shows the interfaces and the implementations available in Java SE 7. The names are mostly self-explanatory but where needed, some explanations are given under the table.

Interface Implementing Class C# Analogon
Collection All of the following ICollection
List LinkedList, ArrayList, Vector, Stack IList
Set HashSet, TreeSet, LinkedHashSet ISet
Queue ArrayDeque, LinkedList Queue class
Deque ArrayDeque, LinkedList LinkedList class
  • ArrayList and Vector are nearly the same, the main difference is that Vector is synchronized and ArrayList is not.
  • Stack extends Vector and so is synchronized, too.
  • LinkedHashSet is implemented as a hash table with an added linked list. The linked list orders its elements in the order the elements have been added to the LinkedHashSet.

Map Interface

The Map interface is equivalent to C#’s IDictionary. Map is the base interface of these extended interfaces: SortedMap, NavigableMap, concurrent.ConcurrentMap and concurrent.ConcurrentNavigableMap.

Gotcha: Strange, but true: a Map is not a Collection in Java.

HashMap, Hashtable, TreeMap, WeakHashMap, Properties and LinkedHashMap are the most important implementations of the Map interface.

  • The difference between Hashtable and HashMap is: Hashtable is synchronized, HashMap is not.
  • A TreeMap is implemented as tree. It is ordered by key.
  • LinkedHashMap keeps a linked list which is ordered by the order of insertion of elements into the list.
  • A WeakHashMap stores keys as weak references. The C# analogon to a WeakHashMap<K,V> is a Dictionary<K, WeakReference<V>>.
    Gotcha: The WeakHashMap<K,V> automatically removes an entry if the entry’s key is garbage collected. Of course, a Dictionary<K, WeakReference<V>> does not remove entries automatically.
  • Properties can be used for ini-files.

Map Example

Map<Integer, String> h = new HashMap<Integer, String>();

h.put(20097, "Hamburg");
h.put(76307, "Karlsbad-Auerbach");
h.put(76307, "Karlsbad");

if (h.containsKey(20097))
    System.out.println(h.get(20097));     // Hamburg

System.out.println(h.get(22222));   //  null
System.out.println(h.get(76307));   //  Karlsbad

Set<Integer> keys = h.keySet(); 
// -> { 20097, 76307 } in unpredictable order.

Collection<String> values = h.values();  
// -> { "Hamburg", "Karlsbad" } in unpredictable order.

Set<Map.Entry<Integer,String>> entries = h.entrySet();  
// -> { {20097, "Hamburg"},  {76307, "Karlsbad"} }
//    in unpredictable order. 

Properties class

The Properties class is a Map that according to docs shall only contain String-String pairs. Nevertheless, and strange but true: it is derived from Hashtable<Object, Object>, not from Hashtable<String, String>.

The Properties class can be used to store ini-like files on disk. An example on how to use Properties to read and write data follows.

Properties p = new Properties();
p.setProperty("a", "A");
p.setProperty("b", "xy");


static void StoreProperties(String filename, Properties prop)
       throws IOException
{
    FileWriter fw = null;
    try
    {
        fw = new FileWriter(filename);
        prop.store(fw, "This is a comment.");
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
    finally
    {
        if (fw != null)
            fw.close();  // may throw
    }
}


static void ReadProperties(String filename, Properties prop) 
       throws IOException
{
    prop.clear();
    Reader fr = null;
    try
    {
        fr = new FileReader(filename);
        prop.load(fr);
    }
    catch  (Exception e)
    {
        e.printStackTrace();
    }
    finally
    {
        if( fr != null)
            fr.close();  // may throw
    }
}

Dictionary

From Java 1.0 on, there has been the abstract class Dictionary which of course denotes the same concept as the Map. So you will find this in legacy code frequently. Nowadays, Dictionary is deprecated.

IEnumerable, Equals, Indexers, …

equals, hashCode and compareTo

The Java methods equals, hashCode and compareTo are analog to C#’s Equals, GetHashCode and CompareTo. Like in C#, if you use any of the generic containers you must be aware that these methods must be implemented on your classes. Otherwise strange things will happen.

IEnumerable, IEnumerator / Iterable, Iterator

Iterable<T> is the Java equivalent of C#’s IEnumerable<T> and Iterator<T> is the Java equivalent of IEnumerator<T>. The following two code snippets show their usage.

// Java
TreeSet<Integer> ts = ...;

Iterable<Integer> t = ts;
for(java.util.Iterator<Integer> it = t.iterator(); it.hasNext();)
        System.out.print(it.next() + " "); 


// C# 
List<int> a = ...;

IEnumerable<int>  t = a; 
for(IEnumerator<int> en = t.GetEnumerator(); en.MoveNext();)
    Console.Write(en.Current + " ");

Some further Gotchas:

  • Ordinary Java arrays do not implement IEnumerable. Nevertheless you can iterate over them by the extended for loop.
  • Collections of primitive types are not allowed in Java. You need to use the corresponding wrapper class. Example: List<Integer> = new LinkedList<Integer>();
  • Indexers do not exist in Java. Access to elements of array-like collections can be done by get(i) instead.