Extending Guava Iterables for Java8.
Stream
provides interesting composibility constructs that encourage smaller functions that focus on very straightforward tasks. However, there are two features that are sorely lacking:
- While it implements all of the Iterable interface methods, it does not declare Iterable, meaning that you cannot use it in a "enhanced for" loop. While that may seem like mere syntactic sugar, it also means that in order to refer to references in the scope, you end up creating a "capturing lambda" which does not perform as well as a non-capturing one (some sources say 6x slower). More important than this may be the distress it places on developers who have to figure out what to do to get things to be "effectively final" such as using
int[] counter = {0};
to referencecounter[0]
inside the lambda orAtomicInteger
. - Streams are not replayable - this may be by design to declare a contract of something that is consumable, but it means that much of the goodness of the architecture is lost even when using things that should be replayable such as
Collection::stream
.
Guava's FluentIterable
class provides very similar composibility constructs and makes using Iterable
quite enjoyable. However, it seems to lack in some of the finishing composibility such as "collect", and "reduce".
Finally, going between the two constructs (Stream and Iterable) isn't incredibly easy though it can be.
<dependency>
<groupId>com.github.scr</groupId>
<artifactId>j8iterables</artifactId>
<version>1.7.1</version>
</dependency>
Stream<Integer> theStream = Stream.of(1, 2, 3);
int i = 0;
for (Integer integer : J8Iterables.fromStream(theStream)) {
Assert.assertEquals(integer, ++i);
}
return J8Iterables.emptyIterable();
Optional<Ends<Integer>> ends = J8Iterables.ends(iterable);
if (!ends.isPresent()) {
// Error case?
}
int first = ends.get().getFirst(), last = ends.get().getLast();
Iterable<Integer> input = Arrays.asList(1, 2, 3, 4);
Map<Boolean, Integer> result = J8Iterables.collect(
Collections.partitioningBy(i -> (i % 2) == 0,
Collections.reducing(Integer::sum)));
Assert.assertEquals(result.get(false), 4);
Assert.assertEquals(result.get(true), 6);
Iterable<Integer> input = Arrays.asList(1, 2, 3, 4);
List<Integer> collectingList = new ArrayList<>();
List<Integer> result = J8Iterables.peek(input, collectingList::add)
.toList();
Assert.assertEquals(result, collectingList);
// NodeList has a method elements() that returns a NodeIterator, which is not a java.util.Iterator!
org.htmlparser.NodeList childrenNodeList = node.getChildren();
Iterable<Node> children = J8Iterables.fromSupplier(() -> {
final NodeIterator NODE_ITERATOR = childrenNodeList.elements();
return new Iterator<Node>() {
@Override
public boolean hasNext() {
try {
return NODE_ITERATOR.hasMoreNodes();
} catch (ParserException e) {
return false;
}
}
@Override
public boolean next() {
try {
return NODE_ITERATOR.nextNode();
} catch (ParserException e) {
return false;
}
}
};
});
// Then just use as usual:
for (Node child : children) {
// Do something interesting!
}