How to use Java 8 streams to swiftly replace elements in a list
Lukas Eder pulls another magic trick from his sleeve for all Java developers – this time it’s a smooth and simple way to make lists do what you want them to do, logically.
Imagine you have a list of items:
List<String> books = Arrays.asList( "The Holy Cow: The Bovine Testament", "True Hip Hop", "Truth and Existence", "The Big Book of Green Design" );
(Don’t judge me. Books from this random book generator)
Now you’d like to create a new list where the third item only is replaced by some new value:
List<String> books = Arrays.asList( "The Holy Cow: The Bovine Testament", "True Hip Hop", "Pregnancy For Dummies", // New book at index 2 "The Big Book of Green Design" );
Of course, you could go and either modify the original list:
books.set(2, "Pregnancy For Dummies");
… or create a copy of the original list and then modify that copy:
List<String> copy = new ArrayList<>(books); copy.set(2, "Pregnancy For Dummies");
But if you want to write a one-liner to do the same in a functional style, you’ll write the following, using jOOλ.
seq(books) .zipWithIndex() .map(t -> t.v2 == 2 ? "Pregnancy For Dummies" : t.v1) .toList();
With the JDK standard Streams API, things get a bit harder. You could write:
Stream.concat( Stream.concat( books.stream().limit(2), Stream.of("Pregnancy For Dummies") ), books.stream.skip(3) ).collect(Collectors.toList());
That would be a bit unfortunate, though, as the first part of the stream would need to be traversed twice – once for the limit and once for the skipping (see also our post about the caveats of OFFSET pagination in SQL).
Swift or not?
Clearly, the JDK APIs won’t help you to write concise functional logic, as can be seen above and the “imperative” style is more straight-forward. We’ve written about this before. This has also been our main motivation to create jOOλ.
If you’re looking for even more functional bliss, do also have a look at the following libraries: