收集器与结果类型

时间:2016-06-29 03:22:11

标签: java generics java-stream collectors

我有一个数据对象列表,我需要以各种方式对其进行分组,然后对分组结果执行常见操作。所以我试图将常用操作提取到单个方法,如下面的设计示例所示:

private static void print(List<Integer> data, 
                          Collector<Integer, ?, Map<?, List<Integer>>> collector) {
    data.stream().collect(collector)
            .forEach((key, list) -> System.out.println(key + ": " + list));
}

private static void printByMagnitude() {
    List<Integer> data = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    print(data, Collectors.<Integer,String>groupingBy(i -> i < 5 ? "small" : "large"));
}

private static void printByModulus() {
    List<Integer> data = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    print(data, Collectors.<Integer,Integer>groupingBy(i -> i % 2));
}

请注意,在print方法中,收集器的结果类型是Map,其中包含未知密钥Map<?, List<Integer>>,因为当我按幅度打印时,我使用String个密钥,当我按模数打印时,我使用Integer个键。

此代码在两次调用Collectors.groupingBy时都会产生两个编译错误。第一个抱怨使用String键进行通话:

Error:(19, 58) java: incompatible types:
java.util.stream.Collector<java.lang.Integer,capture#1 of ?,
    java.util.Map<java.lang.String,java.util.List<java.lang.Integer>>>
cannot be converted to
java.util.stream.Collector<java.lang.Integer,?,
    java.util.Map<?,java.util.List<java.lang.Integer>>>

第二个抱怨使用Integer键进行通话:

Error:(24, 59) java: incompatible types:     
java.util.stream.Collector<java.lang.Integer,capture#2 of ?,     
    java.util.Map<java.lang.Integer,java.util.List<java.lang.Integer>>>
cannot be converted to 
java.util.stream.Collector<java.lang.Integer,?,
    java.util.Map<?,java.util.List<java.lang.Integer>>>

Collectors.groupingBy的返回类型为<T, K> Collector<T, ?, Map<K, List<T>>>,因此在MagnitudeModulus情况下,这应该是

Collector<Integer, ?, Map<String,List<Integer>>>
Collector<Integer, ?, Map<Integer,List<Integer>>>

分别

为什么这些不匹配print中的收集器参数,

Collector<Integer, ?, Map<?, List<Integer>>> collector

3 个答案:

答案 0 :(得分:1)

使用更简单的示例进行说明,如果您使用方法foo(Number),则可以传入Integer,因为IntegerNumber的子类型。但是,如果您使用方法foo(List<Number>),则无法传递List<Integer>,因为List<Integer>不是List<Number>的子类型。

但如果您的方法foo只想从Number检索List,您可以将签名更改为foo(List<? extends Number>)(另请参阅“What is PECS”) ,允许列表使用Number的子类型进行参数化。 List<Integer>List<? extends Number>

的子类型

更复杂的是,Map<Integer,List<Integer>>Map<String,List<Integer>>都是Map<?,List<Integer>>的子类型,但Collector< … Map<Integer,List<Integer>> >Collector< … Map<String,List<Integer>> > 不是 Collector< … Map<?,List<Integer>> >的子类型。

解决方案是一样的。您希望从Collector 检索地图,因此您必须求助于“? extends …”,即使用Collector<Integer, ?, ? extends Map<?, List<Integer>>>类型:

private static void print(List<Integer> data, 
                      Collector<Integer, ?, ? extends Map<?, List<Integer>>> collector) {
    data.stream().collect(collector)
            .forEach((key, list) -> System.out.println(key + ": " + list));
}

private static void printByMagnitude() {
    List<Integer> data = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    print(data, Collectors.<Integer,String>groupingBy(i -> i < 5 ? "small" : "large"));
}

private static void printByModulus() {
    List<Integer> data = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    print(data, Collectors.<Integer,Integer>groupingBy(i -> i % 2));
}

答案 1 :(得分:0)

只需写一下:

private static <T, U> void print(List<T> data, 
        Collector<T, ?, Map<U, List<T>>> collector) {
    data.stream().collect(collector)
    .forEach((key, list) -> System.out.println(key + ": " + list));
}

private static void printByMagnitude() {
    List<Integer> data = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    print(data, Collectors.<Integer, String>groupingBy(i -> i < 5 ? "small" : "large"));
}

private static void printByModulus() {
    List<Integer> data = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    print(data, Collectors.<Integer, Integer>groupingBy(i -> i % 2));
}

这里的不同之处在于我用通用参数替换了无界通配符类型(&#39; ?&#39;字符)(我使打印方法通用)。最大的区别在于?&#39;无界通配符代表 one type 的某些东西不是 any type 。由于你有两种类型的东西(整数和字符串),编译器抱怨。

另请注意,您并不需要使用参数调用泛型方法(我认为从Java 7开始)。您也可以简单地写一下:

private static <T, U> void print(List<T> data, 
        Collector<T, ?, Map<U, List<T>>> collector) {
    data.stream().collect(collector)
    .forEach((key, list) -> System.out.println(key + ": " + list));
}

private static void printByMagnitude() {
    List<Integer> data = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    print(data, Collectors.groupingBy(i -> i < 5 ? "small" : "large"));
}

private static void printByModulus() {
    List<Integer> data = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    print(data, Collectors.groupingBy(i -> i % 2));
}

答案 2 :(得分:0)

简单地替换

private static <A, B> void print(List<Integer> data, Collector<Integer, ?, Map<A, List<B>>> groupingBy) {
        data.stream().collect(groupingBy).forEach((key, value) -> System.out.println(key + " : " + value));
    }

private static void print(List<Integer> data, 
                          Collector<Integer, ?, Map<?, List<Integer>>> collector) {
    data.stream().collect(collector)
            .forEach((key, list) -> System.out.println(key + ": " + list));
}

<强>为什么吗

由于 ?仅适用于Object,仅适用于其子类

AnyType<?> object=new AnyType<Object> // true
AnyType<?> object=new AnyType<Number> // since Number is subchild of Object but Still false

编辑:

在您的方法printByModulus()中传递Collectors.<Integer, Integer>而在方法printByMagnitude()中传递Collectors.<Integer, String>是导致错误的唯一原因。