Let's say I have a List
of classes that each has a Map
.
public class Test {
public Map<Long, Integer> map;
}
the Long
keys in the Map
are timestamps and the Integer
values are scores.
I am trying to create a Stream
that can combine the maps from all objects and output a Map
with the unique Timestamps (The Long
s) and an average score.
I have this code, but it gives me the sum of all the scores and not the average (The Integer
class doesn't have an average method).
Test test1 = new Test();
test1.map = new HashMap() {{
put(1000L, 1);
put(2000L, 2);
put(3000L, 3);
}};
Test test2 = new Test();
test2.map = new HashMap() {{
put(1000L, 10);
put(2000L, 20);
put(3000L, 30);
}};
List<Test> tests = new ArrayList() {{
add(test1);
add(test2);
}};
Map<Long, Integer> merged = tests.stream()
.map(test -> test.map)
.map(Map::entrySet)
.flatMap(Collection::stream)
.collect(
Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
Integer::sum
)
);
System.out.println(merged);
I'm thinking that this might not be an easy problem so solve in a single Stream
, so an output with a Map
with the unique timestamps and a List
of all the scores would also be fine. Then I can calculate the average myself.
Map<Long, List<Integer>>
It it possible?
The average() method of the IntStream class in Java returns an OptionalDouble describing the arithmetic mean of elements of this stream, or an empty optional if this stream is empty. It gets the average of the elements of the stream.
The Java HashMap merge() method inserts the specified key/value mapping to the hashmap if the specified key is already not present. If the specified key is already associated with a value, the method replaces the old value with the result of the specified function.
Java 8 Stream's map method is intermediate operation and consumes single element forom input Stream and produces single element to output Stream. It simply used to convert Stream of one type to another. Let's see method signature of Stream's map method.
Instead of Collectors.toMap
use Collectors.groupingBy
:
Map<Long, Double> merged = tests.stream()
.map(test -> test.map)
.map(Map::entrySet)
.flatMap(Collection::stream)
.collect(
Collectors.groupingBy(
Map.Entry::getKey,
Collectors.averagingInt(Map.Entry::getValue)
)
);
Oh, and even though you probably don't need it anymore, you can get the Map<Long, List<Integer>>
you asked about in the last part of your question just as easily:
Map<Long, List<Integer>> merged = tests.stream()
.map(test -> test.map)
.map(Map::entrySet)
.flatMap(Collection::stream)
.collect(
Collectors.groupingBy(
Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue, Collectors.toList())
)
);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With