The Google Collections project has released an impressive new collection of features.
The new release has prompted me to continue this series. My favourite feature of the new release is ImmutableSet. It's a top-level implementation of
Set that's full of features - this class alone justifies adding the entire .jar to your application's libraries folder...For static constants
Before
public static final Set<Integer> LUCKY_NUMBERS;
static {
Set<Integer> luckyNumbersMutable = new HashSet<Integer>();
luckyNumbersMutable.add(4);
luckyNumbersMutable.add(8);
luckyNumbersMutable.add(15);
luckyNumbersMutable.add(16);
luckyNumbersMutable.add(23);
luckyNumbersMutable.add(42);
LUCKY_NUMBERS = Collections.unmodifiableSet(luckyNumbersMutable);
}After:
public static final ImmutableSet<Integer> LUCKY_NUMBERS
= ImmutableSet.of(4, 8, 15, 16, 23, 42);For defensive copies
Before:
private final Set<Integer> luckyNumbers;
public Hatch(Set<Integer> luckyNumbers) {
this.luckyNumbers = Collections.unmodifiableSet(new HashSet<Integer>(luckyNumbers));
}
public Set<Integer> getLuckyNumbers() {
return luckyNumbers;
}After:
private final ImmutableSet<Integer> luckyNumbers;
public Foo(Set<Integer> luckyNumbers) {
this.luckyNumbers = ImmutableSet.copyOf(luckyNumbers);
}
public ImmutableSet<Integer> getLuckyNumbers() {
return luckyNumbers;
}For more defensive copies
ImmutableSet.copyOf has been heavily optimized when the argument is itself an ImmutableSet. It turns out that this method can no-op if the argument is itself an ImmutableSet, since copying immutable objects is redundant. The more you use ImmutableSet, the cheaper it is to do defensive copies!For predictable insertion order
ImmutableSet's elements iterate in the same order that they're added in. That means there's less unpredictable behaviour in your application's business logic. There's no need to worry about tests that pass on one JVM and fail with another because of
HashSet's unspecified iteration order. ImmutableSet<Integer> numbers = ImmutableSet.of(8, 6, 7, 5, 3, 0, 9);
assertEquals("[8, 6, 7, 5, 3, 0, 9]", numbers.toString());For self-documenting APIs
Before:
/**
* Returns numbers most likely to be selected in upcoming lottery drawings.
*
* @return an immutable, non-empty set
*/
public Set<Integer> getLuckyNumbers();After:
/**
* Returns numbers most likely to be selected in upcoming lottery drawings.
*
* @return a non-empty set
*/
public ImmutableSet<Integer> getLuckyNumbers();For less memory allocation
ImmutableSet allocates fewer objects and smaller objects than either
HashSet or LinkedHashSet. This makes your program's heap smaller. There's also less work for your garbage collector.For non-null data
ImmutableSet does not permit null elements. This is almost always what you want, and helps to detect bugs early:
Before:
public Hatch(Set<Integer> numbers) {
for (Integer number : numbers) {
if (number == null) {
throw new NullPointerException();
}
}
this.numbers = Collections.unmodifiableSet(new LinkedHashSet<Integer>(numbers));
}After:
public Hatch(Set<Integer> numbers) {
this.numbers = ImmutableSet.copyOf(numbers);
}ImmutableSet is my new default collection, replacing
ArrayList. It's that good. And as a special treat, when I do need a List, Google Collections also includes ImmutableList.

3 comments:
Should your very first example use LinkedHashSet, to make it behave similarly to ImmutableSet?
Also in the first example, I typically use Arrays.asList(...) rather than calling add(...) six times in a row. (although ImmutableSet is still FAR nicer!)
Yup, the first example should use LinkedHashSet. And Arrays.asList() is a nice workaround, since it removes the need for the static initializer block. The before code is improved, but yeah, it's still pretty weak:
Before
public static finalSet<Integer> LUCKY_NUMBERS
= Collections.unmodifiableSet(new LinkedHashSet<Integer>(Arrays.asList(4, 8, 15, 16, 23, 42)));
Post a Comment