5.0.0 (March 2014)
Binaries
Javadoc
JDiff
API differences between 4.0.0 and 5.0.0
Acquiring GS Collections
Maven
<dependency>
<groupId>com.goldmansachs</groupId>
<artifactId>gs-collections-api</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>com.goldmansachs</groupId>
<artifactId>gs-collections</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>com.goldmansachs</groupId>
<artifactId>gs-collections-testutils</artifactId>
<version>5.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.goldmansachs</groupId>
<artifactId>gs-collections-forkjoin</artifactId>
<version>5.0.0</version>
</dependency>
Ivy
<dependency org="com.goldmansachs" name="gs-collections-api" rev="5.0.0" />
<dependency org="com.goldmansachs" name="gs-collections" rev="5.0.0" />
<dependency org="com.goldmansachs" name="gs-collections-testutils" rev="5.0.0" />
<dependency org="com.goldmansachs" name="gs-collections-forkjoin" rev="5.0.0"/>
New Functionality
Parallel-Lazy Iteration
Previous versions of GS Collections included parallel evaluation and lazy evaluation as separate features. Parallel-eager utility has been available through the ParallelIterate
utility class. Serial-lazy evaluation has been available through LazyIterable
, the view returned by RichIterable.asLazy()
. GS Collections 5.0 adds parallel-lazy evaluation through ParallelIterable
, the view returned by asParallel(ExecutorService, int batchSize)
. The method asParallel
is not on interfaces like RichIterable
yet, but rather on a few supported collections, including FastList
and UnifiedSet
.
FastList<Integer> integers = FastList.newListWith(1, 2, 3, 4, 5, 6, 7, 8, 9);
ExecutorService threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
int batchSize = 2;
ParallelListIterable<Integer> parallelListIterable = integers.asParallel(threadPool, batchSize);
ParallelListIterable<Integer> evenNumbers = parallelListIterable.select(each -> each % 2 == 0); // deferred evaluation
ParallelListIterable<String> evenStrings = evenNumbers.collect(Object::toString); // deferred evaluation
MutableList<String> strings = evenStrings.toList(); // forced evaluation
threadPool.shutdown();
Assert.assertEquals(FastList.newListWith("2", "4", "6", "8"), strings);
The calls to select
and collect
are lazy, indicated by the fact that they return subclasses of ParallelIterable
. The call to toList()
forces evaluation.
The two parameters to asParallel
are used to configure parallelism. The code example above sets up a thread pool with one thread per core, which is appropriate for CPU bound tasks. It's possible to configure the thread pool for IO bound tasks, and to share thread pools between multiple calls to asParallel.
The batch size determines the number of elements from the backing collection that get processed by each task submitted to the thread pool. The appropriate batch size for CPU-bound tasks will be much larger, usually 10,000 to 100,000. The right batch size should be determined through thorough performance testing.
NOTE: The new parallel-lazy API is experimental and is tagged as @Beta
. Until we remove the @Beta
annotation, we reserve the right to make incompatible changes to the parallel-lazy API even in minor versions of GS Collections.
API
anySatisfyWith
, allSatisfyWith
, noneSatisfyWith
, countWith
, partitionWith
, detectWith
, and detectWithIfNone
on RichIterable
These are the two-argument forms of anySatisfy
, allSatisfy
, noneSatisfy
, count
, partition
, detect
and detectIfNone
. They take a Predicate2
instead of a Predicate
, and a second argument, which is passed as the second argument to the Predicate2
. The two argument forms allow reusing some code blocks that would otherwise have one differing parameter, resulting in less garbage creation. Some of these methods already existed on MutableCollection
and were pulled up to RichIterable
. Here is a comparison between anySatisfy
and anySatisfyWith
.
Assert.assertTrue(this.newWith(1, 2, 3).anySatisfyWith(Predicates2.equal(), 2));
Assert.assertTrue(this.newWith(1, 2, 3).anySatisfy(Predicates.equal(2)));
RichIterable.collect<Primitive>(<Primitive>Function, Mutable<Primitive>Collection target)
The new overload RichIterable.collect<Primitive>(<Primitive>Function, Mutable<Primitive>Collection target)
is similar to collect<Primitive>(<Primitive>Function)
, except that the results are gathered into the specified target collection.
ListIterable.toImmutable()
, SortedSetIterable.toImmutable()
, UnsortedSetIterable.toImmutable()
, SortedMapIterable.toImmutable()
, UnsortedMapIterable.toImmutable()
, StackIterable.toImmutable()
Previously, toImmutable()
only existed on MutableCollection
s. It's now available on the read-only interfaces as well. When called on an immutable collection, it returns the same instance.
MutableStack<Integer> mutableStack = Stacks.mutable.with(1, 2, 3);
Verify.assertInstanceOf(ImmutableStack.class, mutableStack.toImmutable());
Assert.assertNotSame(mutableStack, mutableStack.toImmutable());
StackIterable<Integer> immutableStack = Stacks.immutable.with(1, 2, 3);
Assert.assertSame(immutableStack, immutableStack.toImmutable());
ListIterable.binarySearch(T)
and ListIterable.binarySearch(T, Comparator)
Similar to java.util.Collections.binarySearch
, but available from the object-oriented API.
LazyIterable.distinct()
and LazyIterate.distinct(Iterable)
Similar to toSet(),
but returns a LazyIterable
(does not force evaluation).
MapIterable.flip()
Returns a new associative array where the position of the keys and values have been flipped. Since the values in the MapIterable
are not necessarily unique, flip()
returns a Multimap
instead of a MapIterable
. Since the keys in the MapIterable
are unique, flip()
returns a SetMultimap
instead of the more general Multimap
interface. In summary, MapIterable<K, V>.flip()
returns SetMultimap<V, K>
.
MutableSetMultimap<String, String> expected = UnifiedSetMultimap.newMultimap();
expected.put("odd", "One");
expected.put("even", "Two");
expected.put("odd", "Three");
expected.put("even", "Four");
Assert.assertEquals(expected, UnifiedMap.newWithKeysValues("One", "odd", "Two", "even", "Three", "odd", "Four", "even").flip());
MapIterable.flipUniqueValues()
Similar to MapIterable.flip()
but asserts that the values in the MapIterable
are unique and thus returns MapIterable
instead of Multimap
. Throws IllegalArgumentException
if the MapIterable
contains duplicate values.
MapIterable<Integer, String> map = this.newMapWithKeysValues(1, "1", 2, "2", 3, "3");
MapIterable<String, Integer> flip = map.flipUniqueValues();
Assert.assertEquals(UnifiedMap.newWithKeysValues("1", 1, "2", 2, "3", 3), flip);
MutableMap.getIfAbsentPut(K key, V value)
Gets and returns the value in the map at the specified key. If the map does not contain the key, getIfAbsentPut()
puts the value in the map and returns it. Similar to getIfAbsentPut(K key, Function0<? extends V> function)
, but takes in a value directly instead of a value factory (Function0
).
MutableMap<Integer, String> map = UnifiedMap.newWithKeysValues(1, "1", 2, "2", 3, "3");
Assert.assertEquals("4", map.getIfAbsentPut(4, "4")); // mutates
Assert.assertEquals("3", map.getIfAbsentPut(3, "5")); // does not mutate
Verify.assertContainsKeyValue(3, "3", map);
Verify.assertContainsKeyValue(4, "4", map);
MutableMap.add(Pair<K, V>)
Adds the key-value pair to the map. It's a convenience method for working with Pair
s, similar to put(K, V)
.
MutableMap<String, Integer> map = this.newMapWithKeyValue("A", 1);
Assert.assertEquals(Integer.valueOf(1), map.add(Tuples.pair("A", 3)));
Assert.assertNull(map.add(Tuples.pair("B", 2)));
Verify.assertMapsEqual(UnifiedMap.newWithKeysValues("A", 3, "B", 2), map);
MutableBag.setOccurrences(T item, int occurrences)
Mutates the bag to contain the given number of occurrences of the item. Returns true
if the bag has been modified as a result of the call to setOccurrences()
.
MutableBag<String> bag = HashBag.newBag();
MutableBag<String> expected = this.newWith("betamax-tape", "betamax-tape");
Assert.assertTrue(bag.setOccurrences("betamax-tape", 2));
Assert.assertEquals(expected, bag);
ListIterate.reverseForEachWithIndex(List, ObjectIntProcedure)
Iterates over the list in reverse order executing the ObjectIntProcedure
for each element. The index passed into the ObjectIntProcedure
is the actual index of the range.
Primitive API
Mutable<Primitive>Collection.retainAll
Like Collection.retainAll
, but for primitive collections. There are two variants, one that takes a <Primitive>Iterable
, and another that takes varargs.
Assert.assertTrue(collection.retainAll(IntArrayList.newListWith(1, 2, 5)));
Assert.assertEquals(this.newMutableCollectionWith(1, 2), collection);
MutableIntCollection collection = this.newMutableCollectionWith(1, 2, 3);
Assert.assertFalse(collection.retainAll(1, 2, 3));
Assert.assertEquals(this.newMutableCollectionWith(1, 2, 3), collection);
keysView()
and keyValuesView()
on primitive maps
Returns a lazy view of keys or key/value pairs respectively.
keySet()
and values()
on synchronized, unmodifiable, and immutable primitive maps
These methods already existed on the API but threw UnsupportedOperationException
s in places. They are fully supported now.
addToValue(key, amount)
on mutable primitive maps
Adds the given amount to the value at the given key and returns the updated value. This method exists only for maps where the values are numeric types (not boolean
or Object
).
MutableByteIntMap map = new ByteIntHashMap();
Assert.assertEquals(1, map.addToValue((byte) 0, 1));
Assert.assertEquals(ByteIntHashMap.newWithKeysValues((byte) 0, 1), map);
Assert.assertEquals(11, map.addToValue((byte) 0, 10));
Assert.assertEquals(ByteIntHashMap.newWithKeysValues((byte) 0, 11), map);
Mutable<Primitive>ObjectMap.putAll(<Primitive>ObjectMap)
putAll()
was already implemented on MutableObject<Primitive>Map
and <Primitive><Primitive>Map
. This rounds out the API.
Reversible<Primitive>Iterable.asReversed()
Returns a reversed view of the primitive iterable. Like ReversibleIterable.asReversed()
, but for the primitive API.
Reversible<Primitive>Iterable.toReversed()
Returns a reversed copy of the primitive iterable. Like asReversed()
but executes eagerly.
<Primitive>Iterable.injectInto
injectInto
was already implemented on primitive collections and has now been pulled up to the <Primitive>Iterable
interfaces.
IntIterable arrayList = IntArrayList.newListWith(1, 2, 3);
MutableInteger sum = arrayList.injectInto(new MutableInteger(0), new ObjectIntToObjectFunction<MutableInteger, MutableInteger>()
{
public MutableInteger valueOf(MutableInteger object, int value)
{
return object.add(value);
}
});
Assert.assertEquals(new MutableInteger(6), sum);
Code Block Factory Methods
Functions.nullSafe(Function)
Returns a null-safe wrapper around the given Function
. The wrapper delegates to the valueOf()
method of the delegate Function
only if the parameter is not null
, otherwise it returns null
or the provided null
replacement value.
MutableList<Integer> squares = FastList.newListWith(1, 2, null, 3).collect(Functions.nullSafe(Functions.squaredInteger()));
Assert.assertEquals(FastList.newListWith(1, 4, null, 9), squares);
MutableList<Integer> squaresWithNullValue =
FastList.newListWith(1, 2, null, 3).collect(Functions.nullSafe(Functions.squaredInteger(), 0));
Assert.assertEquals(FastList.newListWith(1, 4, 0, 9), squaresWithNullValue);
Functions2.integerAddition()
Creates a Function2
that takes in two Integer
s as parameters and returns their sum.
HashingStrategies.chain(HashingStrategy...)
Takes a vararg number of hashing strategies and returns a wrapper HashingStrategy
that considers objects as equal if all the delegate strategies consider them equal. The hashcode is computed using a strategy similar to code-generated hashCode()
methods. It start with the first delegate strategy's computed hashcode, and then repeatedly multiplies the accumulated hashcode by 31
and adds the next computed hashcode for each remaining delegate strategy.
HashingStrategies.identityStrategy()
Returns a HashingStrategy
that considers objects as equal only if they are the same object. It uses reference equality in the implementation of equals
, and System.identityHashCode
in the implementation of computeHashCode
.
HashingStrategies.fromFunctions(Function, Function)
and HashingStrategies.fromFunctions(Function, Function, Function)
Creates a HashingStrategy
from each Function
, chains them, and returns the chain.
HashingStrategies.from<Primitive>Function
Similar to HashingStrategies.fromFunction(Function)
but optimized for primitives. Implemented in a way that calls to equals
and computeHashCode
do not create any garbage.
ReversibleIterable
and SortedIterable
Interfaces
Two new interfaces have been introduced in the GS Collections Hierarchy. ReversibleIterable
is an ordered iterable which can be traversed efficiently forwards or backwards. ReversibleIterable
has extra API for iterating from the end like asReversed()
and reverseForEach()
. Lists are the most common ReversibleIterable
s, and ListIterable
extends ReversibleIterable
.
SortedIterable
, is an ordered iterable where the elements are stored in sorted order. Its method comparator()
returns the Comparator
used to sort the elements, or null
if they are sorted in natural order. SortedSetIterable
and SortedBag
extend SortedIterable
.
Changes in the Bag
Interface Hierarchy
The inheritance hierarchy of Bag
s is now more consistent with Set
s. We introduced a new interface UnsortedBag
, as a sibling of SortedBag
. Bag
no longer overrides collect
, so it returns RichIterable
instead of Bag
. SortedBag.collect
returns ListIterable
instead of Bag
. UnsortedBag.collect
returns UnsortedBag
, which means the implementations of UnsortedBag.collect
are unchanged. The new interface structure is as follows:
The change to the interface hierarchy changed the serialized forms of TreeBag
and UnmodifiableSortedBag
.
Changes to serialized forms
Serialized form of synchronized and unmodifiable collections
The hierarchy of synchronized and unmodifiable collections has been changed slightly. AbstractSynchronizedMutableCollection
has been extracted from SynchronizedMutableCollection
, and all the classes that used to extend SynchronizedMutableCollection
like SynchronizedMutableList
and SynchronizedMutableSet
now extend AbstractSynchronizedMutableCollection
directly. Similar changes have been made to the unmodifiable collections.
Serialized form of immutable collections
The serialized forms of ImmutableSet
, ImmutableBag
and ImmutableMap
implementations have been changed.
The ImmutableSet
implementations now use a proxy class for serialization.
The implementations of ImmutableMap
and ImmutableBag
already used proxy classes for serialization. These proxy classes were inner classes inside AbstractImmutableMap
and AbstractImmutableBag
respectively. These proxy classes were moved to top level, package private classes.