Mysterious Eclipse Collections APIs: forEachInBoth
Sometimes APIs go undiscovered for a long time
Have you ever heard of forEachInBoth?
There is a method named forEachInBoth
available in Eclipse Collections, but it can only be found on four utility classes. You can find the method on ArrayIterate
, ListIterate
, RandomAccessListIterate
and ArrayListIterate
.
The method forEachInBoth
, takes two Lists or Arrays of the same size and iterates over both of them passing the elements from both lists at particular indexes into a two argument procedure.
The method has not been promoted to the ListIterable
API in Eclipse Collections. This is probably because you can now zip
two lists together and just use forEach
or any of the other rich APIs from MutableList
instead.
The Method Signatures
The method signatures for forEachInBoth
on ListIterate
, RandomAccessListIterate
and ArrayListIterate
are very similar so I will only show the signatures for ListIterate
and ArrayIterate
.
ListIterate
public static <T1, T2> void forEachInBoth(
List<T1> list1,
List<T2> list2,
Procedure2<? super T1, ? super T2> procedure)
ArrayIterate
public static <T1, T2> void forEachInBoth(
T1[] objectArray1,
T2[] objectArray2,
Procedure2<? super T1, ? super T2> procedure)
When is forEachInBoth useful?
One example that I discovered that forEachInBoth
works well with is for converting two separate lists or arrays into a Map
. Using forEachInBoth
, I am able to use a method reference for calling put on the map instance. This was unexpectedly cool.
The Source
@Test
public void forEachInBothList()
{
List<Integer> one =
Lists.mutable.with(1, 2, 3);
List<String> two =
Lists.mutable.with("One", "Two", "Three");
Map<Integer, String> map = Maps.mutable.empty();
ListIterate.forEachInBoth(one, two, map::put);
Assert.assertEquals(
Maps.mutable.with(1, "One", 2, "Two", 3, "Three"),
map);
}
This could easily work with two arrays of the same size using ArrayIterate
instead.
What about zip?
If you use zip
instead, you can use the fluent API to convert the zipped Lists into a Map
using toMap
. However, I could not take an existing Map
and iterate using forEach
or each
and call Map.put
since I would be iterating over Pair
instances that would need to have the the two component parts of the pairs extracted and passed in as the key and value parameters.
The Source
@Test
public void zipList()
{
MutableList<Integer> one =
Lists.mutable.with(1, 2, 3);
MutableList<String> two =
Lists.mutable.with("One", "Two", "Three");
MutableMap<Integer, String> map =
one.zip(two).toMap(Pair::getOne, Pair::getTwo);
Assert.assertEquals(
Maps.mutable.with(1, "One", 2, "Two", 3, "Three"),
map);
}
In the case of zip
with this example, there is a temporary List
created after zip
is called, but it will be garbage collected after the call to toMap
since the List
is not referenced anywhere. This same code can be implemented lazily by calling asLazy
before calling zip
. This removes the temporary List
creation.
MutableMap<Integer, String> map =
one.asLazy()
.zip(two)
.toMap(Pair::getOne, Pair::getTwo);
When to use forEachInBoth or zip?
The primary benefit of forEachInBoth
is that it is located in static utility and will work directly with Java arrays and any java.util.List
. So if you need to convert two JDK List
s quickly into a Map
, it might just be what the doctor ordered. However, because the method forEachInBoth
returns void
, you will not be able to make fluent methods calls like you can using zip
. Most of the time, you may find zip
more useful when dealing with two lists especially if you are performing multiple operations on the result.
If you discover a novel use case where forEachInBoth
works well, please let us know by creating an issue on GitHub. We could move forEachInBoth
up directly to the ListIterable
API if there prove to be more good use cases that we just haven’t seen yet.
Eclipse Collections is open for contributions. If you like the library, you can let us know by starring it on GitHub.