Java stream: iterate two collections, chaining response - java

I have following block, processRule() removes entries from diff list.
public List<Difference> process(List<Rule> rules, List<Difference> differences) {
for (Rule rule : rules) {
differences = processRule(rule, differences);
}
return differences;
}
how can this be done with stream api? i can't just use flatMap because i need each new call to processRule() to have reduced differences as an argument.

May be something like this using stream reduce.
Note: not tested, posting from my mobile
return rules
.stream()
.reduce(differences, (rule1, rule2) ->
processRule(rule2,
processRule(rule1, differences))
} );

Related

Can this code be reduced using Java 8 Streams?

I want to use Java 8 lambdas and streams to reduce the amount of code in the following method that produces an Optional. Is it possible to achieve?
My code:
protected Optional<String> getMediaName(Participant participant) {
for (ParticipantDevice device : participant.getDevices()) {
if (device.getMedia() != null && StringUtils.isNotEmpty(device.getMedia().getMediaType())) {
String mediaType = device.getMedia().getMediaType().toUpperCase();
Map<String, String> mediaToNameMap = config.getMediaMap();
if (mediaMap.containsKey(mediaType)) {
return Optional.of(mediaMap.get(mediaType));
}
}
}
return Optional.empty();
}
Yes. Assuming the following class hierarchy (I used records here).
record Media(String getMediaType) {
}
record ParticipantDevice(Media getMedia) {
}
record Participant(List<ParticipantDevice> getDevices) {
}
It is pretty self explanatory. Unless you have an empty string as a key you don't need, imo, to check for it in your search. The main difference here is that once the map entry is found, Optional.map is used to return the value instead of the key.
I also checked this out against your loop version and it works the same.
public static Optional<String> getMediaName(Participant participant) {
Map<String, String> mediaToNameMap = config.getMediaMap();
return participant.getDevices().stream()
.map(ParticipantDevice::getMedia).filter(Objects::nonNull)
.map(media -> media.getMediaType().toUpperCase())
.filter(mediaType -> mediaToNameMap.containsKey(mediaType))
.findFirst()
.map(mediaToNameMap::get);
}
Firstly, since your Map of media types returned by config.getMediaMap() doesn't depend on a particular device, it makes sense to generate it before processing the collection of devices. I.e. regurless of the approach (imperative or declarative) do it outside a Loop, or before creating a Stream, to avoid generating the same Map multiple times.
And to implement this method with Streams, you need to use filter() operation, which expects a Predicate, to apply the conditional logic and map() perform a transformation of stream elements.
To get the first element that matches the conditions apply findFirst(), which produces an optional result, as a terminal operation.
protected Optional<String> getMediaName(Participant participant) {
Map<String, String> mediaToNameMap = config.getMediaMap();
return participant.getDevices().stream()
.filter(device -> device.getMedia() != null
&& StringUtils.isNotEmpty(device.getMedia().getMediaType())
)
.map(device -> device.getMedia().getMediaType().toUpperCase())
.filter(mediaToNameMap::containsKey)
.map(mediaToNameMap::get)
.findFirst();
}

Arbitrary created with flatMap does not consider the filter

I am trying jqwik (version 1.5.1) and I read from the documentation that I can create an Arbitrary whose generated value depends on the one supplied by another Arbitrary, specifically using the flatMap function.
My actual goal is different, but based on this idea: I need 2 Arbitrarys that always generate different values for a single test. This is what I tried:
#Provide
private Arbitrary<Tuple.Tuple2<Integer, Integer>> getValues() {
var firstArbitrary = Arbitraries.integers().between(1, Integer.MAX_VALUE);
var secondArbitrary = firstArbitrary.flatMap(first ->
Arbitraries.integers().between(1, Integer.MAX_VALUE).filter(i -> !i.equals(first)));
return Combinators.combine(firstArbitrary, secondArbitrary).as(Tuple::of);
}
#Property
public void test(#ForAll("getValues") Tuple.Tuple2<Integer, Integer> values) {
assertThat(values.get1()).isNotEqualTo(values.get2());
}
And it immediately fails with this sample:
Shrunk Sample (1 steps)
-----------------------
arg0: (1, 1)
Throwing an AssertionError of course:
java.lang.AssertionError:
Expecting:
1
not to be equal to:
1
I expected the filter function would have been enough to exclude the generated value produced by the firstArbitrary but it seems like it is not even considered, or more likely it does something else. What am I missing? Is there an easier way to make sure that, given a certain number of integer generators, they always produce different values?
The general idea of one generated value influencing the next generation step through flatMap is right. The thing you are missing is that you loose this coupling by combining firstArbitrary and secondArbitrary outside of the flat mapping scope. The fix is minor:
#Provide
private Arbitrary<Tuple.Tuple2<Integer, Integer>> getValues() {
var firstArbitrary = Arbitraries.integers().between(1, Integer.MAX_VALUE);
return firstArbitrary.flatMap(
first -> Arbitraries.integers().between(1, Integer.MAX_VALUE)
.filter(i -> !i.equals(first))
.map(second -> Tuple.of(first, second))
);
}
That said there are more - I'd argue simpler - ways to achieve your goal:
#Provide
private Arbitrary<Tuple.Tuple2<Integer, Integer>> getValues() {
var firstArbitrary = Arbitraries.integers().between(1, Integer.MAX_VALUE);
return firstArbitrary.tuple2().filter(t -> !t.get1().equals(t.get2()));
}
This gets rid of flat mapping, which means less effort while shrinking for jqwik.
Another possible solution:
#Provide
private Arbitrary<Tuple.Tuple2<Integer, Integer>> getValues() {
var firstArbitrary = Arbitraries.integers().between(1, Integer.MAX_VALUE);
return firstArbitrary.list().ofSize(2).uniqueElements().map(l -> Tuple.of(l.get(0), l.get(1)));
}
This one might seem a bit involved, but it has the advantage that no flat mapping and no filtering is being used. Filtering often reduces performance of generation, edge cases, exhaustive generation and shrinking. That's why I steer clear of filtering whenever I can without too much hassle.

Convert an for each loop with var inside to a Java Stream?

I have a function:
String fun(List<Function<String, String>> pro, String x){
for(var p: pro){
x = p.apply(x);
}
return x;
}
How can I convert this function to functional style instead of imperative style?
Assuming what you want is to apply each function to your string, passing along the result of each function to the next, you can do this with reduce.
String fun(List<Function<String, String>> functions, String x) {
return functions.stream()
.reduce(s -> s, Function::andThen)
.apply(x);
}
Using reduce with andThen creates a combined function that chains your list of functions together. We then apply the combined function to x.
Alternatively, #Naman in the comments suggests the formulation:
functions.stream()
.reduce(Function::andThen)
.orElse(Function.identity())
.apply(x)
which I believe performs one fewer andThen operation (when the list of functions is nonempty), but is functionally the same as the first version.
(Function.identity() is an another way to write s -> s.)
I believe you are already aware about those compilation errors. You can't just define List<Function<>> without having a common understanding about those list of functions. Maybe you can get some inspiration from below code snippet.
String fun(List<Function<String, String>> listOfFunctions, String commonInputStr){
for (Function<String, String> function : listOfFunctions) {
String tempValStr = function.apply(commonInputStr);
if (tempValStr != null){
return tempValStr;
}
}
return null;
}
Or if you want to find the first result value like below:
Optional<String> fun(List<Function<String, String>> listOfFunctions, String commonInputStr){
return listOfFunctions.stream()
.map(stringStringFunction -> stringStringFunction.apply(commonInputStr))
.findFirst();
}

Replace nested for loops with parallel stream - Java

I'm working on improving the speed of a program where performance is critical. Currently it fails to process large data sets. There are many nested for loops and so I thought it would be worth trying parallel streams. I have access to a high performance cluster so potentially have many cores available.
I have the method below:
public MinSpecSetFamily getMinDomSpecSets() {
MinSpecSetFamily result = new MinSpecSetFamily();
ResourceType minRT = this.getFirstEssentialResourceType();
if (minRT == null || minRT.noSpecies()) {
System.out.println("Problem in getMinDomSpecSets()");
}
for (Species spec : minRT.specList) {
SpecTree minTree = this.getMinimalConstSpecTreeRootedAt(spec);
ArrayList<SpecTreeNode> leafList = minTree.getLeaves();
for (SpecTreeNode leaf : leafList) {
ArrayList<Species> sp = leaf.getAncestors();
SpecSet tmpSet = new SpecSet(sp);
result.addSpecSet(tmpSet);
}
}
return result;
}
I understand that I can turn a nested for loop into a parallel stream with something like:
minRT.specList.parallelStream().flatMap(leaf -> leaflist.parallelStream())
However, I cannot find examples showing how to deal with the actions inside each for loop and I'm not at all confident about how this is supposed to work. I'd really appreciate some assistance and explanation of how to convert this method so that I can translate the solution to other methods in the program too.
Thanks.
Here's one way of doing it (hopefully I have no typos):
MinSpecSetFamily result =
minRT.specList
.parallelStream()
.flatMap(spec -> getMinimalConstSpecTreeRootedAt(spec).getLeaves().stream())
.map(leaf -> new SpecSet(leaf.getAncestors()))
.reduce(new MinSpecSetFamily (),
(fam,set)-> {
fam.addSpecSet(set);
return fam;
},
(f1, f2) -> new MinSpecSetFamily(f1, f2));
EDIT: Following Holger's comment, you should use collect instead of reduce:
MinSpecSetFamily result =
minRT.specList
.parallelStream()
.flatMap(spec -> getMinimalConstSpecTreeRootedAt(spec).getLeaves().stream())
.map(leaf -> new SpecSet(leaf.getAncestors()))
.collect(MinSpecSetFamily::new,MinSpecSetFamily::addSpecSet,MinSpecSetFamily::add);

Loop fusion of Stream in Java-8 (how it works internally)

I'm reading the book 'Java in Action'.
And I saw an example code of Stream in the book.
List<String> names = menu.stream()
.filter(d -> {
System.out.println("filtering" + d.getName());
return d.getCalories() > 300;
})
.map(d -> {
System.out.println("mapping" + d.getName());
return d.getName();
})
.limit(3)
.collect(toList());
When the code is executed, the result is as follows.
filtering __1__.
mapping __1__.
filtering __2__.
mapping __2__.
filtering __3__.
mapping __3__.
That is, because of limit(3), the log message is printed only 3 times!
In this book, this is called in "loop fusion."
But, I don't understand this.
Because, if you know whether an object is filtered, you have to calculate the filtering function. Then, "filtering ..." message is should be printed, I think.
Please, Explain me about how the loop fusion works internally.
“Because, if you [want to] know whether an object is filtered, you have to calculate the filtering function”, is right, but perhaps your sample data wasn’t sufficient to illustrate the point. If you try
List<String> result = Stream.of("java", "streams", "are", "great", "stuff")
.filter(s -> {
System.out.println("filtering " + s);
return s.length()>=4;
})
.map(s -> {
System.out.println("mapping " + s);
return s.toUpperCase();
})
.limit(3)
.collect(Collectors.toList());
System.out.println("Result:");
result.forEach(System.out::println);
it will print
filtering java
mapping java
filtering streams
mapping streams
filtering are
filtering great
mapping great
Result:
JAVA
STREAMS
GREAT
Showing that
In order to find three elements matching the filter, you might have to evaluate more than three elements, here, four element are evaluated, but you don’t need to evaluate subsequent elements once you have three matches
The subsequent mapping function only need to be applied to matching elements. This allows to conclude that it is irrelevant whether .map(…).limit(…)or .limit(…).map(…) was specified.
This differs from the relative position of .filter and .limit which is relevant.
The term “loop fusion” implies that there is not a filtering loop, followed by a mapping loop, followed by a limit operation, but only one loop (conceptionally), performing the entire work, equivalent to the following single loop:
String[] source = { "java", "streams", "are", "great", "stuff"};
List<String> result = new ArrayList<>();
int limit = 3;
for(String s: source) {
System.out.println("filtering " + s);
if(s.length()>=4) {
System.out.println("mapping " + s);
String t = s.toUpperCase();
if(limit-->0) {
result.add(t);
}
else break;
}
}
I think you got it wrong. limit is actually called short-circuiting (because it is executed only 3 times).
While loop fusion is filter and map executed at a single pass. These two operations where merged into a single one that is executed at each element.
You do not see output like this:
filtering
filtering
filtering
mapping
mapping
mapping
Instead you see filter followed immediately by a map; so these two operations were merged into a single one.
Generally you should not care how that is done internally (it build a pipeline of these operations), because this might change and it is implementation specific.

Categories

Resources