Skip to content

Releases: goldmansachs/gs-collections

4.0.0 (September 2013)

11 Sep 17:58
Compare
Choose a tag to compare

Binaries

gs-collections-4.0.0.zip

Javadoc

4.0.0 Javadoc

JDiff

API differences between 3.0.0 and 4.0.0

New Functionality

Version 4.0 completes the immutable primitive collections work started in 3.2.0. All the primitive collections now have immutable counterparts.

Immutable Primitive Collections

4.0 has immutable primitive sets, stacks, bags and maps. They can each be created using either the toImmutable() method or the primitive collection factories.

MutableIntSet set = IntHashSet.newSetWith(1, 2, 3);
ImmutableIntSet immutableSet = set.toImmutable();

ImmutableIntSet immutableSet2 = IntSets.immutable.of(1, 2, 3);
MutableIntStack stack = IntArrayStack.newStackWith(1, 2, 3);
ImmutableIntStack immutableStack = stack.toImmutable();

ImmutableIntStack immutableStack2 = IntStacks.immutable.of(1, 2, 3);
MutableIntBag bag = IntHashBag.newBagWith(1, 2, 3);
ImmutableIntBag immutableBag = bag.toImmutable();

ImmutableIntBag immutableBag2 = IntBags.immutable.of(1, 2, 3);
MutableIntCharMap map = IntCharHashMap.newWithKeysValues(1, 'a', 2, 'b', 3, 'c');
ImmutableIntCharMap immutableMap = map.toImmutable();

ImmutableIntCharMap immutableMap2 = IntCharMaps.immutable.ofAll(map);

collect() Methods on RichIterable

The collect() methods on RichIterable are similar to collect(), but they take a primitive function and return a primitive collection. There are variants for all eight primitives.

  • collectBoolean()
  • collectByte()
  • collectChar()
  • collectShort()
  • collectInt()
  • collectFloat()
  • collectLong()
  • collectDouble()

More API on Primitive Maps

Primitive maps now include additional API from the non-primitive variants.

putAll()

Copies all of the mappings from the map parameter to this map.

keySet()

Returns a set view of the keys contained in the map. Changes to the map are reflected in the set, and vice versa. The set supports removal, which removes the corresponding mapping from the map. It does not support add() or addAll().

values()

Returns a collection view of the values contained in the map. Changes to the map are reflected in the collection, and vice versa. The collection supports removal, which removes the corresponding mapping from the map. It does not support add() or addAll().

3.2.0 (June 2013)

11 Sep 18:20
Compare
Choose a tag to compare

Binaries

gs-collections-3.2.0.zip

Javadoc

3.2.0 Javadoc

New Functionality

Immutable Primitive Containers

Version 3.2 adds initial support for immutable primitive containers, starting with lists. They can be created using the toImmutable() method or using the primitive list factories.

MutableIntList mutableIntList = IntArrayList.newListWith(1, 2, 3);
ImmutableIntList immutableIntList = mutableIntList.toImmutable();
ImmutableIntList immutableIntList = IntLists.immutable.of(1, 2, 3);

injectInto() on primitive collections

Primitive containers now support injectInto(). It works like RichIterable.injectInto(), but it takes an Object<Primitive>ToObjectFunction, where the primitive type matches the container type. The accumulator can be any type.

IntArrayList 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);

injectIntoWithIndex() on primitive lists

injectIntoWithIndex() is similar to injectInto(), except the function takes a third parameter: the index of the current element. It's only available on lists since sets and bags don't have indexed positions.

IntArrayList list1 = IntArrayList.newListWith(1, 2, 3);
final IntArrayList list2 = IntArrayList.newListWith(1, 2, 3);
MutableInteger dotProduct = list1.injectIntoWithIndex(new MutableInteger(0), new ObjectIntIntToObjectFunction<MutableInteger, MutableInteger>()
    {
        public MutableInteger valueOf(MutableInteger object, int value, int index)
        {
            return object.add(value * list2.get(index));
        }
    });
Assert.assertEquals(new MutableInteger(14), dotProduct);

dotProduct() on primitive lists

The primitive lists that hold numeric types now support dotProduct(). It takes a second list, multiplies the pairs of elements appearing at the same index, and sums the products.

@Test
public void dotProduct()
{
   IntArrayList list1 = IntArrayList.newListWith(1, 2);
   IntArrayList list2 = IntArrayList.newListWith(3, 4);
   Assert.assertEquals(1 * 3 + 2 * 4, list1.dotProduct(list2));
}

forEachWithIndex() on primitive lists

Evaluates the <Primitive>IntProcedure for each element in the list passing in the element and its index.

final StringBuilder string = new StringBuilder();
CharArrayList.newListWith('a', 'b', 'c').forEachWithIndex(new CharIntProcedure()
    {
        public void value(char each, int index)
        {
            string.append(each).append('-').append(index);
        }
    });
Assert.assertEquals("a-1b-2c-3", string.toString());

Unmodifiable wrappers for primitive maps

Version 3.2 adds unmodifiable wrappers for primitive-to-Object maps and Object-to-primitive maps. With this addition, all primitive collections support asUnmodifiable().

@Test(expected = UnsupportedOperationException.class)
public void put_throws()
{
   MutableIntObjectMap<String> map = IntObjectHashMap.newWithKeysValues(0, "zero", 31, "thirtyOne", 32, "thirtyTwo");
   MutableIntObjectMap<String> unmodifiableMap = map.asUnmodifiable();
   unmodifiableMap.put(0, "one");
}

No API changes in this release

This release preserves binary, source, and serialization compatibility with 3.0.

  • The method toImmutable() already existed on primitive collections in GS Collections 3.0, but it threw UnsupportedOperationException.
  • injectInto(), injectIntoWithIndex() and forEachWithIndex() only have been added to the implementations. They will be added to the primitive iterable interfaces in the next major release.

3.1.0 (May 2013)

11 Sep 18:21
Compare
Choose a tag to compare

Binaries

gs-collections-3.1.0.zip

Javadoc

3.1.0 Javadoc

New Functionality

Synchronized and Unmodifiable Wrappers for Primitive Collections

The wrappers for primitive collections are similar to the wrappers for generic collections.

asUnmodifiable() returns a wrapper which throws on mutating methods. All other methods pass through to the wrapped collection.

For example:

@Test(expected = UnsupportedOperationException.class)
public void add_throws()
{
    IntArrayList list = IntArrayList.newListWith(1, 2, 3);
    MutableIntList unmodifiableList = list.asUnmodifiable();
    unmodifiableList.add(4);
}

asSynchronized() returns a wrapper which synchronizes on a lock before delegating to the wrapped collection. The iterator is still not synchronized, so the warning on [java.util.Collections.synchronizedCollection()](http://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#synchronizedCollection(java.util.Collection\)) applies to this API as well.

By default, the lock is the wrapper itself. Use the wrapper's constructor instead of the asSynchronized() method if you need to customize the lock.

Fork Join Functionality

This release includes a new module, gs-collections-forkjoin-3.1.0.jar. Its main utility is the FJIterate class. It's conceptually similar to ParallelIterate, but it's based on Java 7's ForkJoinPools. All other modules continue to be backward compatible with Java 5.

Improved memory benchmarks and performance tests

This release includes additional performance test for UnifiedSet and ImmutableArrayList. Existing performance tests are now more fair. The memory tests record more data points.

No API changes in this release

The methods asUnmodifiable() and asSynchronized() already existed on primitive collections in GS Collections 3.0, but they threw UnsupportedOperationException. There are no API changes in this release. It preserves binary, source, and serialization compatibility with 3.0.

3.0.1 (March 2013)

11 Sep 18:28
Compare
Choose a tag to compare

Binaries

gs-collections-3.0.1.zip

Javadoc

3.0.0 Javadoc

JDiff

API differences between 2.0.0 and 3.0.0

New Functionality

Primitive Collections

GS Collections 3.0 has memory optimized collections for primitive types. The collections include lists, sets, stacks, maps and bags. The interface hierarchy is very similar to the hierarchy of object collections.

Some examples:

InterfaceAnalogous to
IntIterableRichIterable
MutableIntCollection MutableCollection
IntList ListIterable
MutableIntListMutableList

The hierarchy is similar for the other primitive types.

Primitive Lists

Primitive lists are backed by an array (like FastList), but it's a primitive array instead of Object[]. They are named IntArrayList, FloatArrayList, etc.

BooleanArrayList is a special case. Current JVMs use one byte per boolean in a boolean[], instead of one bit per boolean. BooleanArrayLists are backed by a java.util.BitSet as an optimization.

To create an IntArrayList, use one of the following:

IntArrayList emptyList = new IntArrayList();
IntArrayList intList = IntArrayList.newListWith(1, 2, 3);
IntArrayList listFromIntIterable = IntArrayList.newListWith(IntHashSet.newSetWith(1, 2, 3));
IntInterval

IntInterval is Similar to Interval, but implements the IntList interface instead of List. It represents a range of ints and a step value.

Assert.assertEquals(IntArrayList.newListWith(1, 2, 3), IntInterval.oneTo(3));
Assert.assertEquals(IntArrayList.newListWith(1, 3, 5), IntInterval.oneToBy(5, 2));

Primitive Sets

The primitive set implementations are backed by hashtables. The hashtables are implemented using open addressing and quadratic probing. They are named IntHashSet, FloatHashSet, etc. BooleanHashSet is implemented using a single integer to keep track of the four states [], [F], [T], or [T, F].

Primitive Stacks

Primitive stacks are similar to ArrayStack but backed by primitive lists instead of FastList.

Primitive Bags

Primitive bags are similar to HashBag, but both item and count are primitives.

Primitive Maps

There are three types of primitive maps:

  1. Object to Primitive (ObjectIntHashMap, ObjectFloatHashMap, etc.)
  2. Primitive to Object (IntObjectHashMap, FloatObjectHashMap, etc.)
  3. Primitive to Primitive (IntIntHashMap, IntLongHashMap, etc.)

There are no maps with boolean keys. All the maps are implemented as hashtables using open addressing and quadratic probing.

get()

Since there is no concept of null when working with primitives, maps with primitive values return an EMPTY_VALUE sentinel from get() if the key is not present in the map. EMPTY_VALUE is false for boolean and 0 for all other primitives.

IntIntHashMap map = IntIntHashMap.newWithKeysValues(1, 1, 2, 2, 3, 3);
Assert.assertEquals(1, map.get(1));
Assert.assertEquals(2, map.get(2));
Assert.assertEquals(3, map.get(3));
Assert.assertEquals(0, this.map.get(4));

Assert.assertFalse(new IntBooleanHashMap().get(1));
getOrThrow()

If the key is present in the map, getOrThrow() returns the corresponding value, otherwise it throws IllegalStateException.

final IntIntHashMap map = IntIntHashMap.newWithKeysValues(1, 1, 2, 2, 3, 3);
Assert.assertEquals(1, map.getOrThrow(1));
Assert.assertEquals(2, map.getOrThrow(2));
Assert.assertEquals(3, map.getOrThrow(3));

Verify.assertThrows(IllegalStateException.class, new Runnable()
{
    public void run()
    {
        map.getOrThrow(4);
    }
});

Primitive Code Blocks and Code Block Factories

IntPredicates, LongPredicates, etc. can be used to create common instances of IntPredicate, LongPredicate, etc.

More API

RichIterable.aggregateBy()

Groups the elements in the RichIterable by the function named groupBy. Then all the values that map to the same key are aggregated together using the Function2 named aggregator. aggregateBy is conceptually similar to a groupBy into a Multimap followed by injectInto on each collection of values, finally yielding a MapIterable. The third parameter, a Function0 named factory, creates the initial value for each aggregation (the accumulator used by injectInto).

Aggregate values are allowed to be immutable since they are replaced in the map.

Function0<Integer> factory = new Function0<Integer>()
{
    public Integer value()
    {
        return Integer.valueOf(0);
    }
};
Function2<Integer, Integer, Integer> sumAggregator = new Function2<Integer, Integer, Integer>()
{
    public Integer value(Integer aggregate, Integer value)
    {
        return aggregate + value;
    }
};

Function<Integer, Integer> groupBy = new Function<Integer, Integer>()
{
    public Integer valueOf(Integer integer)
    {
        return integer % 2;
    }
};
FastList<Integer> integers = FastList.newListWith(1, 1, 1, 2, 2, 3);
MutableMap<Integer, Integer> aggregation = integers.aggregateBy(groupBy, factory, sumAggregator);
Assert.assertEquals(4, aggregation.get(0).intValue());
Assert.assertEquals(6, aggregation.get(1).intValue());

RichIterable.aggregateInPlaceBy()

Conceptually similar to aggregateBy, but aggregateInPlaceBy mutates values in the result map instead of replacing them. The aggregator parameter is a Procedure2 instead of a Function2.

Function0<AtomicInteger> factory = new Function0<AtomicInteger>()
{
    public AtomicInteger value()
    {
        return new AtomicInteger(0);
    }
};
Procedure2<AtomicInteger, Integer> sumAggregator = new Procedure2<AtomicInteger, Integer>()
{
    public void value(AtomicInteger aggregate, Integer value)
    {
        aggregate.addAndGet(value);
    }
};

Function<Integer, Integer> groupBy = new Function<Integer, Integer>()
{
    public Integer valueOf(Integer integer)
    {
        return integer % 2;
    }
};
FastList<Integer> integers = FastList.newListWith(1, 1, 1, 2, 2, 3);
MutableMap<Integer, AtomicInteger> aggregation = integers.aggregateInPlaceBy(groupBy, factory, sumAggregator);
Assert.assertEquals(4, aggregation.get(0).intValue());
Assert.assertEquals(6, aggregation.get(1).intValue());

RichIterable.noneSatisfy()

Returns true if the predicate evaluates to false for every element of the iterable or if the iterable is empty, otherwise it returns false. It's conceptually similar to RichIterable.allSatisfy(Predicates.not(predicate)).

Assert.assertFalse(IntArrayList.newListWith(1, 2, 3).noneSatisfy(IntPredicates.greaterThan(0)));
Assert.assertTrue(IntArrayList.newListWith(1, 2, 3).noneSatisfy(IntPredicates.greaterThan(3)));

ListIterable.distinct()

Returns a new ListIterable containing the distinct elements in the list. It's conceptually similar to toSet().toList() but it retains the original order. If an element appears multiple times in the list, only the first one will be copied into the result.

Verify.assertListsEqual(Lists.mutable.of(5, 2, 3, 4), Lists.mutable.of(5, 2, 3, 5, 4, 2).distinct());

FastList.newWithNValues()

Returns a new FastList containing N values generated by calling the Function0 N times.

Assert.assertEquals(FastList.newListWith(1, 1, 1, 1, 1), FastList.newWithNValues(5, Functions0.value(1)))

Bag.selectByOccurrences()

Returns all elements of the bag that have a number of occurrences that satisfy the predicate.

MutableBag<Integer> integers = Bags.mutable.of(1, 1, 1, 1, 2, 2, 2, 3, 3, 4);
Assert.assertEquals(Bags.mutable.of(1, 1, 1, 1, 3, 3), integers.selectByOccurrences(IntPredicates.isEven()));

Bag.toStringOfItemToCount()

Returns a string representation of the bag. It's conceptually the same as Bag.toMapOfItemToCount().toString() but without creating the intermediate map.

Assert.assertEquals("{}", Bags.mutable.of().toStringOfItemToCount());
Assert.assertEquals("{1=3}", Bags.mutable.of(1, 1, 1).toStringOfItemToCount());

MutableMap.updateValue() and MutableMap.updateValueWith()

Looks up the value associated with the given key, applies the Function to it, and replaces the value. If there is no value associated with the key, starts it off with a value supplied by the factory Function0.
updateValueWith() is the same as updateValue() with a Function2 and an extra parameter which is passed as a second argument to the function.

MutableMap<String, Integer> integers = UnifiedMap.newWithKeysValues("two", 2, "three", 3);
integers.updateValue("two", Functions0.value(1), Functions.squaredInteger());
Assert.assertEquals(UnifiedMap.newWithKeysValues("two", 4, "three", 3), integers);
integers.updateValue("four", Functions0.value(4), Functions.squaredInteger());
Assert.assertEquals(UnifiedMap.newWithKeysValues("two", 4, "three", 3, "four", 16), integers);

2.0.0 (August 2012)

11 Sep 18:29
Compare
Choose a tag to compare

Binaries

gs-collections-2.0.0.zip

Javadoc

2.0.0 Javadoc

JDiff

API differences between 1.2.0 and 2.0.0

New Functionality

Stack Container

A stack is a collection enforcing a last-in, first-out order; its methods iterate over elements beginning with the most-recently added element.

The StackIterable interface extends RichIterable and its methods such as forEach and toString process elements in reverse order of their addition to the stack. MutableStack extends StackIterable and adds mutating methods like push, pop, and clear.

The concrete type is ArrayStack. The current implementation delegates to a FastList and thus has similar runtime complexity. For example, ArrayStack's push method takes amortized constant time, like FastList's add method. In GS Collections, stacks are not lists. This is a deliberate difference from java.util.Stack which extends Vector.

push Adds a new element to the top of the stack.
pop Returns the top (most recently-added) element and removes it from the collection
pop(int count) Returns a ListIterable of the number of elements specified by the count, beginning with the top of the stack.
peek Returns but does not remove the top element. Note that, on a stack, getFirst likewise returns the top element, and that getLast throws an exception.
peek(int count) Returns a ListIterable of the number of elements specified by the count, beginning with the top of the stack; does not remove the elements from the stack.
peekAt(int index) Returns the element at index.

To create a MutableStack, use one of the following static factory methods:

MutableStack<Integer> emptyStack = ArrayStack.newStack();
MutableStack<Integer> mutableStack = ArrayStack.newStackWith(1, 2, 3);
MutableStack<Integer> stackFromFastList = ArrayStack.newStack(FastList.newListWith(1, 2, 3));
MutableStack<Integer> stackFromTopToBottom = ArrayStack.newStackFromTopToBottom(3, 2, 1);
MutableStack<Integer> stackUsingStacksFactory = Stacks.mutable.of(1, 2, 3);

ConcurrentHashMap

GS Collections 2.0 includes a new concurrent hashmap implementation, com.gs.collections.impl.map.mutable.ConcurrentHashMap.

Primitive Iterables and their Lazy Implementations

The primitive iterable interfaces are memory-optimized for primitives.

int IntIterable
long LongIterable
float FloatIterable
double DoubleIterable

They are inspired by the RichIterable interface, and contain a subset of the iteration pattern methods from RichIterable like collect. They add some primitive specific API like sum, average, etc. They also include Iterators specialized for each primitive type. They do not extend Iterator, to prevent accidental auto-boxing.

The current implementations use lazy evaluation. Here's an example which calculates the average of a collection of ints.

double average = Interval.oneTo(4).collectInt(PrimitiveFunctions.unboxIntegerToInt()).average();

Here, conllectInt() returns an instance of CollectIntIterable, an implementation of IntIterable. CollectIntIterable transforms a source iterable using an IntFunction as it iterates.

The PrimitiveFunctions class has a number of common unboxing functions. For example PrimitiveFunctions.unboxIntegerToInt() returns an IntFunction.

More RichIterable API

selectInstancesOf()

Returns all elements of the source collection that are instances of the Class parameter.

MutableList<Number> numbers = FastList.newListWith(1.0, 2, 3.0, 4, 5.0);
MutableList<Integer> integers = numbers.selectInstancesOf(Integer.class);

It is meant to address this problem with select and an “instanceOf” Predicate.

MutableList<Number> numbers = FastList.newListWith(1.0, 2, 3.0, 4, 5.0);
MutableList<Number> integers = numbers.select(Predicates.instanceOf(Integer.class));

The result is a collection of Number instead of Integer. If we try to add a simple cast, we get a compiler error.

MutableList<Integer> integers = (MutableList<Integer>) numbers.select(Predicates.instanceOf(Integer.class));

The error message tells us we’re trying to cast to an inconvertible type. We can use raw types, or do a double cast, but neither is intuitive.

MutableList<Integer> integers = (MutableList<Integer>) (MutableList<?>) numbers.select(Predicates.instanceOf(Integer.class));

The new method selectInstancesOf() addresses these problems with types, plus and it’s concise and communicates what you’re doing.

sumOf() Methods - sumOfInt(), sumOfLong(), sumOfFloat(), sumOfDouble()

The sumOf methods return the final primitive result of evaluating function for each element of the iterable and adding elements together. For example, sumOfInt() takes an IntFunction and returns the int sum without auto-boxing.