A comprehensive collections library for Java delivering productivity and performance through an expressive and efficient set of APIs and types.
—
Eclipse Collections provides advanced collection types and utility APIs for specialized use cases including Multimaps, BiMaps, Partitions, and various container types with sophisticated operations.
Multimaps provide one-to-many key-value relationships where each key can map to multiple values.
/**
* Base multimap interface providing one-to-many key-value relationships
*/
interface Multimap<K, V> {
// Size and capacity operations
/**
* Returns total number of key-value pairs
* @return total size of multimap
*/
int size();
/**
* Returns number of distinct keys
* @return count of unique keys
*/
int sizeDistinct();
/**
* Tests if multimap is empty
* @return true if no key-value pairs exist
*/
boolean isEmpty();
/**
* Tests if multimap is not empty
* @return true if key-value pairs exist
*/
boolean notEmpty();
// Key-value operations
/**
* Associates key with value
* @param key key to associate
* @param value value to associate with key
* @return true if multimap was modified
*/
boolean put(K key, V value);
/**
* Associates key with multiple values
* @param key key to associate
* @param values values to associate with key
* @return true if multimap was modified
*/
boolean putAll(K key, Iterable<? extends V> values);
/**
* Associates all key-value pairs from another multimap
* @param multimap source multimap to copy from
* @return true if this multimap was modified
*/
boolean putAll(Multimap<? extends K, ? extends V> multimap);
// Retrieval operations
/**
* Gets all values associated with key
* @param key key to retrieve values for
* @return RichIterable of values for key (empty if key not found)
*/
RichIterable<V> get(K key);
/**
* Tests if key exists in multimap
* @param key key to test
* @return true if key has associated values
*/
boolean containsKey(Object key);
/**
* Tests if value exists in multimap
* @param value value to test
* @return true if value is associated with any key
*/
boolean containsValue(Object value);
/**
* Tests if specific key-value pair exists
* @param key key to test
* @param value value to test
* @return true if key-value pair exists
*/
boolean containsKeyValuePair(Object key, Object value);
// Removal operations
/**
* Removes specific key-value pair
* @param key key to remove from
* @param value value to remove
* @return true if pair was removed
*/
boolean remove(Object key, Object value);
/**
* Removes all values for key
* @param key key to remove all values for
* @return RichIterable of removed values
*/
RichIterable<V> removeAll(Object key);
/**
* Removes all key-value pairs
*/
void clear();
// Views and iteration
/**
* Gets view of all distinct keys
* @return RichIterable of keys
*/
RichIterable<K> keysView();
/**
* Gets view of all values (including duplicates)
* @return RichIterable of all values
*/
RichIterable<V> valuesView();
/**
* Gets view of keys with their occurrence counts
* @return Bag mapping keys to occurrence counts
*/
Bag<K> keyBag();
/**
* Gets view of key-multivalues pairs
* @return RichIterable of Pairs where each pair contains key and its values collection
*/
RichIterable<Pair<K, RichIterable<V>>> keyMultiValuePairsView();
/**
* Gets view of individual key-value pairs
* @return RichIterable of individual Pairs
*/
RichIterable<Pair<K, V>> keyValuePairsView();
// Transformation operations
/**
* Create new multimap with keys and values swapped
* @return Multimap with flipped key-value relationships
*/
Multimap<V, K> flip();
/**
* Filter key-value pairs based on predicate
* @param predicate predicate to test key-value pairs
* @return new Multimap with pairs matching predicate
*/
Multimap<K, V> selectKeysValues(Predicate2<? super K, ? super V> predicate);
/**
* Filter out key-value pairs based on predicate
* @param predicate predicate to test key-value pairs
* @return new Multimap with pairs not matching predicate
*/
Multimap<K, V> rejectKeysValues(Predicate2<? super K, ? super V> predicate);
/**
* Filter keys based on predicate
* @param predicate predicate to test keys
* @return new Multimap with keys matching predicate
*/
Multimap<K, V> selectKeysMultiValues(Predicate2<? super K, ? super RichIterable<V>> predicate);
/**
* Transform key-value pairs
* @param function function to transform key-value pairs
* @return new Multimap with transformed pairs
*/
<K2, V2> Multimap<K2, V2> collectKeysValues(Function2<? super K, ? super V, Pair<K2, V2>> function);
/**
* Transform keys
* @param function function to transform keys
* @return new Multimap with transformed keys
*/
<NK> Multimap<NK, V> collectKeys(Function<? super K, ? extends NK> function);
/**
* Transform values
* @param function function to transform values
* @return new Multimap with transformed values
*/
<NV> Multimap<K, NV> collectValues(Function<? super V, ? extends NV> function);
// Grouping operations
/**
* Group values by applying function to each value
* @param function function to compute grouping key from values
* @return new Multimap grouped by function results
*/
<NV> Multimap<NV, V> groupBy(Function<? super V, ? extends NV> function);
// Conversion operations
/**
* Convert to regular map with collection values
* @return MutableMap where values are collections
*/
MutableMap<K, RichIterable<V>> toMap();
/**
* Convert to map with lists as values
* @return MutableMapIterable with List values
*/
MutableMapIterable<K, RichIterable<V>> toMapWithTarget();
}
/**
* Mutable multimap interface
*/
interface MutableMultimap<K, V> extends Multimap<K, V> {
// Fluent API methods
MutableMultimap<K, V> withKeyValue(K key, V value);
MutableMultimap<K, V> withKeyMultiValues(K key, V... values);
MutableMultimap<K, V> withoutKey(K key);
MutableMultimap<K, V> withoutKeyValue(K key, V value);
// Mutable transformations
MutableMultimap<K, V> selectKeysValues(Predicate2<? super K, ? super V> predicate);
MutableMultimap<K, V> rejectKeysValues(Predicate2<? super K, ? super V> predicate);
<K2, V2> MutableMultimap<K2, V2> collectKeysValues(Function2<? super K, ? super V, Pair<K2, V2>> function);
<NK> MutableMultimap<NK, V> collectKeys(Function<? super K, ? extends NK> function);
<NV> MutableMultimap<K, NV> collectValues(Function<? super V, ? extends NV> function);
// Immutable view
ImmutableMultimap<K, V> toImmutable();
}
/**
* Immutable multimap interface
*/
interface ImmutableMultimap<K, V> extends Multimap<K, V> {
// Immutable modification operations
ImmutableMultimap<K, V> newWith(K key, V value);
ImmutableMultimap<K, V> newWithAll(K key, Iterable<? extends V> values);
ImmutableMultimap<K, V> newWithoutKey(K key);
ImmutableMultimap<K, V> newWithout(Object key, Object value);
// Immutable transformations
ImmutableMultimap<K, V> selectKeysValues(Predicate2<? super K, ? super V> predicate);
ImmutableMultimap<K, V> rejectKeysValues(Predicate2<? super K, ? super V> predicate);
<K2, V2> ImmutableMultimap<K2, V2> collectKeysValues(Function2<? super K, ? super V, Pair<K2, V2>> function);
<NK> ImmutableMultimap<NK, V> collectKeys(Function<? super K, ? extends NK> function);
<NV> ImmutableMultimap<K, NV> collectValues(Function<? super V, ? extends NV> function);
}Different multimap implementations provide various value collection behaviors.
/**
* Multimap that stores values as lists (preserves insertion order, allows duplicates)
*/
interface ListMultimap<K, V> extends Multimap<K, V> {
/**
* Gets values as a list for the specified key
* @param key key to get values for
* @return MutableList of values (empty list if key not found)
*/
MutableList<V> get(K key);
// Transformation operations return ListMultimaps
ListMultimap<K, V> selectKeysValues(Predicate2<? super K, ? super V> predicate);
ListMultimap<K, V> rejectKeysValues(Predicate2<? super K, ? super V> predicate);
<K2, V2> ListMultimap<K2, V2> collectKeysValues(Function2<? super K, ? super V, Pair<K2, V2>> function);
<NK> ListMultimap<NK, V> collectKeys(Function<? super K, ? extends NK> function);
<NV> ListMultimap<K, NV> collectValues(Function<? super V, ? extends NV> function);
}
/**
* Multimap that stores values as sets (no duplicates)
*/
interface SetMultimap<K, V> extends Multimap<K, V> {
/**
* Gets values as a set for the specified key
* @param key key to get values for
* @return MutableSet of values (empty set if key not found)
*/
MutableSet<V> get(K key);
// Set-specific transformation operations
SetMultimap<K, V> selectKeysValues(Predicate2<? super K, ? super V> predicate);
SetMultimap<K, V> rejectKeysValues(Predicate2<? super K, ? super V> predicate);
<K2, V2> SetMultimap<K2, V2> collectKeysValues(Function2<? super K, ? super V, Pair<K2, V2>> function);
<NK> SetMultimap<NK, V> collectKeys(Function<? super K, ? extends NK> function);
<NV> SetMultimap<K, NV> collectValues(Function<? super V, ? extends NV> function);
}
/**
* Multimap that stores values as bags (allows duplicates with counts)
*/
interface BagMultimap<K, V> extends Multimap<K, V> {
/**
* Gets values as a bag for the specified key
* @param key key to get values for
* @return MutableBag of values with occurrence counts (empty bag if key not found)
*/
MutableBag<V> get(K key);
// Bag-specific transformation operations
BagMultimap<K, V> selectKeysValues(Predicate2<? super K, ? super V> predicate);
BagMultimap<K, V> rejectKeysValues(Predicate2<? super K, ? super V> predicate);
<K2, V2> BagMultimap<K2, V2> collectKeysValues(Function2<? super K, ? super V, Pair<K2, V2>> function);
<NK> BagMultimap<NK, V> collectKeys(Function<? super K, ? extends NK> function);
<NV> BagMultimap<K, NV> collectValues(Function<? super V, ? extends NV> function);
}
/**
* Multimap that stores values as sorted sets
*/
interface SortedSetMultimap<K, V> extends SetMultimap<K, V> {
/**
* Gets values as a sorted set for the specified key
* @param key key to get values for
* @return MutableSortedSet of values (empty sorted set if key not found)
*/
MutableSortedSet<V> get(K key);
/**
* Gets the comparator used for sorting values
* @return Comparator used for value ordering
*/
Comparator<? super V> comparator();
}
/**
* Multimap that stores values as sorted bags
*/
interface SortedBagMultimap<K, V> extends BagMultimap<K, V> {
/**
* Gets values as a sorted bag for the specified key
* @param key key to get values for
* @return MutableSortedBag of values (empty sorted bag if key not found)
*/
MutableSortedBag<V> get(K key);
/**
* Gets the comparator used for sorting values
* @return Comparator used for value ordering
*/
Comparator<? super V> comparator();
}Usage Examples:
import org.eclipse.collections.impl.factory.Multimaps;
// Create different types of multimaps
MutableListMultimap<String, Integer> listMultimap = Multimaps.mutable.list.empty();
MutableSetMultimap<String, String> setMultimap = Multimaps.mutable.set.empty();
MutableBagMultimap<String, String> bagMultimap = Multimaps.mutable.bag.empty();
// ListMultimap preserves order and allows duplicates
listMultimap.put("numbers", 1);
listMultimap.put("numbers", 2);
listMultimap.put("numbers", 1); // Duplicate allowed
MutableList<Integer> numbers = listMultimap.get("numbers"); // [1, 2, 1]
// SetMultimap prevents duplicates
setMultimap.put("colors", "red");
setMultimap.put("colors", "blue");
setMultimap.put("colors", "red"); // Duplicate ignored
MutableSet<String> colors = setMultimap.get("colors"); // [red, blue]
// BagMultimap tracks occurrence counts
bagMultimap.put("words", "hello");
bagMultimap.put("words", "world");
bagMultimap.put("words", "hello"); // Tracked as second occurrence
MutableBag<String> words = bagMultimap.get("words"); // {hello=2, world=1}
// Multimap operations
Multimap<String, Integer> flipped = listMultimap.flip(); // Values become keys
// Group a list into multimap
List<Person> people = Arrays.asList(
new Person("Alice", "Engineering"),
new Person("Bob", "Sales"),
new Person("Charlie", "Engineering")
);
ListMultimap<String, Person> peopleByDepartment = people
.stream()
.collect(Multimaps.toListMultimap(Person::getDepartment, Function.identity()));Partition APIs split collections into two groups based on a predicate.
/**
* Base partition interface splitting collection into selected and rejected elements
*/
interface PartitionIterable<T> {
/**
* Gets elements that satisfied the partitioning predicate
* @return RichIterable of selected elements
*/
RichIterable<T> getSelected();
/**
* Gets elements that did not satisfy the partitioning predicate
* @return RichIterable of rejected elements
*/
RichIterable<T> getRejected();
}
/**
* Mutable partition interface
*/
interface PartitionMutableCollection<T> extends PartitionIterable<T> {
/**
* Gets selected elements as mutable collection
* @return MutableCollection of selected elements
*/
MutableCollection<T> getSelected();
/**
* Gets rejected elements as mutable collection
* @return MutableCollection of rejected elements
*/
MutableCollection<T> getRejected();
}
/**
* Immutable partition interface
*/
interface PartitionImmutableCollection<T> extends PartitionIterable<T> {
/**
* Gets selected elements as immutable collection
* @return ImmutableCollection of selected elements
*/
ImmutableCollection<T> getSelected();
/**
* Gets rejected elements as immutable collection
* @return ImmutableCollection of rejected elements
*/
ImmutableCollection<T> getRejected();
}
// Type-specific partition interfaces
/**
* Partition for lists
*/
interface PartitionList<T> extends PartitionIterable<T> {
MutableList<T> getSelected();
MutableList<T> getRejected();
}
/**
* Partition for sets
*/
interface PartitionSet<T> extends PartitionIterable<T> {
MutableSet<T> getSelected();
MutableSet<T> getRejected();
}
/**
* Partition for bags
*/
interface PartitionBag<T> extends PartitionIterable<T> {
MutableBag<T> getSelected();
MutableBag<T> getRejected();
}
/**
* Partition for stacks
*/
interface PartitionStack<T> extends PartitionIterable<T> {
MutableStack<T> getSelected();
MutableStack<T> getRejected();
}Usage Examples:
import org.eclipse.collections.impl.factory.Lists;
MutableList<Integer> numbers = Lists.mutable.with(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Partition into even and odd numbers
PartitionList<Integer> evenOddPartition = numbers.partition(x -> x % 2 == 0);
MutableList<Integer> evens = evenOddPartition.getSelected(); // [2, 4, 6, 8, 10]
MutableList<Integer> odds = evenOddPartition.getRejected(); // [1, 3, 5, 7, 9]
// Partition with parameter
PartitionList<Integer> aboveThreshold = numbers.partitionWith(
(num, threshold) -> num > threshold,
5
);
MutableList<Integer> above5 = aboveThreshold.getSelected(); // [6, 7, 8, 9, 10]
MutableList<Integer> below5 = aboveThreshold.getRejected(); // [1, 2, 3, 4, 5]
// Multiple partitioning
List<String> words = Lists.mutable.with("apple", "banana", "cherry", "date", "elderberry");
PartitionList<String> shortLong = words.partition(word -> word.length() <= 5);
MutableList<String> shortWords = shortLong.getSelected(); // [apple, date]
MutableList<String> longWords = shortLong.getRejected(); // [banana, cherry, elderberry]BiMaps provide bidirectional one-to-one mappings between keys and values.
/**
* Bidirectional map providing one-to-one mapping between keys and values
* Both keys and values must be unique
*/
interface BiMap<K, V> extends MapIterable<K, V> {
/**
* Get the inverse BiMap where values become keys and keys become values
* @return BiMap with key-value relationships reversed
*/
BiMap<V, K> inverse();
/**
* Put key-value pair, enforcing uniqueness of both keys and values
* If key already exists, its previous value is removed from value->key mapping
* If value already exists, its previous key is removed from key->value mapping
* @param key key to put
* @param value value to put
* @return previous value associated with key, or null
* @throws IllegalArgumentException if putting would create duplicate values
*/
V put(K key, V value);
}
/**
* Mutable bidirectional map
*/
interface MutableBiMap<K, V> extends BiMap<K, V>, MutableMap<K, V> {
/**
* Force put key-value pair, removing any existing mappings that would conflict
* This method removes existing mappings to ensure uniqueness constraints
* @param key key to put
* @param value value to put
* @return previous value associated with key, or null
*/
V forcePut(K key, V value);
/**
* Get inverse mutable BiMap
* @return MutableBiMap with relationships reversed
*/
MutableBiMap<V, K> inverse();
// Fluent API
MutableBiMap<K, V> withKeyValue(K key, V value);
MutableBiMap<K, V> withoutKey(K key);
MutableBiMap<K, V> withoutAllKeys(Iterable<? extends K> keys);
// Transformations
MutableBiMap<K, V> select(Predicate2<? super K, ? super V> predicate);
MutableBiMap<K, V> reject(Predicate2<? super K, ? super V> predicate);
// Immutable view
ImmutableBiMap<K, V> toImmutable();
}
/**
* Immutable bidirectional map
*/
interface ImmutableBiMap<K, V> extends BiMap<K, V>, ImmutableMapIterable<K, V> {
/**
* Get inverse immutable BiMap
* @return ImmutableBiMap with relationships reversed
*/
ImmutableBiMap<V, K> inverse();
// Immutable modification operations
ImmutableBiMap<K, V> newWithKeyValue(K key, V value);
ImmutableBiMap<K, V> newWithoutKey(K key);
ImmutableBiMap<K, V> newWithoutAllKeys(Iterable<? extends K> keys);
// Immutable transformations
ImmutableBiMap<K, V> select(Predicate2<? super K, ? super V> predicate);
ImmutableBiMap<K, V> reject(Predicate2<? super K, ? super V> predicate);
}Usage Examples:
import org.eclipse.collections.impl.bimap.mutable.HashBiMap;
// Create bidirectional mapping between IDs and names
MutableBiMap<Integer, String> idToName = HashBiMap.newMap();
idToName.put(1, "Alice");
idToName.put(2, "Bob");
idToName.put(3, "Charlie");
// Access in both directions
String name = idToName.get(2); // "Bob"
Integer id = idToName.inverse().get("Alice"); // 1
// BiMap enforces uniqueness of both keys and values
idToName.put(4, "Alice"); // This removes the existing mapping 1 -> "Alice"
// Now idToName contains: {2="Bob", 3="Charlie", 4="Alice"}
// Force put removes conflicting mappings
idToName.forcePut(5, "Bob"); // Removes 2 -> "Bob" and adds 5 -> "Bob"
// Now idToName contains: {3="Charlie", 4="Alice", 5="Bob"}
// Use inverse BiMap
MutableBiMap<String, Integer> nameToId = idToName.inverse();
Integer charlieId = nameToId.get("Charlie"); // 3
String idForFive = nameToId.inverse().get(5); // "Bob"
// BiMaps are useful for lookups in both directions
public class UserService {
private final MutableBiMap<Long, String> userIdToUsername = HashBiMap.newMap();
public void addUser(Long id, String username) {
userIdToUsername.put(id, username);
}
public String getUsernameById(Long id) {
return userIdToUsername.get(id);
}
public Long getUserIdByUsername(String username) {
return userIdToUsername.inverse().get(username);
}
}Enhanced iterator interfaces providing additional functionality.
/**
* Enhanced iterator with additional methods beyond standard Iterator
*/
interface RichIterator<T> extends Iterator<T> {
/**
* Collect remaining elements using transformation function
* @param function transformation function
* @return MutableList of transformed elements
*/
<V> MutableList<V> collect(Function<? super T, ? extends V> function);
/**
* Select remaining elements matching predicate
* @param predicate predicate for selection
* @return MutableList of matching elements
*/
MutableList<T> select(Predicate<? super T> predicate);
/**
* Reject remaining elements matching predicate
* @param predicate predicate for rejection
* @return MutableList of non-matching elements
*/
MutableList<T> reject(Predicate<? super T> predicate);
/**
* Detect first remaining element matching predicate
* @param predicate predicate for detection
* @return first matching element or null
*/
T detect(Predicate<? super T> predicate);
/**
* Test if any remaining element matches predicate
* @param predicate predicate to test
* @return true if any element matches
*/
boolean anySatisfy(Predicate<? super T> predicate);
/**
* Test if all remaining elements match predicate
* @param predicate predicate to test
* @return true if all elements match
*/
boolean allSatisfy(Predicate<? super T> predicate);
/**
* Count remaining elements matching predicate
* @param predicate predicate for counting
* @return count of matching elements
*/
int count(Predicate<? super T> predicate);
/**
* Collect remaining elements to list
* @return MutableList of remaining elements
*/
MutableList<T> toList();
}
/**
* Mutable iterator supporting removal operations
*/
interface MutableIterator<T> extends Iterator<T> {
/**
* Remove current element (standard Iterator method)
*/
void remove();
}
// Primitive iterators for each primitive type
/**
* Iterator for int primitives
*/
interface IntIterator {
/**
* Test if more int elements available
* @return true if more elements exist
*/
boolean hasNext();
/**
* Get next int element
* @return next int value
* @throws NoSuchElementException if no more elements
*/
int next();
}
// Similar primitive iterators exist for:
// BooleanIterator, ByteIterator, CharIterator, ShortIterator
// LongIterator, FloatIterator, DoubleIteratorCollections with defined iteration order providing positional operations.
/**
* Base interface for collections with defined iteration order
*/
interface OrderedIterable<T> extends RichIterable<T> {
/**
* Get first element
* @return first element
* @throws NoSuchElementException if empty
*/
T getFirst();
/**
* Get last element
* @return last element
* @throws NoSuchElementException if empty
*/
T getLast();
/**
* Find index of object in ordered collection
* @param object object to find
* @return index of object or -1 if not found
*/
int indexOf(Object object);
/**
* Get element at index
* @param index index to retrieve
* @return element at index
* @throws IndexOutOfBoundsException if index invalid
*/
T get(int index);
// Positional operations
/**
* Take first N elements
* @param count number of elements to take
* @return OrderedIterable of first N elements
*/
OrderedIterable<T> take(int count);
/**
* Drop first N elements
* @param count number of elements to drop
* @return OrderedIterable without first N elements
*/
OrderedIterable<T> drop(int count);
/**
* Take elements while predicate is true
* @param predicate condition to test elements
* @return OrderedIterable of elements taken while condition holds
*/
OrderedIterable<T> takeWhile(Predicate<? super T> predicate);
/**
* Drop elements while predicate is true
* @param predicate condition to test elements
* @return OrderedIterable after dropping elements while condition holds
*/
OrderedIterable<T> dropWhile(Predicate<? super T> predicate);
/**
* Remove consecutive duplicate elements preserving order
* @return OrderedIterable with duplicates removed
*/
OrderedIterable<T> distinct();
/**
* Reverse the order of elements
* @return OrderedIterable with reversed order
*/
OrderedIterable<T> toReversed();
// Zipping operations
/**
* Zip with another iterable creating pairs
* @param that iterable to zip with
* @return OrderedIterable of Pairs
*/
<S> OrderedIterable<Pair<T, S>> zip(Iterable<S> that);
/**
* Zip with indices creating index-element pairs
* @return OrderedIterable of Pairs containing elements and their indices
*/
OrderedIterable<Pair<T, Integer>> zipWithIndex();
}
/**
* Mutable ordered collection
*/
interface MutableOrderedIterable<T> extends OrderedIterable<T>, MutableCollection<T> {
// Mutable positional operations
MutableOrderedIterable<T> take(int count);
MutableOrderedIterable<T> drop(int count);
MutableOrderedIterable<T> takeWhile(Predicate<? super T> predicate);
MutableOrderedIterable<T> dropWhile(Predicate<? super T> predicate);
MutableOrderedIterable<T> distinct();
MutableOrderedIterable<T> toReversed();
// Mutable zipping
<S> MutableOrderedIterable<Pair<T, S>> zip(Iterable<S> that);
MutableOrderedIterable<Pair<T, Integer>> zipWithIndex();
}Usage Examples:
import org.eclipse.collections.impl.factory.Lists;
MutableList<Integer> numbers = Lists.mutable.with(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Positional operations
OrderedIterable<Integer> firstFive = numbers.take(5); // [1, 2, 3, 4, 5]
OrderedIterable<Integer> lastFive = numbers.drop(5); // [6, 7, 8, 9, 10]
OrderedIterable<Integer> whileLessThan5 = numbers.takeWhile(x -> x < 5); // [1, 2, 3, 4]
OrderedIterable<Integer> afterGreaterThan5 = numbers.dropWhile(x -> x <= 5); // [6, 7, 8, 9, 10]
// Zipping operations
List<String> letters = Lists.mutable.with("A", "B", "C", "D", "E");
OrderedIterable<Pair<Integer, String>> zipped = numbers.take(5).zip(letters);
// [(1,A), (2,B), (3,C), (4,D), (5,E)]
OrderedIterable<Pair<Integer, Integer>> withIndex = numbers.take(3).zipWithIndex();
// [(1,0), (2,1), (3,2)]
// Distinct operation
List<Integer> withDuplicates = Lists.mutable.with(1, 2, 2, 3, 3, 3, 4, 4, 5);
OrderedIterable<Integer> uniqueValues = withDuplicates.distinct(); // [1, 2, 3, 4, 5]Additional specialized collection types and features.
/**
* Collection that maintains fixed size after creation
* Modification operations that would change size throw UnsupportedOperationException
*/
interface FixedSizeCollection<T> extends MutableCollection<T> {
// Available operations: set, replace, sort, reverse
// Not available: add, remove, clear (size-changing operations)
}
/**
* Thread-safe collection optimized for multiple readers with single writer
*/
interface MultiReaderCollection<T> extends MutableCollection<T> {
/**
* Acquire read lock for thread-safe read operations
* @return read lock
*/
Lock getReadLock();
/**
* Acquire write lock for thread-safe write operations
* @return write lock
*/
Lock getWriteLock();
/**
* Execute read operation with automatic read lock management
* @param procedure read operation to perform
*/
void withReadLockAndDelegate(Procedure<? super MutableCollection<T>> procedure);
/**
* Execute write operation with automatic write lock management
* @param procedure write operation to perform
*/
void withWriteLockAndDelegate(Procedure<? super MutableCollection<T>> procedure);
}
/**
* Collection using custom hashing strategy instead of object's hashCode/equals
*/
interface MutableCollectionWithHashingStrategy<T> extends MutableCollection<T> {
/**
* Get the hashing strategy used by this collection
* @return HashingStrategy instance
*/
HashingStrategy<? super T> hashingStrategy();
}
/**
* Custom hashing and equality strategy interface
*/
interface HashingStrategy<T> {
/**
* Compute hash code for object using custom strategy
* @param object object to compute hash for
* @return hash code
*/
int computeHashCode(T object);
/**
* Test equality using custom strategy
* @param object1 first object
* @param object2 second object
* @return true if objects are equal according to strategy
*/
boolean equals(T object1, T object2);
}Usage Examples:
import org.eclipse.collections.impl.collection.mutable.CollectionAdapter;
import org.eclipse.collections.impl.set.strategy.mutable.MutableHashingStrategySet;
// Fixed-size collections
FixedSizeList<String> fixedList = Lists.fixedSize.with("A", "B", "C");
// fixedList.add("D"); // Throws UnsupportedOperationException
fixedList.set(0, "X"); // OK - doesn't change size
// Custom hashing strategy for case-insensitive strings
HashingStrategy<String> caseInsensitive = new HashingStrategy<String>() {
public int computeHashCode(String string) {
return string == null ? 0 : string.toLowerCase().hashCode();
}
public boolean equals(String s1, String s2) {
return s1 == null ? s2 == null : s1.equalsIgnoreCase(s2);
}
};
MutableSet<String> caseInsensitiveSet = new MutableHashingStrategySet<>(caseInsensitive);
caseInsensitiveSet.add("Hello");
caseInsensitiveSet.add("HELLO"); // Not added - considered duplicate
caseInsensitiveSet.add("World");
// Set contains: ["Hello", "World"] (case-insensitive duplicates removed)
// Multi-reader collections for concurrent access
MultiReaderList<String> concurrentList = MultiReaderFastList.newList();
concurrentList.add("item1");
concurrentList.add("item2");
// Thread-safe read operation
concurrentList.withReadLockAndDelegate(list -> {
// Perform read operations
int size = list.size();
boolean contains = list.contains("item1");
});
// Thread-safe write operation
concurrentList.withWriteLockAndDelegate(list -> {
// Perform write operations
list.add("item3");
list.remove("item1");
});Eclipse Collections provides seamless conversion between different collection types:
// Convert between mutable and immutable
MutableList<String> mutableList = Lists.mutable.with("A", "B", "C");
ImmutableList<String> immutableList = mutableList.toImmutable();
MutableList<String> backToMutable = immutableList.toList();
// Convert between different collection types
MutableSet<String> set = mutableList.toSet();
MutableBag<String> bag = mutableList.toBag();
MutableStack<String> stack = Stacks.mutable.withAll(mutableList);
// Convert to JDK collections
List<String> jdkList = mutableList.castToList();
Set<String> jdkSet = set.castToSet();
Map<String, String> jdkMap = Maps.mutable.with("key1", "value1").castToMap();
// Convert from JDK collections
List<String> jdkSource = Arrays.asList("X", "Y", "Z");
MutableList<String> adapted = ListAdapter.adapt(jdkSource);
MutableList<String> copied = Lists.mutable.withAll(jdkSource);Use Eclipse Collections features for optimal performance:
// Use primitive collections to avoid boxing
MutableIntList primitiveInts = IntLists.mutable.with(1, 2, 3, 4, 5);
long sum = primitiveInts.sum(); // No boxing
// Use lazy evaluation for large datasets
LazyIterable<String> lazy = hugeMutableList
.asLazy()
.select(expensive::predicate)
.collect(expensive::transformation)
.select(another::predicate);
// Only computed when terminal operation called
MutableList<String> result = lazy.toList();
// Use parallel processing for CPU-intensive operations
MutableList<ComplexObject> processed = hugeList
.asParallel(ForkJoinPool.commonPool(), 1000)
.collect(ComplexObject::expensiveTransformation)
.toList();
// Use immutable collections for thread safety without locks
ImmutableList<String> threadSafeList = Lists.immutable.with("A", "B", "C");
// Can be safely shared across threads without synchronizationInstall with Tessl CLI
npx tessl i tessl/maven-org-eclipse-collections--eclipse-collections