Skip to content

6.0.0 (February 2015)

Compare
Choose a tag to compare
@goldmansachs goldmansachs released this 09 Feb 21:24
· 115 commits to master since this release

Binaries

gs-collections-6.0.0.zip

Javadoc

6.0.0 Javadoc

JDiff

Differences between 5.1.0 and 6.0.0

Acquiring GS Collections

Maven

<dependency>
  <groupId>com.goldmansachs</groupId>
  <artifactId>gs-collections-api</artifactId>
  <version>6.0.0</version>
</dependency>

<dependency>
  <groupId>com.goldmansachs</groupId>
  <artifactId>gs-collections</artifactId>
  <version>6.0.0</version>
</dependency>

<dependency>
  <groupId>com.goldmansachs</groupId>
  <artifactId>gs-collections-testutils</artifactId>
  <version>6.0.0</version>
  <scope>test</scope>
</dependency>

<dependency>
  <groupId>com.goldmansachs</groupId>
  <artifactId>gs-collections-forkjoin</artifactId>
  <version>6.0.0</version>
</dependency>

Ivy

<dependency org="com.goldmansachs" name="gs-collections-api" rev="6.0.0" />
<dependency org="com.goldmansachs" name="gs-collections" rev="6.0.0" />
<dependency org="com.goldmansachs" name="gs-collections-testutils" rev="6.0.0" />
<dependency org="com.goldmansachs" name="gs-collections-forkjoin" rev="6.0.0"/>

New Functionality

RichIterable API

RichIterable.each(Procedure)

Java 8 introduced Iterable.forEach(Consumer) which can cause problems for users of RichIterable.forEach(Procedure). Consumer and Procedure have the same shape, so passing in a lambda is ambiguous.

FastList.newListWith(1, 2, 3).forEach(System.out::println);

This code fails with the following compiler error.

Error: reference to forEach is ambiguous
        both method forEach(java.util.function.Consumer<? super T>) in java.lang.Iterable
        and method forEach(com.gs.collections.api.block.procedure.Procedure<? super T>) in com.gs.collections.impl.list.mutable.FastList match

You can work around this problem by using a cast, Procedures.cast(), or by using RichIterable.each(Procedure) which behaves exactly like InternalIterable.forEach(Procedure).

FastList.newListWith(1, 2, 3).forEach((Procedure<? super Integer>) System.out::println);
FastList.newListWith(1, 2, 3).forEach(Procedures.cast(System.out::println));
FastList.newListWith(1, 2, 3).each(System.out::println);

RichIterable.tap(Procedure): RichIterable

Executes the Procedure for each element in the iterable and returns the RichIterable. Similar to RichIterable.forEach(Procedure) and RichIterable.each(Procedure) but returns this.

LazyIterable.tap(Procedure): LazyIterable

LazyIterable.tap(Procedure) overrides RichIterable.tap(Procedure) and executes lazily. It is useful to "tap into" a method chain, executing a Procedure on every element of the LazyIterable without ending the chain or forcing evaluation.

RichIterable<String> list = Lists.mutable.of("One", "Two", "Three", "Four");

list.asLazy()
    .tap(each -> System.out.println(each + " --(Each element prints this)"))
    .select(StringPredicates.contains("o"))
    .tap(selected -> System.out.println(selected + " --(Only selected element prints this)"))
    .collect(String::toUpperCase)
    .tap(collected -> System.out.println(collected + " --(Collected element prints this)"))
    .each(a -> {}); // force evaluation

Prints

One --(Each element prints this)
Two --(Each element prints this)
Two --(Only selected element prints this)
TWO --(Collected element prints this)
Three --(Each element prints this)
Four --(Each element prints this)
Four --(Only selected element prints this)
FOUR --(Collected element prints this)

RichIterable.toSortedBag(), RichIterable.toSortedBag(Comparator), and RichIterable toSortedBagBy(Function)

RichIterable.toSortedBag() converts the collection to a MutableSortedBag implementation and sorts it using the natural order of the elements. RichIterable.toSortedBag(Comparator) sorts using the Comparator parameter. RichIterable.toSortedBagBy(Function) sorts based on the natural order of the attribute returned by the Function parameter.

RichIterable.groupByUniqueKey(Function): MapIterable.

Similar to RichIterable.groupBy(Function). The keys returned by the Function must be unique, otherwise an exception is thrown. Since the keys are unique, groupByUniqueKey() returns a MapIterable instead of a Multimap.

RichIterable.sumBy(Int|Long|Float|Double)

  • RichIterable.sumByInt(Function<T, V> groupBy, IntFunction<? super T> function): ObjectLongMap<V>
  • RichIterable.sumByLong(Function<T, V> groupBy, LongFunction<? super T> function): ObjectLongMap<V>
  • RichIterable.sumByFloat(Function<T, V> groupBy, FloatFunction<? super T> function): ObjectDoubleMap<V>
  • RichIterable.sumByDouble(Function<T, V> groupBy, DoubleFunction<? super T> function): ObjectDoubleMap<V>

Groups the elements in the RichIterable by the groupBy Function. Each group is converted to numbers using the primitive function and then summed. sumByInt() and sumByLong() return ObjectLongMap. sumByFloat() and sumByDouble() return ObjectDoubleMap.

OrderedIterable API

OrderedIterable interface for order dependent functionality.

An OrderedIterable is a RichIterable with some meaningful order, such as insertion order, access order, or sorted order. ReversibleIterable and SortedIterable now extend OrderedIterable.

Several methods were pulled up to OrderedIterable.

  • indexOf(Object)
  • takeWhile(Predicate), dropWhile(Predicate), and partitionWhile(Predicate)
  • distinct()
  • toStack()

Other methods on InternalIterable and RichIterable are now deprecated because they imply a meaningful order which not all containers have. These methods are overridden on OrderedIterable so that the deprecation warning will not appear on ordered collections.

  • getFirst() and getLast()
  • forEach(startIndex, endIndex, procedure)
  • forEachWithIndex(ObjectIntProcedure)
  • forEachWithIndex(fromIndex, toIndex, objectIntProcedure)

OrderedIterable.corresponds(OrderedIterable, Predicate2).

Returns true if both OrderedIterables have the same length and the predicate returns true for all elements e1 of the current OrderedIterable and e2 of the other OrderedIterable.

The predicate is evaluated for pairs of elements at the same position in both OrderedIterables. The corresponds() method short circuits as soon as it finds a pair of elements which do not correspond.

MutableList<Integer> integers1 = FastList.newListWith(1, 2, 2, 3, 3, 3, 4, 4, 4, 4);
MutableList<Integer> integers2 = FastList.newListWith(2, 3, 3, 4, 4, 4, 5, 5, 5, 5);
Assert.assertTrue(integers1.corresponds(integers3, Predicates2.lessThan()));

OrderedIterable.detectIndex(Predicate).

Returns the index of the first element which satisfies the Predicate or -1 if no elements do. The detectIndex() method short circuits as soon as it finds an element which satisfies the Predicate.

ListIterable<Integer> list = FastList.newListWith(1, 1, 2, 2, 3, 3);
Assert.assertEquals(2, list.detectIndex(integer -> integer == 2));
Assert.assertEquals(-1, list.detectIndex(integer -> integer == 4));

ReversibleIterable API

ReversibleIterable.detectLastIndex(Predicate).

Returns the index of the last element which satisfies the Predicate or -1 if no elements do. The detectLastIndex() method iterates in reverse order and short circuits as soon as it finds an element which satisfies the Predicate.

ListIterable<Integer> list = FastList.newListWith(1, 1, 2, 2, 3, 3);
Assert.assertEquals(3, list.detectLastIndex(integer -> integer == 2));
Assert.assertEquals(-1, list.detectLastIndex(integer -> integer == 4));

ReversibleIterable.distinct().

Same as ReversibleIterable.distinct() for primitive collections.

ReversibleIterable.take(int n) and ReversibleIterable.drop(int n).

take()

Returns the first n elements of the iterable or all the elements in the iterable if n is greater than the length of the iterable.

MutableList<Integer> list = FastList.newListWith(1, 2, 3, 4, 5);
Assert.assertEquals(FastList.newList(), list.take(0));
Assert.assertEquals(FastList.newListWith(1, 2, 3), list.take(3));
Assert.assertEquals(FastList.newListWith(1, 2, 3, 4, 5), list.take(6));

drop()

Returns an iterable after skipping the first n elements or an empty iterable if n is greater than the length of the iterable.

MutableList<Integer> list = FastList.newListWith(1, 2, 3, 4, 5);
Assert.assertEquals(FastList.newListWith(1, 2, 3, 4, 5), list.drop(0));
Assert.assertEquals(FastList.newListWith(4, 5), list.drop(3));
Assert.assertEquals(FastList.newListWith(), list.drop(6));

ParallelIterable API

ListIterable.asParallel(), SetIterable.asParallel(), and SortedSetIterable.asParallel().

In 5.0, asParallel() was added to FastList and UnifiedSet. Now it's on the interfaces ListIterable, SetIterable and SortedSetIterable as well.

ListIterable<Person> people = ...;
ExecutorService threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
int batchSize = 10_000;
ParallelListIterable<Person> peopleParallel = people.asParallel(threadPool, batchSize);

min(), max(), minBy(), and maxBy() on ParallelIterable.

Similar to the same methods on RichIterable. These methods force evaluation.

ParallelListIterable<Person> peopleParallel = people.asParallel(threadPool, batchSize);
Integer youngestAge = peopleParallel.collect(Person::getAge).min();
Integer oldestAge = peopleParallel.collect(Person::getAge).max();
Person youngestPerson = peopleParallel.minBy(Person::getAge);
Person oldestPerson = peopleParallel.maxBy(Person::getAge);

sumOfInt(), sumOfFloat(), sumOfLong(), sumOfDouble() on ParallelIterable.

Similar to the same methods on RichIterable. These methods force evaluation.

ParallelListIterable<Person> peopleParallel = people.asParallel(threadPool, batchSize);
long sum1 = peopleParallel.sumOfInt(Person::getIntAttribute);
long sum2 = peopleParallel.sumOfLong(Person::getLongAttribute);
double sum3 = peopleParallel.sumOfFloat(Person::getFloatAttribute);
double sum4 = peopleParallel.sumOfDouble(Person::getDoubleAttribute);

Multimap API

Constructors for Multimaps that take Iterable<Pair<K, V>>.

ListIterable<Pair<Integer, String>> pairs = ...;
Multimap<Integer, String> actual = FastListMultimap.newMultimap(pairs);

MutableMultimap.add(Pair).

Similar to MutableMultimap.put(K, V) but takes Pair<K, V> instead.

Multimap.forEachKeyMultiValues(Procedure2<K, ? super Iterable>)

Similar to forEachKeyValue(Procedure2<K, V>) but the Procedure2 gets invoked on each group of values instead of each individual value.

Multimap.selectKeysValues(Predicate2<? super K, ? super V>) and Multimap.rejectKeysValues(Predicate2<? super K, ? super V>).

Similar to RichIterable.select() and RichIterable.reject() but the Predicate2 is evaluated against each key/value pair. The implementation of the Predicate2 is free to ignore either the key or the value.

Multimap.selectKeysMultiValues(Predicate2<? super K, ? super Iterable>) and Multimap.rejectKeysMultiValues(Predicate2<? super K, ? super Iterable>).

Similar to Multimap.selectKeysValues() and Multimap.rejectKeysValues() but the Predicate2 takes (K, Iterable) pairs instead of (K, V) pairs.

Multimap.collectKeysValues(Function2<? super K, ? super V, Pair<K2, V2>>).

Similar to RichIterable.collect() but the Function2 is applied to each key/value pair.

Multimap.collectValues(Function<? super V, ? extends V2>)

Similar to Multimap.collectKeysValues() but only transforms keys. It is more efficient than using Multimap.collectKeysValues() and passing through the keys unchanged.

Multimap<K, V>.flip(): Multimap<V, K>.

Returns a new Multimap where the positions of the keys and values are swapped.

Other new API

Unify the map interface hierarchy through new interfaces MutableMapIterable and ImmutableMapIterable.

MutableMap, MutableSortedMap, and MutableBiMap now extend a common interface MutableMapIterable. ImmutableMap, ImmutableSortedMap, and ImmutableBiMap now extend a common interface ImmutableMapIterable. The SetIterable and Bag hierarchy had similar changes.

MutableIterator.remove()

Mutable<Primitive>Iterator is a new subclass of <Primitive>Iterator that adds the remove() method. It behaves similarly to Iterator.remove(), but does not appear on immutable primitive containers and thus doesn't throw UnsupportedOperationException. Immutable containers continue to return the read-only <Primitive>Iterator.

Bag.topOccurrences() and Bag.bottomOccurrences().

Bag.topOccurrences() returns the most frequently occurring item. Bag.bottomOccurrences() returns the least frequently occurring item. In the event of a tie, all tied items are returned.

ImmutableList.subList(fromIndex, toIndex): ImmutableList.

Similar to List.subList() but returns an ImmutableList.

Primitive forms of MutableList.sortThisBy().

Similar to sortThisBy(Function) but taking primitive functions. For example,

MutableList<T> sortThisByInt(IntFunction<? super T> function);
MutableList<T> sortThisByFloat(FloatFunction<? super T> function);
...

Pair.swap().

Returns a new Pair with the two elements transposed.

Functions.swappedPair().

Returns a Function which swaps the two elements in a Pair. Similar to Pair::swap but doesn't rely on Java 8.

StringIterate.chunk(int).

Breaks up a String into fixed size chunks. Similar to RichIterable.chunk(), but for Strings rather than collections.

New Containers

ImmutableBiMap

ImmutableBiMaps can be created by using the BiMaps factory or by calling MutableBiMap.toImmutable().

MutableBiMap<Integer, Character> biMap = ...;
ImmutableBiMap<Integer, Character> characters = biMap.toImmutable();

MultiReaderFastListMultimap, MultiReaderUnifiedSetMultimap, and MultiReaderHashBagMultimap.

Thread-safe Multimaps backed by ConcurrentMutableMaps of multi-reader collections.

Tests

JUnit Runner for interfaces containing concrete tests. New test suite leveraging virtual extension methods.

A new JUnit Runner named Java8Runner that extends the standard test runner by looking for tests in interfaces in addition to classes. Tests in interfaces are default methods annotated with @Test. For example:

@Test
default void MutableList_sortThis()
{
    MutableList<Integer> mutableList = this.newWith(5, 1, 4, 2, 3);
    MutableList<Integer> sortedList = mutableList.sortThis();
    assertSame(mutableList, sortedList);
    assertEquals(Lists.immutable.with(1, 2, 3, 4, 5), sortedList);
}

This allows for a form of multiple inheritance in tests. For example, a MutableList is both a MutableCollection and a ListIterable. Any assertions about MutableCollections and ListIterables should be true for MutableLists as well. Thus, the test interface MutableListTestCase extends both MutableCollectionTestCase and ListIterableTestCase.

unit-tests-java8 is a new test suite containing over 50 new test classes and 60 new test interfaces.

Additional JMH Tests

Performance tests covering min, max, minBy, maxBy, sumOf, and sumBy.

Optimizations

Optimize primitive hash maps with keys and values of the same primitive type.

Primitive hash maps with keys and values of the same primitive type (IntIntHashMap, DoubleDoubleHashMap, etc.), keys and values are now stored in a single array of double the length.

This yields a small memory savings and a speed increase in larger maps. The corresponding JMH performance tests are IntIntMapTest and LongIntMapTest.

Optimize code paths that unnecessarily use iterator by delegating to IterableIterate by delegating to forEach() instead.

Bug Fixes

  • Make ArrayListAdapterSerializationTest more robust for future versions of Java. Fixes #12.
  • Fix array index bug in ArrayList.sortThis(). Fixes #16.
  • Fix ObjectHashMap.injectInto() to include sentinels values.
  • Fix ImmutableSortedMap.entrySet() to return entries sorted by key.
  • Fix CountSetTest to delegate to CountSetScalaTest. Fixes #15.
  • Fix concurrency issue in the aggregateBy tests.
  • Fix the iteration order of several iteration patterns.
  • Use the Kahan summation algorithm to handle double and float precision in sumOf().
  • Fix looping logic in SortedSetIterable.forEach() and SortedSetIterable.forEachWithIndex() with fromIndex and toIndex.
  • Fix CompositeFastList.size() to execute in constant time.
  • Fix ListAdapter.reverseThis() to run in linear time.
  • Fix ListIterate.toArray(list, target, startIndex, sourceSize) and ListIterate.getLast() to run in linear time.
  • Fix UnifiedSet.getLast() and UnifiedSetWithHashingStrategy.getLast() to return the last instead of the first element.