Eclipse Collections extends the Java Collections Framework with new interfaces and classes and provides additional methods. These new methods implement iteration patterns derived from the collection protocols of the Smalltalk language (e.g., select, reject, collect, detect, injectInto).
In idiomatic (imperative) Java code, iteration is performed by external for and while loops that enclose a block of business logic to be performed on each element of a collection. This is an approach that often leads to much code duplication.
In Eclipse Collections, business logic is reified as a code block: a class that is passed as a parameter to an iteration method. Each implementation of an iteration method iterates over the collection, passing each element to the code block for processing. In Java 8, these code blocks can be specified using the lambda and method reference syntax.
The most important advantage of internal iteration patterns is that they increase readability by giving names to the structural iteration patterns and by reducing code duplication. Moreover, by encapsulating implementation within specialized collection types (e.g., list, sets, maps), iteration can be optimized for the particular type.
These are the most commonly-used iteration patterns in Eclipse Collections.
Filtering patterns Transforming patterns |
Fused patterns "Short-circuit" patterns |
Generic action patterns |
Filter a collection to create a new collection: includes select, reject, and partition.
Methods using the Select pattern return a new collection comprising those elements from the source collection that satisfy some logical condition. Reject is the inverse pattern, returning a new collection of elements that do not satisfy the condition. The condition is a boolean expression in the form of single-argument code block that implements the Predicate interface.
create <newCollection> for each <element> of <collection> if condition(<element>) add <element> to <newCollection>
MutableList<Integer> greaterThanFifty = list.select(each -> each > 50);
MutableList<Integer> greaterThanFifty =
list.select(new Predicate<Integer>()
{
public boolean accept(Integer each)
{
return each > 50;
}
});
MutableList<Integer> greaterThanFifty = list.select(Predicates.greaterThan(50));
MutableList<Integer> greaterThanFifty = list.stream()
.collect(Collectors2.select(each -> each.intValue() > 50, Lists.mutable::empty));
List<Integer> greaterThanFifty = Lists.mutable.empty();
for (Integer each : list)
{
if (each.intValue() > 50)
{
greaterThanFifty.add(each);
}
}
create <newCollection> for each <element> of <collection> if not condition(<element>) add <element> to <newCollection>
MutableList<Integer> notGreaterThanFifty =
list.reject(each -> each > 50);
MutableList<Integer> notGreaterThanFifty =
list.reject(Predicates.greaterThan(50));
MutableList<Integer> notGreaterThanFifty = list.stream()
.collect(Collectors2.reject(each -> each.intValue() > 50, Lists.mutable::empty));
List<Integer> notGreaterThanFifty = Lists.mutable.empty();
for (Integer each : list)
{
if (each <= 50)
{
notGreaterThanFifty.add(each);
}
}
These Eclipse Collections methods implement the Select and Reject pattern:
select(Predicate): RichIterable
reject(Predicate): RichIterable
The Predicate is evaluated for each element of the collection. The selected elements are those where the Predicate returned true (false for rejected). The selected (or rejected) elements are returned in a new collection of the same type.
select(Predicate, targetCollection): targetCollection
reject(Predicate, targetCollection): targetCollection
Same as the select/reject methods with one argument, but results are added to the specified targetCollection.
selectWith(Predicate2, argument): RichIterable
rejectWith(Predicate2, argument): RichIterable
For each element of the collection, Predicate2 is evaluated with the element as one argument, plus one additional argument; selected or rejected elements are returned in a new collection of the same type. See Reusing code blocks for more information.
selectWith(Predicate2, argument, targetCollection): targetCollection
rejectWith(Predicate2, argument, targetCollection): targetCollection
Same as the selectWith/rejectWith methods, but results are added to the specified targetCollection.
Create two collections using Select and Reject.
The Partition pattern allocates each element of a collection into one of two new collections depending on whether the element satisfies the condition expressed by the Predicate. In effect, it combines the Select and Reject patterns. The collections are returned in a PartitionIterable specialized for the type of the source collection. You can retrieve the selected and rejected elements from the PartitionIterable. In this example, the list of people is partitioned into lists of adults and children.
PartitionMutableList<Person> partitionedFolks =
people.partition(person -> person.getAge() >= 18);
MutableList<Person> adults = partitionedFolks.getSelected();
MutableList<Person> children = partitionedFolks.getRejected();
MutableList<Person> people =...
PartitionMutableList<Person> partitionedFolks = people.partition(
new Predicate<Person>()
{
public boolean accept(Person each)
{
return each.getAge() >= 18;
}
});
MutableList<Person> adults = partitionedFolks.getSelected();
MutableList<Person> children = partitionedFolks.getRejected();
List<Person> people =...
PartitionMutableList<Person>> partitionedFolks = people.stream()
.collect(Collectors2.partition(person -> person.getAge() >= 18, PartitionFastList::new)
MutableList<Person> adults = partitionedFolks.getSelected();
MutableList<Person> children = partitionedFolks.getRejected());
These Eclipse Collections methods implement the partition pattern:
partition(Predicate): PartitionIterable
Returns a PartitionIterable, a logical pair of containers. The first container consists of all elements that satisfy the Predicate. The second container consists of all elements that do not satisfy the Predicate. The subtypes of PartitionIterable correspond with the subtypes of RichIterable.
partitionWith(Predicate2, argument): PartitionIterable
For each element of the collection, Predicate2 is evaluated with the element as one argument, plus one additional argument; partitioned elements are returned in a new collection of type PartitionIterable.
Transform a collection’s elements, creating a new collection: includes collect, flatCollect, and groupBy.
The Collect pattern methods return a new collection whose data elements are the results of an evaluation performed by the code block; that is, each element of the original collection is mapped to a new object, which is usually a different type. The code block used as the collect method’s parameter implements the Function interface.
Eclipse Collections provides two specialized variations on the Collect pattern: the FlatCollect (or Flatten) pattern and the GroupBy pattern. As with Collect, the patterns' methods take a single Function as a parameter.
create <newCollection> for each <element> of <collection> <result> = transform(<element>) add <result> to <newCollection>
MutableList<Address> addresses =
people.collect(person -> person.getAddress());
//or
MutableList<Address> addresses =
people.collect(Person::getAddress);
MutableList<Person> people =...;
Function<Person, Address> addressFunction =
new Function<Person, Address>()
{
public Address valueOf(Person person)
{
return person.getAddress();
}
};
MutableList<Address> addresses = people.collect(addressFunction);
Notice that this assumes each person in the people collection has just one address.
If, instead, a person has multiple addresses, the Function returns a list of addresses for each person (a list that has only one element if the person has just one address). The result is a List of Lists.
MutableList<MutableList<Address>> addresses =
people.collect(person -> person.getAddresses());
//or
MutableList<MutableList<Address>> addresses =
people.collect(Person::getAddresses);
MutableList<Person> people =...;
Function<Person, MutableList<Address>> addressFunction =
new Function<Person, MutableList<Address>>()
{
public MutableList<Address> valueOf(Person person)
{
return person.getAddresses();
}
};
MutableList<MutableList<Address>> addresses =
people.collect(addressFunction);
MutableList<MutableList<Address>> addresses = people.stream()
.map(person -> person.getAddresses())
.collect(Collectors2.toList());
//or
MutableList<MutableList<Address>> addresses = people.stream()
.collect(Collectors2.collect(Person::getAdddresses, Lists.mutable::empty));
List<Address> addresses = Lists.mutable.empty();
for (Person person : people)
{
addresses.add(person.getAddress());
}
//or for multiple addresses
List<List<Address>> addresses = Lists.mutable.empty();
for (Person person : people)
{
addresses.add(person.getAddresses());
}
These Eclipse Collections methods implement the Collect pattern:
collect(Function): RichIterable
For each element of the collection, the Function is evaluated with the current element as the argument; returns a new collection with the transformed type.
collectInt(IntFunction): IntIterable
Similar to collect, but it takes an IntFunction and returns a primitive collection which extends from IntIterable. There are variants for all eight primitives: collectBoolean, collectFloat etc.
collect(Function, targetCollection): targetCollection
Same as collect, except that the results are added to the specified targetCollection, which extends java.util.Collection.
collectInt(IntFunction, targetCollection): targetCollection
Same as collectInt, except that the results are added to the specified targetCollection, which extends MutableIntCollection. There are variants for all eight primitives.
collectIf(Predicate, Function): RichIterable
Same as collect, except that the Predicate is first evaluated with the element as the argument to filter the collection.
collectIf(Predicate, Function, argument2): RichIterable
Same as collect, but the Function2 is evaluated with the element as one argument, plus one additional argument; returns a new collection of the same size and the transformed type.
collectWith(Function2, argument2, targetCollection): targetCollection
Same as collectWith, except that the results are added to a specified targetCollection. (On all RichIterables since version 1.0)
Create a single, linear collection from selected values of a collection’s elements.
The FlatCollect or Flatten pattern is a specialized form of the the Collect pattern. It returns a single-level, or "flattened," collection of attribute values from a source collection’s elements.
flatCollect(Function): RichIterable
Applies the Function to each element. The Function must return an Iterable type. Returns the intermediate Iterables in a single, flattened collection.
Given a list of people, as in the collect method examples, here is how flatCollect could be used to create a flat list from the address fields of the person objects in that list, using the same Function (addressFunction):
create <newCollection> for each <element> of <collection> <results> = transform(<element>) Add all <results> to <newCollection>
MutableList<Address> flatAddress =
people.flatCollect(person -> person.getAddresses());
// or
MutableList<Address> flatAddress =
people.flatCollect(Person::getAddresses);
Note the flatCollect method’s similarity to a collect method having the same signature: each method’s Function parameter maps to an Iterable type. This is optional for collect, but required of flatCollect. Both methods return a new collection. The difference is that collect in this form creates a collection of collections from a simple List, Set or Bag, while flatCollect performs a different (and in this instance, somewhat more useful) action, returning a flat list of addresses.
MutableList<Address> addresses =
people.flatCollect(
new Function<Person, MutableList<Address>>()
{
public MutableList<Address> valueOf(Person person)
{
return person.getAddresses();
}
});
MutableList<Address> flatAddress = people.stream()
.collect(Collectors2.flatCollect(Person::getAddresses, Lists.mutable::empty));
List<Address> addresses = Lists.mutable.empty();
for (Person person : people)
{
addresses.addAll(person.getAddresses());
}
Create a Multimap from a collection by grouping on a selected or generated key value.
The GroupBy pattern gathers the elements on the collection into a map-like container called a Multimap, which associates multiple values for each key. The Function is applied to each element and the result is used as the key into the Multimap where the element should appear as the value.
See the discussion of Multimap for examples of groupby.
groupBy(Function): Multimap
groupBy(Function, targetMultimap) : targetMultimap
Same as groupBy except that results are added to the specified targetMultimap.
groupByEach(Function): Multimap
Same as groupBy except that the Function transforms each value into multiple keys, returning a new Multimap containing all the key/value pairs.
Methods that control processing by testing a collection for a logical condition: includes detect, anySatisfy, and allSatisfy.
The "short-circuit" patterns—Detect, AnySatisfy and AllSatisfy—are so called because they describe methods that cease execution when a specific condition is met. With each iteration, the Predicate is evaluated. If the evaluation resolves as a specified boolean (true/false) value, then iteration halts and returns the appropriate value.
Finds and returns the first element that satisfies a given logical expression.
Detect returns the first element that satisfies a Predicate. If every element in the collection is tested and the Predicate never returns true, then the method returns null.
for each <element> of <collection> if condition(<element>) return <element> // and end process
Integer result =
list.detect(each -> each > 50);
Integer result =
list.detect(Predicates.greaterThan(50));
Integer result =
list.stream().findAny(each -> each > 50).get();
for (int i = 0; i < list.size(); i++)
{
Integer v = list.get(i);
if (v.intValue() > 50)
{
return v;
}
}
return null;
detect(Predicate): element
Returns the first element which satisfies the Predicate or null if no element satisfies the Predicate.
detectIfNone(Predicate, Function0): element (or Function0 result)
Same as detect, but if no element causes Predicate to evaluate as true, return the result of evaluating Function0.
detectWith(Predicate2, parameter): element
Returns the first element that evaluates as true for the specified Predicate2 and parameter, or null if none evaluate as true. See Reusing a code block for more information.
detectWithIfNone(Predicate2, parameter, Function0): element (or Function0 result)
Same as detectWith, but if no element causes Predicate2 to evaluate as true, return the result of evaluating Function0.
Determine if any collection element satisfies a given logical expression.
The AnySatisfy pattern determines whether any element of a collection satisfies a Predicate. It applies the Predicate to each element; if the Predicate returns true, execution halts and the method returns true immediately. Otherwise, every element is checked and the method returns false.
for each <element> of <collection> if condition(<element>) return true // and end process otherwise return false
boolean result =
list.anySatisfy(num -> num > 50);
boolean result =
list.anySatisfy(Predicates.greaterThan(50));
boolean result =
list.stream().anyMatch(num -> num > 50);
for (int i = 0; i < list.size(); i++)
{
Integer v = list.get(i);
if (v.intValue() > 50)
{
return true;
}
}
return false;
anySatisfy(Predicate): boolean
Returns true if the Predicate returns true for any element of the collection. Otherwise (or if the collection is empty), return false.
anySatisfyWith(Predicate2, parameter): boolean
Returns true if the Predicate2 returns true for any element of the collection. Otherwise (or if the collection is empty), return false.
Determine if all collection elements satisfy a given logical expression.
The AllSatisfy pattern is the inverse of AnySatisfy. It reports whether all elements satisfy a Predicate. It applies the Predicate to each element; if the Predicate returns false, execution halts and the method returns false immediately. Otherwise, every element is checked and the method returns true.
for each <element> of <collection> if not condition(<element>) return false // and end process otherwise return true
boolean result =
list.allSatisfy(each -> each > 50);
boolean result =
list.allSatisfy(Predicates.greaterThan(50));
boolean result =
list.stream().allMatch(each -> each > 50);
for (int i = 0; i < list.size(); i++)
{
Integer v = list.get(i);
if (v.intValue() <= 50)
{
return false;
}
}
return true;
NoneSatisfy is similar to AllSatisfy, but negates the Predicate. It returns true only if no element satisfies the Predicate. If the container is empty it also returns true.
allSatisfy(Predicate): boolean
Returns true if the Predicate returns true for all elements of the collection. Otherwise (or if the collection is empty), return false.
allSatisfyWith(Predicate2, parameter): boolean
Return true if the Predicate2 returns true for all elements of the collection. Otherwise (or if the collection is empty), return false.
noneSatisfy(Predicate):* boolean*
Return true if the Predicate returns false for all elements of the collection or if the collection is empty. Otherwise, return false.
noneSatisfyWith(Predicate2, parameter): boolean
Return true if the Predicate2 returns false for all elements of the collection or if the collection is empty. Otherwise, return false.
Perform a calculation on each element of the current collection.
The ForEach pattern defines the most basic iteration operation that can be used with all collection types. Unlike the other patterns discussed in this topic, the ForEach pattern prescribes methods that operate on each element of the calling collection object, with no value returned by the method itself.
In Eclipse Collections, the each and forEach methods offer the most straightforward replacement for the Java for loop. It executes the code in a Procedure on each element. You can use these methods to perform some action using the values of the source collection—for example, to print a value or to call another method on each element.
for each <element> of <collection> evaluate(<element>)
list.each(each -> doSomething(each));
// or
list.forEach(each -> doSomething(each));
list.each(new Procedure()
{
public void value(Object each)
{
doSomething(each);
}
});
for (int i = 0; i < list.size(); i++)
{
this.doSomething(list.get(i));
}
each(Procedure): void
For each element, the Procedure is evaluated with the element as the argument.
forEach(Procedure): void
For each element, the Procedure is evaluated with the element as the argument.
forEachIf(Predicate, Procedure): void
For each element that satisfies the Predicate, executes the Procedure on that element.
forEach(fromIndex, toindex, Procedure): void
Iterates over the section of a ListIterable covered by the specified indexes (inclusive).
forEachWith(Procedure2, parameter): void
For each element of the collection, the Procedure2 is evaluated with the element as the first argument, and the specified parameter as the second argument.
forEachWithIndex(ObjectIntProcedure): void
Iterates over a collection passing each element and the current relative int index to the specified instance of ObjectIntProcedure.
forEachWithIndex(fromIndex, toIndex, ObjectIntProcedure): void
Iterates over the section of the list covered by the specified indexes (inclusive).
Calculate and maintain a running value during iteration; use each evaluated result as an argument in the next iteration.
The InjectInto pattern is used to carry a computed result from one iteration as input to the next. In this pattern, the injectInto method takes an initial injected value as a parameter. This value is used as the first argument to a two-argument code block; the current element (for each iteration of the collection) is taken as the second argument.
For each iteration, the code block’s evaluation result is passed to the next iteration as the first argument (the injected value) of the code block, with the (new) current element as the second argument. The injectInto() method returns the code block’s cumulative result upon the final iteration.
set <result> to <initialvalue> for each <element> of <collection> <result> = apply(<result>, <element>) return <result>
Integer result =
Lists.mutable.of(1, 2).injectInto(3, (result, each) -> result + each);
// or
Integer result =
Lists.mutable.of(1, 2).injectInto(3, Integer::sum);
Integer result =
Lists.mutable.of(1, 2).injectInto(3, AddFunction.INTEGER);
Integer result =
Lists.mutable.of(1, 2).stream().reduce(3, (result, each) -> result + each);
// or
Integer result =
Lists.mutable.of(1, 2).stream().reduce(3, Integer::sum);
List<Integer> list = Lists.mutable.of(1, 2);
int result = 3;
for (int i = 0; i < list.size(); i++)
{
Integer v = list.get(i);
result = result + v.intValue();
}
injectInto(injectedValue, Function2): (final result)
Return the final result of all evaluations using as the arguments each element of the collection, and the result of the previous iteration’s evaluation.
injectInto(floatValue, FloatObjectToFloatFunction): float
Return the final result of all evaluations using as the arguments each element of the collection, and the result of the previous iteration’s evaluation. The injected value and result are both primitive floats.
injectInto(intValue, IntObjectToIntFunction): int
Return the final result of all evaluations using as the arguments each element of the collection, and the result of the previous iteration’s evaluation. The injected value and final result are both primitive ints.
injectInto(longValue, LongObjectToLongFunction): long
Return the final result of all evaluations using as the arguments each element of the collection, and the result of the previous iteration’s evaluation. The injected value and result are both primitive longs.
injectInto(doubleValue, DoubleObjectToDoubleFunction): double
Return the final result of all evaluations using as the arguments each element of the collection, and the result of the previous iteration’s evaluation. The injected value and result are both primitive doubles.
The superinterface that specifies the iteration patterns in Eclipse Collections container types.
RichIterable is the most important interface in Eclipse Collections. It provides the blueprint for all non-mutating iteration patterns. It represents an object made up of elements that can be individually and consecutively viewed or evaluated (an iterable), and it prescribes the actions that can be performed with each evaluation (the iteration patterns). The most commonly used implementations include FastList and UnifiedSet.
RichIterable is extended by ListIterable, SetIterable, Bag, StackIterable, and MapIterable. A MapIterable of keys and values is also a RichIterable of values. (Note that Multimap and its subinterfaces have a separate API.)
RichIterable is also extended by MutableCollection, and indirectly by MutableList and MutableSet (which also extend the mutable Java Collection types List and Set). Another subinterface defines a non-JDK container, MutableBag (or multiset); yet another, ImmutableCollection, delineates the immutable forms of these Eclipse Collections containers. These latter two interfaces are detailed in the Collections and containers topic.
The subinterface LazyIterable for the most part replicates RichIterable, but overrides some specific collection-returning methods— collect, collectIf, select, reject, and flatCollect—so that they delay their actual execution until the returned collection is needed, a technique called "lazy iteration."
Deferring evaluation until necessary.
Lazy iteration is an optimization pattern in which an iteration method is invoked, but its actual execution is deferred until its action or return values are required by another, subsequent method. In practical terms, the objective is typically to forestall unnecessary processing, memory use, and temporary-object creation unless and until they are needed.
Lazy iteration is implemented as an adapter on the current RichIterable collection by the asLazy method:
richIterable.asLazy()
Returns a deferred-evaluation iterable of type LazyIterable. (Note the list below of other Eclipse Collections methods that return lazy Iterables.)
In a way, lazy iteration is a companion to the short-circuit iteration patterns, in which iteration ceases as soon the method’s purpose is achieved. In the last line of the example below, the anySatisfy method quits execution when it detects the "address2" element in the addresses list created by collect. The third element ("address 3") is never examined by anySatisfy - although it was present in addresses.
Person person1 = new Person(address1);
Person person2 = new Person(address2);
Person person3 = new Person(address3);
MutableList<Person> people =
Lists.mutable.with(person1, person2, person3);
MutableList<MutableList<Address>> addresses =
people.collect(addressFunction);
Assert.assertTrue(addresses.anySatisfy(Predicates.equal(address2)));
One excess element out of three may be trivial, but if people were to be a very long list (or a stream), anySatisfy will still have to wait for the collect method to finish aggregating an equally-large temporary collection - one that will have only its first two elements inspected. By applying a lazy-iteration adapter to people, the collect iteration defers to that of anySatisfy: only the elements anySatisfy requires are "collected."
MutableList<Person> people = Lists.mutable.with(person1, person2, person3);
LazyIterable<Person> lazyPeople = people.asLazy();
LazyIterable<Address> addresses = lazyPeople.collect(addressFunction);
Assert.assertTrue(addresses.anySatisfy(Predicates.equal(address2)));
This example demonstrates lazy iteration using both Java 8 lambdas and method references:
LazyIterable<Person> lazyPeople = people.asLazy();
LazyIterable<Address> addresses =
lazyPeople.flatCollect(person -> person.getAddresses());
//or
LazyIterable<Address> addresses =
lazyPeople.flatCollect(Person::getAddresses);
Finally, note these Eclipse Collections methods that implicitly return a lazy-iterable type.
Lazy interables in MutableMap implementations
valuesView() : RichIterable
An unmodifiable view of the map’s values.
keysView() : RichIterable
An unmodifiable view of the map’s keyset.
keyValuesView() : RichIterable
An unmodifiable view of the map’s entryset.
Lazy interables in Multimap implementations
keyMultiValuePairsView() : RichIterable
An unmodifiable view of key and multi-value pairs.
keysView() : RichIterable
An unmodifiable view of unique keys.
keyValuePairsView() : RichIterable
An unmodifiable view of key/value pairs.
multiValuesView() : RichIterable
An unmodifiable view of each key’s values, without the key.
An API that combines parallel iteration with lazy evaluation.
Parallel-eager utility is available through the ParallelIterate utility class. Serial-lazy evaluation is available through LazyIterable, the view returned by RichIterable.asLazy(). The ParallelIterable interface defines a view where iteration is both parallel and lazy. Sub-interfaces of ParallelIterable are returned from the various implementations of asParallel( ExecutorService executorService, int batchSize ). The ParallelIterable API is new in 5.0 and considered experimental, as indicated by the @Beta annotation. API tagged as @Beta may be altered in ways that are not backward-compatible, even in minor versions of Eclipse Collections. The method asParallel is not on interfaces like RichIterable in version 5.0, but rather on a few supported collections, including FastList and UnifiedSet.
FastList integers = Lists.mutable.with(1, 2, 3, 4, 5, 6, 7, 8, 9);
ExecutorService threadPool =
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
int batchSize = 2;
ParallelListIterable parallelListIterable =
integers.asParallel(threadPool, batchSize);
// deferred evaluation
ParallelListIterable evenNumbers =
parallelListIterable.select(each -> each % 2 == 0);
// deferred evaluation
ParallelListIterable evenStrings =
evenNumbers.collect(Object::toString);
// forced evaluation
MutableList strings = evenStrings.toList();
threadPool.shutdown();
Assert.assertEquals(Lists.mutable.with("2", "4", "6", "8"), strings);
In this code example, the calls to select and collect are lazy, as indicated by the fact that they return subclasses of ParallelIterable. The call to toList() forces evaluation.
The two parameters to asParallel(ExecutorService executorService, int batchSize) are used to configure parallelism.
executorService |
This code example sets up a thread pool with one thread per core, which is appropriate for CPU-bound tasks. A thread pool used for I/O bound tasks should be infinite or should have one thread per I/O-bound resource, for example, one thread per database connection. It often makes sense to share thread pools between multiple calls to asParallel. |
batchSize |
The batch size determines the number of elements from the backing collection (FastList or UnifiedSet) that get processed by each task submitted to the thread pool. Appropriate batch sizes for CPU-bound tasks are usually larger, in the 10,000 to 100,000 range. |
As with lazy evaluation, there is no guarantee that using parallel-lazy evaluation will yield better performance than simple serial-eager evaluation. Performance testing is required, using an appropriate thread pool and trying various batch sizes.
It’s possible to cancel a parallel-lazy computation in progress. It requires a thread pool that can be shut down, which means it usually won’t be a thread pool shared between multiple computations. Cancelling also requires a runnable thread with access to the thread pool. Building on the previous example, we just need to change evenStrings.toList() to execute in a background thread. Then the main thread could call threadPool.shutdownNow() which would cause toList() to terminate relatively quickly by throwing an exception. Shutting down the thread pool won’t stop any batches in progress. However, no new batches will be started.
These methods are available on all implementations of RichIterable.
Methods that convert collection elements to a string that can be appended to a stream or buffer.
The makeString method returns a representation of the calling RichIterable collection as a String object. Elements are converted to strings as they would be by String.valueOf(Object). You can specify start and end strings as delimiters (the default is an empty string for both) and the separator string for the between-values delimiter (defaults to comma and space).
makeString(startString, separatorString, endString): String
Returns a string representation of the calling collection that is a list of elements in the order they are returned by the iterator, enclosed in the startString and endString. Elements are delimited by the separatorString.
makeString(separatorString): String
Same result with no starting and ending strings.
makeString(): String
Same result with the default delimiter ", " (comma space) and no starting and ending strings.
MutableList<Integer> list = Lists.mutable.with(1, 2, 3);
String myDelim = list.makeString("[", "/", "]"); // "[1/2/3]"
String mySeper = list.makeString("/"); // "1/2/3"
String defaultString = list.makeString(); //"1, 2, 3"
MutableList<Integer> list = Lists.mutable.with(1, 2, 3);
String myDelim =
list.stream().map(Object::toString).collect(Collectors.joining("/", "[", "]")); // "[1/2/3]"
String mySeper =
list.stream().map(Object::toString).collect(Collectors.joining("/")); // "1/2/3"
String defaultString =
list.stream().map(Object::toString).collect(Collectors.joining()); // "1/2/3"
MutableList<Integer> list = Lists.mutable.with(1, 2, 3);
String myDelim =
list.stream().collect(Collectors2.makeString("[", "/", "]")); // "[1/2/3]"
String mySeper =
list.stream().collect(Collectors2.makeString("/")); // "1/2/3"
String defaultString =
list.stream().collect(Collectors2.makeString()); // "1/2/3"
The appendString method uses forms similar to makeString, but the string representation of the collection is written to a Java Appendable object, such as a PrintStream, StringBuilder or StringBuffer; the method itself is void.
appendString(Appendable, startString, separatorString, endString): void
Appends a string representation of this collection onto the given Appendable using the specified start, end, and separator strings
appendString(Appendable, separatorString): void
Appends with specified separator, but no starting or ending strings.
appendString(Appendable): void
Appends with the default delimiter ", " (comma space) and no starting and ending strings.
MutableList<Integer> list = Lists.mutable.with(1, 2, 3);
Appendable myStringBuilder = new StringBuilder();
list.appendString(myStringBuilder, "[", "/", "]"); //"[1/2/3]");
Get the total number of elements that satisfy a condition.
The count and countWith methods calculate the number of collection elements that satisfy a given predicate. The countWith method takes a second parameter that is used as an additional argument in evaluating the current element.
count(Predicate): int
Returns the number of elements that satisfy the Predicate. For example:
Here is a Java 8+ lambda example:
int count =
people.count(person -> person.getAddress().getState().getName().equals("New York"));
int count =
people.count(new Predicate<Person>()
{
public boolean value(Person person)
{
return person.getAddress().getState().getName().equals("New York");
}
});
countWith(Predicate2, parameter): int
Returns the number of elements that satisfy the Predicate2. The second parameter to countWith is passed as the second parameter to the Predicate2.
int count =
lastNames.countWith(Predicates2.equal(), "Smith");
Use these methods to get the total number of collection items or to determine whether the collection is empty.
size() : int
Returns the number of items in the collection.
isEmpty() : boolean
Returns true if this iterable has zero items.
notEmpty() : boolean
Returns true if this iterable has greater than zero items.
Locate elements by iteration position or highest/lowest value.
The getFirst and getLast methods return the first and last elements, respectively of a RichIterable collection. In the case of a List, these are the elements at the first and last index. For all any other collections, getFirst and getLast return the first and last elements that would be returned by an iterator. Note that the first or last element of a hash-based Set could be any element, because element order in a hashed structure is not defined. Both methods return null if the collection is empty. If null is a valid element, use the isEmpty method to determine if the container is in fact empty.
getFirst(): element
Returns the first element of an iterable collection.
getLast(): element
Returns the last element of an iterable collection.
The min and max methods, without parameters, return an element from an iterable based on its natural order, that is, by calling the compareTo() method on each element.
max(): element
Returns the maximum value based on the natural ordering.
min(): element
Returns the minimum value based on the natural ordering.
RichIterable<Integer> iterable = Lists.mutable.with(5, 4, 8, 9, 1);
Assert.assertEquals(Integer.valueOf(9), iterable.max());
Assert.assertEquals(Integer.valueOf(1), iterable.min());
If any element in the iterable is not Comparable, then a ClassCastException is thrown.
RichIterable<Object> iterable = Lists.mutable.with(5, 4, 8, 9, 1, new Foo());
iterable.max(); // throws ClassCastException
The min and max methods each have an overload that takes a Comparator that determines the natural order.
max(Comparator): element
Returns the maximum element out of this collection based on the comparator.
min(Comparator): element
Returns the minimum element out of this collection based on the comparator.
public class SillyWalk
{
public final int wiggles;
public SillyWalk(int wiggles)
{
this.wiggles = wiggles;
}
}
private static final Comparator<SillyWalk> SILLY_WALK_COMPARATOR =
new Comparator<SillyWalk>()
{
public int compare(SillyWalk o1, SillyWalk o2)
{
return o1.wiggles - o2.wiggles;
}
};
SillyWalk sillyWalk2 = new SillyWalk(2);
SillyWalk sillyWalk3 = new SillyWalk(3);
RichIterable<SillyWalk> walks = Lists.mutable.with(sillyWalk2, sillyWalk3);
Assert.assertEquals(sillyWalk3,walks.max(SILLY_WALK_COMPARATOR));
Assert.assertEquals(sillyWalk2,walks.min(SILLY_WALK_COMPARATOR));
The related methods minBy and maxBy take a Function and return the minimum or maximum element in the RichIterable based on the natural order of the attribute returned by the Function.
maxBy(Function): element
Returns the maximum element out of this collection based on the result of applying the Function to each element.
minBy(Function): element
Returns the minimum element out of this collection based on the result of applying the Function to each element.
Here, we find the youngest person (the minimum person by age).
Person alice = new Person("Alice", 40);
Person bob = new Person("Bob", 30);
Person charlie = new Person("Charlie", 50);
MutableList<Person> people = Lists.mutable.with(alice, bob, charlie);
Assert.assertEquals(bob, people.minBy(Person::getAge));
In this code example we already had a Function, so calling minBy was more concise than calling min(). These two forms are equivalent though.
people.minBy(Person::getAge);
people.min(Comparators.byFunction(Person::getAge));
Methods that create maps of aggregated values by grouping them on a calculated key.
The aggregateBy method groups elements in the RichIterable by the Function. Then all the elements that map to the same key are aggregated together using the Function2. The third parameter, a Function0, creates the initial value in each aggregation. Aggregate results are allowed to be immutable as they will be replaced in the map.
aggregateBy is conceptually analogous to calling a groupBy method on a RichIterable to create a Multimap, and then calling injectInto on each collection of the Multimap values to create a MapIterable.
aggregateInPlaceBy is similar to aggregateBy, but it mutates values in the output map instead of replacing them. Thus in this case, the aggregate results must be mutable.
aggregateBy(Function, Function0, Function2): MapIterable
Returns a MapIterable by grouping results by keys supplied by evaluating a Function.
FastList<Integer> integers = FastList.newListWith(1, 1, 1, 2, 2, 3);
MutableMap<Integer, Integer> aggregation =
integers.aggregateBy(
integer -> integer % 2,
() -> 0,
Integer::sum);
Assert.assertEquals(4, aggregation.get(0).intValue());
Assert.assertEquals(6, aggregation.get(1).intValue());
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());
aggregateInPlaceBy(Function, Function0, Function2): MapIterable
Returns the same result as aggregateBy with no starting and ending strings.
FastList<Integer> integers = FastList.newListWith(1, 1, 1, 2, 2, 3);
MutableMap<Integer, AtomicInteger> aggregation =
integers.aggregateInPlaceBy(
integer -> integer % 2,
() -> new AtomicInteger(0),
AtomicInteger::addAndGet)
Assert.assertEquals(4, aggregation.get(0).intValue());
Assert.assertEquals(6, aggregation.get(1).intValue());
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());
Grouping and pairing elements of one or more collections.
The chunk method can be used to gather the elements of a collection into chunks; that is, it creates a collection made up of collections of a specified fixed size (an integer). If the size doesn’t divide evenly into the total of collection elements, then the final chunk is smaller.
chunk(size): RichIterable
Returns a new collection with the source collection’s elements grouped in "chunks," with size elements in each chunk, and the last chunk containing the remaining elements, if any.
MutableList<Integer> list =
Lists.mutable.with(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
RichIterable<RichIterable<Integer>> chunks = list.chunk(4);
System.out.println(chunks);
This example prints out:
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]]
The zip method pairs up the elements of one RichIterable with those of second. If one of the two collections has more elements than the other, those remaining elements are dropped. The zipWithIndex method is a special case of zip that pairs the elements in a collection with their index positions.
zip(RichIterable): RichIterable
Returns a new RichIterable by combining, into pairs, corresponding elements from the calling RichIterable collection and the RichIterable collection named in the parameter. If one of the two collections is longer, its remaining elements are ignored..
MutableList<String> list1 = Lists.mutable.with("One", "Two", "Three", "Truncated");
MutableList<String> list2 = Lists.mutable.with("Four", "Five", "Six");
MutableList<Pair<String, String>> pairs = list1.zip(list2);
System.out.println(pairs);
This example prints out:
[One:Four, Two:Five, Three:Six]
zipWithIndex(): RichIterable
Returns a new RichIterable consisting of the calling collection’s elements, each paired with its index (beginning with index 0).
MutableList<String> list = Lists.mutable.with("One", "Two", "Three");
MutableList<Pair<String, Integer>> pairs = list.zipWithIndex();
System.out.println(pairs);
This example prints out:
[One:0, Two:1, Three:2]
Using selectWith, rejectWith, and collectWith inside other iteration patterns (or loops) where code blocks can be created outside of the outer iteration patterns or made static.
The collection-returning iteration methods - collect, select, and reject - each take a single parameter: a code block that itself takes a single argument. These patterns have alternate forms, methods named collectWith, selectWith, andrejectWith respectively. The same is true of the boolean-returning short-circuit methods, detect, anySatisfy, allSatisfy, and noneSatisfy; each has a counterpart having the suffix "With." All of these are available on RichIterable and its subinterfaces.
In each case, the "…With" form of the method takes two parameters:
-
The first method parameter is a code block that itself takes two arguments; the first argument of the code block is the current element with each iteration.
-
The second method parameter is an object that is then passed to the code block as its second argument.
selectWith(Predicate2, argument): RichIterable
rejectWith(Predicate2, argument): RichIterable
For each element of the collection, Predicate2 is evaluated with the element as one argument, plus one additional argument; selected or rejected elements are returned in a new collection of the same type.
collectWith(Predicate2, argument): RichIterable
Same as the collect method, but two arguments are passed to the code block; returns a new collection of the same type and size.
Note
|
These " …With" forms accomplish exactly the same actions as their basic counterparts. Although slightly more verbose, they allow for a specific performance optimization, that is, the reuse of the code block with different arguments. |
Here is an example of select that finds the adults in a list of people. First, the JDK version, and then rewritten in Eclipse Collections form:
List<Person> people =...;
List<Person> adults = Lists.mutable.empty();
for (Person person : people)
{
if (person.getAge() >= 18)
{
adults.add(person);
}
}
MutableList<Person> people =...;
MutableList<Person> adults =
people.select(each -> each.getAge() >= 18);
MutableList<Person> people =...;
MutableList<Person> adults = people.select(
new Predicate<Person>()
{
public boolean accept(Person each)
{
return each.getAge() >= 18;
}
});
Here’s the same algorithm, again in Eclipse Collections, this time using selectWith:
MutableList<Person> people =...;
MutableList<Person> adults =
people.selectWith((eachPerson, age) -> eachPerson.getAge() > age, 18);
// or
MutableList<Person> adults =
people.selectWith(Person::isOlderThan, 18);
MutableList<Person> people =...;
MutableList<Person> adults = people.selectWith(
new Predicate2<Person, Integer>()
{
@Override
public boolean accept(Person eachPerson, Integer age)
{
return eachPerson.getAge() > age;
}
}, 18);
In this single instance, there is no reason to write it out this longer way; the extra generality - making age the second argument to the Predicate2 - is unnecessary.
It does make sense, however, if you wanted to filter on multiple ages: you could hold onto and reuse the Predicate2, thereby creating less garbage.
MutableList<Person> people =...;
Predicate2<Person, Integer> agePredicate =
new Predicate2<Person, Integer>()
{
@Override
public boolean accept(Person eachPerson, Integer age)
{
return eachPerson.getAge() > age;
}
};
MutableList<Person> drivers = people.selectWith(agePredicate, 17);
MutableList<Person> voters = people.selectWith(agePredicate, 18);
MutableList<Person> drinkers = people.selectWith(agePredicate, 21);
collectWith, selectWith, and rejectWith work well with method references.
MutableList<Person> drivers = people.selectWith(Person::isOlderThan, 17);
MutableList<Person> voters = people.selectWith(Person::isOlderThan, 18);
MutableList<Person> drinkers = people.selectWith(Person::isOlderThan, 21);
This style encourages adding more behavior to the classes held in the containers. This style works with any "…With" method in Java 8 or higher.
Methods for iterating over Maps and Multimaps.
The MapIterable and Multimap interfaces are associative arrays that contain key-value pairs. All of the keys in a MapIterable are unique; a Multimap can have multiple values associated with each key.
The Multimap interface does not extend MapIterable. Multimap has a number of subinterfaces, such as ListMultimap, SetMultimap, and BagMultimap, each with custom behavior for how to handle the collection of values associated with each key.
Wrapper classes that return an iterable view of a map; ForEach patterns for Map containers.
These three methods each return an unmodifiable RichIterable view of a Map. They are essentially wrappers over the modifiable, non-lazy objects returned by the corresponding Java Collections Framework methods.
valuesView(): RichIterable
(Maps and Multimaps) Returns an unmodifiable RichIterable wrapper over the values of the Map.
keysView(): RichIterable
(Maps and Multimaps) Returns an unmodifiable RichIterable wrapper over the keySet of the Map.
keyValuesView(): RichIterable
(Maps only) Returns an unmodifiable lazy iterable of key/value pairs.
These three methods call a code block for each element on a Map; all return void.
forEachKey(Procedure): void
Calls the Procedure on each key of the Map.
forEachValue(Procedure): void
Calls the Procedure on each value of the Map.
forEachKeyValue(Procedure2): void
Calls the Procedure2 on each key-value pair of the Map.
Gather entries from another collection into a Map.
Use the collectKeysAndValues method to add all the entries derived from another collection into the current Map.
collectKeysAndValues(collection, keyFunction, valueFunction): MutableMap
(Mutable maps only) The key and value for each entry is determined by applying the keyFunction and valueFunction (in each case, a Function) to each item in collection. Each is converted into a key-value entry and inserted into the Map. If a new entry has the same key as an existing entry in the calling map, the new entry’s value replaces that of the existing entry.
Detect a value by its key and, optionally, insert or return other values.
The updateValue, getIfAbsent and ifPresentApply methods locate a specified key and return a map value that corresponds to that key. Depending on whether a value is found at the given key, each method performs a specific action.
add(Pair<K, V>): value
Adds the given key-value pair to the map. This is a convenience method for working with Pairs, similar to put(K, V).
updateValue(key, Function0, Function): value
If a value in the Map corresponds to the specified key, this method applies the specified Function to the value and replaces the value; otherwise applies the Function to the value supplied by the Function0 and puts the result as a value in the map at the specified key.
updateValueWith(key, Function0, Function2, parameter): value
If a value in the Map corresponds to the specified key, this method applies the specified Function2 to the value and the specified parameter and replaces the value with the result; otherwise applies the Function2 to the value supplied by the Function0 and the parameter and puts the result as a value in the map at the specified key.
getIfAbsent(key, Function0): element (or Function0 result)
Returns the value in the Map that corresponds to the specified key; if there is no value at the key, returns the result of evaluating the specified Function0 (here, specifically, a code block without parameters that returns some object).
getIfAbsentPut(key, value): element
Returns the value in the Map that corresponds to the specified key; if there is no value at the key, returns specified value, and puts that value in the map at the specified key.
getIfAbsentPut(key, Function0): element (or Function0 result)
Returns the value in the Map that corresponds to the specified key; if there is no value at the key, returns the result of evaluating the specified Function0, and puts that value in the map at the specified key
getIfAbsentPutWith(key, Function,parameter): element (or Function result)
Returns the value in the Map that corresponds to the specified key; if there is no value at the key, returns the result of evaluating the specified one-argument Function using the specified parameter, and put that value in the map at the specified key.
*getIfAbsentWith(key, Function, parameter) : element (or Function result)*
Returns the value in the Map that corresponds to the specified key; if there is no value at the key, returns the result of evaluating the specified Function and parameter.
*ifPresentApply(key, Function) : (Function result)*
If there is a value in the Map that corresponds to the specified key, returns the result of evaluating the specified Function with the value, otherwise returns null.
Return a new associative array where the position of the keys and values have been flipped
flip(): Multimap
Since the values in the MapIterable are not necessarily unique, flip() returns a Multimap instead of a MapIterable.
flipUniqueValues(): MapIterable
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.