Edit:: I am rephrasing the question so that its more clear
I have written this code
List<ImmutablePair<Integer, Integer>> list = new ArrayList<ImmutablePair<Integer, Integer>>();
list.add(new ImmutablePair(1, 1));
list.add(new ImmutablePair(1, 1));
list.add(new ImmutablePair(1, 1));
list.add(new ImmutablePair(2, 2));
list.add(new ImmutablePair(2, 2));
list.add(new ImmutablePair(2, 2));
list.add(new ImmutablePair(3, 3));
list.add(new ImmutablePair(3, 3));
list.add(new ImmutablePair(3, 3));
Stream<ImmutablePair<Integer, Integer>> stream = list.stream();
Map<Integer, Integer> result = stream.collect(Collectors.groupingBy(
ImmutablePair::getLeft,
Collectors.mapping(
ImmutablePair::getRight,
Collectors.summingInt(Comparator.comparing(ImmutablePair::getRight))
)
));
I want to process this list using steams so that the output is a map which contains keys (1, 3), (2, 6), (3, 9)
So basically, we group on the left item of the tuple and then sum the right item.
This code does not compile and the compiler says that it cannot resolve getLeft, getRight methods.
Here are the error messages from the compiler
/Users/abhi/JavaProjects/MovieLambda2/src/main/java/com/abhi/MovieLambda2.java:229: error: method summingInt in class Collectors cannot be applied to given types;
Collectors.summingInt(Comparator.comparing(ImmutablePair::getRight))
^
required: ToIntFunction<? super T#1>
found: Comparator<Object>
reason: no instance(s) of type variable(s) T#2,U exist so that Comparator<T#2> conforms to ToIntFunction<? super T#1>
where T#1,T#2,U are type-variables:
T#1 extends Object declared in method <T#1>summingInt(ToIntFunction<? super T#1>)
T#2 extends Object declared in method <T#2,U>comparing(Function<? super T#2,? extends U>)
U extends Comparable<? super U> declared in method <T#2,U>comparing(Function<? super T#2,? extends U>)
/Users/abhi/JavaProjects/MovieLambda2/src/main/java/com/abhi/MovieLambda2.java:229: error: incompatible types: cannot infer type-variable(s) T,U
Collectors.summingInt(Comparator.comparing(ImmutablePair::getRight))
^
(argument mismatch; invalid method reference
no suitable method found for getRight(Object)
method Pair.getRight() is not applicable
(actual and formal argument lists differ in length)
method ImmutablePair.getRight() is not applicable
(actual and formal argument lists differ in length))
where T,U are type-variables:
T extends Object declared in method <T,U>comparing(Function<? super T,? extends U>)
U extends Comparable<? super U> declared in method <T,U>comparing(Function<? super T,? extends U>)
/Users/abhi/JavaProjects/MovieLambda2/src/main/java/com/abhi/MovieLambda2.java:229: error: invalid method reference
Collectors.summingInt(Comparator.comparing(ImmutablePair::getRight))
^
non-static method getRight() cannot be referenced from a static context
where R is a type-variable:
R extends Object declared in class ImmutablePair
/Users/abhi/JavaProjects/MovieLambda2/src/main/java/com/abhi/MovieLambda2.java:228: error: invalid method reference
ImmutablePair::getRight,
^
non-static method getRight() cannot be referenced from a static context
where R is a type-variable:
R extends Object declared in class ImmutablePair
/Users/abhi/JavaProjects/MovieLambda2/src/main/java/com/abhi/MovieLambda2.java:226: error: invalid method reference
ImmutablePair::getLeft,
^
non-static method getLeft() cannot be referenced from a static context
where L is a type-variable:
L extends Object declared in class ImmutablePair
Assuming your ImmutablePair class looks like this (this class might need to be static depending on whether the sample code was executed from within a main method):
class ImmutablePair<T,T2> {
private final T left;
private final T2 right;
public ImmutablePair(T left, T2 right) {
this.left = left;
this.right = right;
}
public T getLeft() {
return left;
}
public T2 getRight() {
return right;
}
}
This seems to work for me:
List<ImmutablePair<Integer, Integer>> list = new ArrayList<>();
list.add(new ImmutablePair(1, 1));
list.add(new ImmutablePair(1, 1));
list.add(new ImmutablePair(1, 1));
list.add(new ImmutablePair(2, 2));
list.add(new ImmutablePair(2, 2));
list.add(new ImmutablePair(2, 2));
list.add(new ImmutablePair(3, 3));
list.add(new ImmutablePair(3, 3));
list.add(new ImmutablePair(3, 3));
Map<Integer, Integer> collect = list.stream()
.collect(
Collectors.groupingBy(
ImmutablePair::getLeft,
Collectors.summingInt(ImmutablePair::getRight)));
System.out.println(collect);
And the result is:
{1=3, 2=6, 3=9}
The possible problem
The problem is in this section of code:
Collectors.mapping(
ImmutablePair::getRight,
Collectors.summingInt(Comparator.comparing(ImmutablePair::getRight))
)
The Collectors.mapping collector first adapts an object of type T into and object of type U and then runs a collector on type U. (That's the gist of the javadoc for me)
So what you're doing is something like:
Collectors.mapping(ImmutablePair::getRight, Collectors.summingInt(Integer::valueOf))
But you don't need this because you can simplify this by calling Collectors.summingInt directly:
Collectors.summingInt(ImmutablePair::getRight)
Update: this answers an original question (revision 2). New question is well answered by @Shiraaz.M.
Your Comparator
accepts a pair, thus you need to call getRight
with Collectors.mapping
:
Map<String, Optional<Movie>> map = ml.getDataAsStream()
.<ImmutablePair<String, Movie>>flatMap(x -> x.getGenre()
.map(g -> new ImmutablePair<String, Movie>(g, x)))
.collect(
Collectors.groupingBy(
ImmutablePair::getLeft,
Collectors.mapping(ImmutablePair::getRight,
Collectors.maxBy(Comparator.comparing(Movie::getVoteCount)))
));
Note that depending on compiler used you may need to specify some types explicitly as automatic inference may fail.
Actually such scenarios are well supported in my StreamEx library which enhances standard Java Streams. The same result can be achieved writing this:
Map<String, Optional<Movie>> map = StreamEx.of(ml.getDataAsStream())
.cross(Movie::getGenre) // create (Movie, Genre) entries
.invert() // swap entries to get (Genre, Movie)
.grouping(Collectors.maxBy(Comparator.comparing(Movie::getVoteCount)));
Finally note that using maxBy
collector you'll have Optional
values which is useless as they cannot be empty. For maxBy/minBy
scenarios it's better to use toMap
collector with custom merging function:
Map<String, Movie> map = ml.getDataAsStream()
.<ImmutablePair<String, Movie>>flatMap(x -> x.getGenre()
.map(g -> new ImmutablePair<String, Movie>(g, x)))
.collect(Collectors.toMap(ImmutablePair::getLeft, ImmutablePair::getRight,
BinaryOperator.maxBy(Comparator.comparing(Movie::getVoteCount))));
Or using StreamEx:
Map<String, Movie> map = StreamEx.of(ml.getDataAsStream())
.cross(Movie::getGenre)
.invert()
.toMap(BinaryOperator.maxBy(Comparator.comparing(Movie::getVoteCount)));
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