Showing posts with label collections. Show all posts
Showing posts with label collections. Show all posts

Saturday, October 17, 2009

Collections.synchronizedXxx() methods are bad for concurrent scenarios (Java)

The java.util.Collections class provides easy factory methods for collections and maps. In this post, I am trying to highlight why the synchronizedXxx() methods are bad for concurrent scenarios. If you look at the source code of Sun JDK and IBM JDK (I haven't checked Oracle/BEA's) for these methods:


Collections.synchronizedList
Collections.synchronizedSet
Collections.synchronizedMap


you will find they essentially maintain a storage-object-level mutex -- every access obtains a lock upon that mutex to maintain thread-safety before going ahead with the operation. For concurrent scenarios, this is a disaster because you can only invoke one operation at a time.

Fortunately, there are few alternatives you can look at right inside the JDK. For maps, you can use:


java.util.concurrent.ConcurrentHashMap


and for lists or sets, you can look at these:


java.util.concurrent.CopyOnWriteArrayList
java.util.concurrent.CopyOnWriteArraySet


Java 6 users can also use these (Note: they are not O(1), but rather O(log(n)) operations):


java.util.concurrent.ConcurrentSkipListMap
java.util.concurrent.ConcurrentSkipListSet


However, there are few points worth knowing:

1. CopyOnWriteXxx collections are suited only for those scenarios where the read operations hugely outnumber the write operations.

2. Concurrent scenarios are better dealt with a strategy at application architecture level rather than brute force use of concurrency-optimized collections.

3. For concurrent scenarios, isolate operations with lifecycle-managed threads each dealing with immutable objects and/or small concurrent datasets rather than giant thread-safe ones. Minimize obtaining of locks rather than making everything thread-safe.

Please let me know what you think.

Thursday, October 1, 2009

Fun with Iterators - UnionIterator

The problem
You want to create an iterator for a union of iterators. For example, you have 5 iterators and you want to create an iterator so that it represents elements in all 5 of them in linear fashion.

Solution
(For brevity and simplicity, the UnionIterator is a readonly iterator.)


1 import java.util.ArrayList;
2 import java.util.Arrays;
3 import java.util.Iterator;
4 import java.util.List;
5 import java.util.NoSuchElementException;
6
7 public class ReadonlyUnionIterator<E> implements Iterator<E> {
8
9 private final Iterator<? extends Iterator<E>> parts;
10
11 private Iterator<E> current = null;
12
13 public ReadonlyUnionIterator(final Iterator<? extends Iterator<E>> parts) {
14 this.parts = parts;
15 alignForwardCurrent();
16 }
17
18 public static <E>ReadonlyUnionIterator<E> fromIterables(
19 final Iterable<? extends Iterable<E>> iterableParts) {
20 final List<Iterator<E>> itlist = new ArrayList<Iterator<E>>();
21 for (final Iterable<E> each: iterableParts) {
22 itlist.add(each.iterator());
23 }
24 return new ReadonlyUnionIterator<E>(itlist.iterator());
25 }
26
27 public static <E>ReadonlyUnionIterator<E> fromIterables(
28 final Iterable<E>... parts) {
29 return fromIterables(Arrays.asList(parts));
30 }
31
32 /**
33 * Aligns 'current' by advancing to a non-empty element.
34 * @return true if current has next element, false otherwise
35 */

36 private synchronized boolean alignForwardCurrent() {
37 while (parts.hasNext()) { // parts has next, but they may be empty!
38 current = parts.next();
39 if (current.hasNext()) { return true; }
40 }
41 current = null;
42 return false;
43 }
44
45 public synchronized boolean hasNext() {
46 // NULL is end-of-union marker for 'current'
47 if (current == null) { return false; }
48 if (current.hasNext()) { return true; }
49 // control reached here means current does not have next
50 return alignForwardCurrent();
51 }
52
53 public synchronized E next() {
54 if (hasNext()) { return current.next(); }
55 throw new NoSuchElementException("No more element available");
56 }
57
58 public void remove() {
59 throw new UnsupportedOperationException(
60 "Readonly iterator -- remove() is not supported");
61 }
62
63 }

Wednesday, August 19, 2009

Almost literals - Initializing Java collections

Dynamic languages like Python and Ruby provide built-in syntax to express common data structures as literals. However, Java provides literal syntax only for arrays.

Although not pretty, here is how you can quickly initialize Java collections:


import java.util.*;

public class Literals {
private static class HashMapNow<K, V> extends HashMap<K, V> {
public HashMapNow<K, V> with(K key, V value) {
put(key, value);
return this;
}
}

public static void main(String[] args) {
// initialize a list
List<String> list = Arrays.asList("a", "b", "c", "c");

// initialize a set
Set<String> set = new HashSet<String>(Arrays.asList("a", "b", "c", "c"));

// initialize a map
Map<String, String> map = new HashMapNow<String, String>()
.with("a", "1")
.with("b", "2")
.with("c", "3")
.with("c", "4");

// print them all
System.out.println(list);
System.out.println(set);
System.out.println(map);
}
}


2009 Aug 25 [Update]: As suggested by Eric (in comments below), the initialization can be done in the static block - though it is admittedly quirkier. Cleaner options are available in Google Collections and LambdaJ.

Disqus for Char Sequence