Java 8 Streams - 对元组流进行分组
Java 8 Streams - Grouping a stream of tuples
编辑::我正在改写问题,以便更清楚
这段代码是我写的
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))
)
));
我想使用 steam 处理这个列表,以便输出是一个包含键 (1, 3), (2, 6), (3, 9) 的映射
所以基本上,我们对元组的左侧项目进行分组,然后对右侧项目求和。
此代码无法编译,编译器表示无法解析 getLeft、getRight 方法。
这是编译器的错误信息
/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
更新: 这回答了原始问题 (revision 2)。 @Shiraaz.M.
很好地回答了新问题
你的 Comparator
接受一对,因此你需要用 Collectors.mapping
:
调用 getRight
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)))
));
请注意,根据使用的编译器,您可能需要明确指定某些类型,因为自动推理可能会失败。
实际上,我的 StreamEx 增强了标准 Java 流的库很好地支持了此类场景。写这个可以达到同样的结果:
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)));
最后请注意,使用 maxBy
收集器您将获得 Optional
无用的值,因为它们不能为空。对于 maxBy/minBy
场景,最好使用具有自定义合并功能的 toMap
收集器:
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))));
或使用 StreamEx:
Map<String, Movie> map = StreamEx.of(ml.getDataAsStream())
.cross(Movie::getGenre)
.invert()
.toMap(BinaryOperator.maxBy(Comparator.comparing(Movie::getVoteCount)));
假设您的 ImmutablePair class 看起来像这样(这个 class 可能需要是静态的,具体取决于示例代码是否从 main 方法中执行):
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;
}
}
这似乎对我有用:
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);
结果是:
{1=3, 2=6, 3=9}
可能的问题
问题出在这段代码中:
Collectors.mapping(
ImmutablePair::getRight,
Collectors.summingInt(Comparator.comparing(ImmutablePair::getRight))
)
Collectors.mapping 收集器首先将类型 T 的对象适配为类型 U 的对象,然后在类型 U 上运行收集器。(That's the gist of the javadoc for me)
所以你正在做的是这样的:
- 将 ImmutablePair 转换为整数。
- 第二个参数现在是一个需要 Integer 类型的 Collector,但你是 "using an ImmutablePair type"。此外,Comparator.compare() return 是一个比较器,而您正在寻找 return 一个 Integer 类型。因此,要修复您的确切代码行,请将其更改为类似以下内容:
Collectors.mapping(ImmutablePair::getRight, Collectors.summingInt(Integer::valueOf))
但你不需要这个,因为你可以通过直接调用 Collectors.summingInt 来简化它:
Collectors.summingInt(ImmutablePair::getRight)
编辑::我正在改写问题,以便更清楚
这段代码是我写的
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))
)
));
我想使用 steam 处理这个列表,以便输出是一个包含键 (1, 3), (2, 6), (3, 9) 的映射
所以基本上,我们对元组的左侧项目进行分组,然后对右侧项目求和。
此代码无法编译,编译器表示无法解析 getLeft、getRight 方法。
这是编译器的错误信息
/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
更新: 这回答了原始问题 (revision 2)。 @Shiraaz.M.
很好地回答了新问题你的 Comparator
接受一对,因此你需要用 Collectors.mapping
:
getRight
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)))
));
请注意,根据使用的编译器,您可能需要明确指定某些类型,因为自动推理可能会失败。
实际上,我的 StreamEx 增强了标准 Java 流的库很好地支持了此类场景。写这个可以达到同样的结果:
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)));
最后请注意,使用 maxBy
收集器您将获得 Optional
无用的值,因为它们不能为空。对于 maxBy/minBy
场景,最好使用具有自定义合并功能的 toMap
收集器:
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))));
或使用 StreamEx:
Map<String, Movie> map = StreamEx.of(ml.getDataAsStream())
.cross(Movie::getGenre)
.invert()
.toMap(BinaryOperator.maxBy(Comparator.comparing(Movie::getVoteCount)));
假设您的 ImmutablePair class 看起来像这样(这个 class 可能需要是静态的,具体取决于示例代码是否从 main 方法中执行):
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;
}
}
这似乎对我有用:
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);
结果是:
{1=3, 2=6, 3=9}
可能的问题
问题出在这段代码中:
Collectors.mapping(
ImmutablePair::getRight,
Collectors.summingInt(Comparator.comparing(ImmutablePair::getRight))
)
Collectors.mapping 收集器首先将类型 T 的对象适配为类型 U 的对象,然后在类型 U 上运行收集器。(That's the gist of the javadoc for me)
所以你正在做的是这样的:
- 将 ImmutablePair 转换为整数。
- 第二个参数现在是一个需要 Integer 类型的 Collector,但你是 "using an ImmutablePair type"。此外,Comparator.compare() return 是一个比较器,而您正在寻找 return 一个 Integer 类型。因此,要修复您的确切代码行,请将其更改为类似以下内容:
Collectors.mapping(ImmutablePair::getRight, Collectors.summingInt(Integer::valueOf))
但你不需要这个,因为你可以通过直接调用 Collectors.summingInt 来简化它:
Collectors.summingInt(ImmutablePair::getRight)