Relatively new to java streams and I have encountered an issue which I could not find a solution for.
I have A list of 10 objects and filtered them to return 4 objects.
List<MyObject> objects.stream()
.filter(x -> x.getFlag == 1)
.filter(x -> x.amount != null)
.collect(Collectors.toList());
this returns 4 objects:
OBJECT 1: {stageNumber = 2, stageToCalc = 1}
OBJECT 2: {stageNumber = 5, stageToCalc = 1}
OBJECT 3: {stageNumber = 9, stageToCalc = 7}
OBJECT 4: {stageNumber = 10, stageToCalc = 7}
So basically what I am trying to do is take stages that have the same value of stageToCalc and find the maximium stageNumber.
E.G: object 3 and object 4 have the same stageToCalc = 7, maximum value according to stage number should return Object 4: {stageNumber = 10, stageToCalc = 7} which is the higher value.
however, my issue comes when I need to get 2 (or more since it might be dynamic). in this case :
it should return object 2 and Object 4.
I have tried using:
objects.stream()
.collect(groupingBy(Function.identity(), TreeMap::new, toList())
.lastEntry()
.getValue()
.forEach(System.out::println)
This will just result in a classCastException
, and if I Use .max()
it would not compile.
I could easy accomplish this in an expensive way with some for loops etc.
however I wonder if there is a way in using just 1 stream iteration.
Hope I am clear on this question. Still new and learning.
THANKS.
CodePudding user response:
You could try using Collectors.toMap():
public record MyObject(int stageNumber, int stageToCalc) {}
List<MyObject> objects = List.of(
new MyObject(1, 1),
new MyObject(5, 1),
new MyObject(3, 1),
new MyObject(1, 2),
new MyObject(5, 3),
new MyObject(1, 3)
);
Map<Integer, MyObject> result = objects.stream()
.collect(Collectors.toMap(
MyObject::stageToCalc,
Function.identity(),
BinaryOperator.maxBy(Comparator.comparing(MyObject::stageNumber))
));
System.out.println(result.values());
Output:
[MyObject[stageNumber=5, stageToCalc=1], MyObject[stageNumber=1, stageToCalc=2], MyObject[stageNumber=5, stageToCalc=3]]
CodePudding user response:
You can use Collectors.groupingBy()
with a downstream Collectors.maxBy()
to get the appropriate objects (With Collectors.collectAndThen()
to pull them out of the Optional
values returned by maxBy()
):
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class Demo {
private record MyObject(int stageNumber, int stageToCalc) {}
public static void main(String[] args) {
List<MyObject> objects = List.of(new MyObject(2, 1), new MyObject(5, 1), new MyObject(9, 7), new MyObject(10, 7));;
List<MyObject> maximums =
new ArrayList<>(objects.stream()
.collect(Collectors.groupingBy(MyObject::stageToCalc,
Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparing(MyObject::stageNumber)),
Optional::orElseThrow)))
.values());
System.out.println(maximums);
}
}
will print out
[MyObject[stageNumber=5, stageToCalc=1], MyObject[stageNumber=10, stageToCalc=7]]
CodePudding user response:
Just do the grouping again, this time, for the other field:
record MyObject(int stageNumber, int stageToCalc) { }
List<MyObject> list = List.of(
new MyObject(2, 1),
new MyObject(5, 1),
new MyObject(9, 7),
new MyObject(10, 7)
);
list.stream()
.collect(Collectors.groupingBy(v -> v.stageToCalc, TreeMap::new, Collectors.toList()))
.lastEntry()
.getValue()
// ---------------------------------------
.stream()
.collect(Collectors.groupingBy(v -> v.stageNumber, TreeMap::new, Collectors.toList()))
.lastEntry()
.getValue()
// ---------------------------------------
.forEach(System.out::println);