-
Notifications
You must be signed in to change notification settings - Fork 4
List Tutorial
Javimmutable provides a JImmutableList interface with a signature similar to java.util.List. (Note: Versions prior to 3.x provided a second interface called JImmutableRandomAccessList but that interface was removed in 3.0.0.) This interfaces provides access to elements in the JImmutableList using an integer index. As with List valid indexes are always in the range zero through size() - 1. Javimmutable uses assign() instead of set(), insert() instead of add(), and delete() instead of remove() so that when converting code from java.util.List
to JImmutableList
the compiler will find all of the places that you need to replace a simple method call with an assignment.
The interface, JImmutableList
, provides indexed access to elements within the list and allows addition and removal of values at any index. Values added to the end of list are always stored in the same order that they were added. In addition entire JImmutableLists can be inserted into or appended to other JImmutableLists very efficiently (roughly the same effort to insert an entire JImmutableList as to insert a single element). The current implementation uses a balanced binary tree with leaf nodes containing up to 64 values each which provides O(log2(n)) performance for all operations. (see Comparative Performance)
As with all of the immutable collection classes any method that modifies the list actually creates a new list instance and returns it as the method's result. This result can be assigned to the original list variable or to a new variable as needed. The original, unmodified, version of the list remains in memory until garbage collected. The lists reuse as much structure as possible so adding and removing elements requires very little copying and only a small amount of additional memory.
In the example below notice that changed
's third value is now 45 and list
's third value is still 30. The two lists internally contain shared copies of the common values to minimize memory consumption and improve performance but as a user of the list you don't need to worry about that detail.
JImmutableList<Integer> list = JImmutables.list();
list = list.insert(10).insert(20).insert(30);
assertEquals(10, list.get(0));
assertEquals(20, list.get(1));
assertEquals(30, list.get(2));
JImmutableList<Integer> changed = list.deleteLast().insert(45);
assertEquals(10, list.get(0));
assertEquals(20, list.get(1));
assertEquals(30, list.get(2));
assertEquals(10, changed.get(0));
assertEquals(20, changed.get(1));
assertEquals(45, changed.get(2));
The JImmutableList
interfaces provide a getList()
method that returns an object implementing java.util.List
using the original list as its data source. The actual data is not copied so this method has very low overhead. You can use this any time you need to pass a JImmutableList
to code that needs a java.util.List
. The resulting List
is unmodifiable so set, remove, etc methods all throw UnsupportedOperationExceptions.
assertEquals(Arrays.asList(10, 20, 30), list.getList());
assertEquals(Arrays.asList(10, 20, 45), changed.getList());
In versions prior to 3.0.0 the JImmutableRandomAccessList
interface was provided to extend JImmutableList with additional methods supporting insertion and removal of values within the list (not just the beginning or end). Since 3.0.0 this interface was removed and JImmutableList supports this feature.
JImmutableList<Integer> list = JImmutables.list();
list = list.insert(30).insert(0, 20).insert(0, 10);
assertEquals(10, list.get(0));
assertEquals(20, list.get(1));
assertEquals(30, list.get(2));
JImmutableList<Integer> list2 = list.delete(1).insert(1, 87);
assertEquals(10, list.get(0));
assertEquals(20, list.get(1));
assertEquals(30, list.get(2));
assertEquals(10, list2.get(0));
assertEquals(87, list2.get(1));
assertEquals(30, list2.get(2));
assertEquals(Arrays.asList(10, 20, 30), list.getList());
assertEquals(Arrays.asList(10, 87, 30), list2.getList());
JImmutableLists provide special methods for filtering the values in the list. The select()
method returns a list containing only those values for which a Predicate
returns true. The reject()
method returns a list containing all elements except those for which a Predicate
returns true. Both methods are optimized for the Predicate
returning true less often than false. Additionally forEach() and reduce() methods can be used to quickly scan and execute a function on all elements of the list without the overhead of creating Iterators. The stream() method allows the use of Java streams to manipulate the contents of the list. Full support is provided for parallel streams.
JImmutableList<String> source = JImmutables.list("able", "baker", "charlie", "delta", "echo");
assertEquals(JImmutables.list("baker", "charlie"), source.select(str -> str.contains("r")));
assertEquals(JImmutables.list("able", "baker", "delta"), source.reject(str -> str.contains("h")));
assertEquals("ablebakercharliedeltaecho", source.reduce("", (answer, str) -> answer + str));
assertEquals(JImmutables.list("baker", "charlie"),
source.stream()
.filter(str -> str.contains("r"))
.collect(JImmutables.listCollector()));