使用收集器根据 Java 中的 class 将 Steam 拆分为列表

Using Collectors to split steam into lists based on class in Java

以下代码是伪代码,表示我要实现的目标:

Map<Class<?>, List<?>> map = Stream.of(1, "2").collect(Collectors.groupingBy(Object::getClass));
List<Integer> ints = map.get(Integer.class);
List<String> strings = map.get(String.class);

以上是简化的代码。我想要的是我有一个输入列表,我想根据对象的 class 将它分成多个列表。

但是上面的代码无法编译,请问您如何修复它?

您可以使用 List<Object> 而不是 List<?>:

Map<Class<?>, List<Object>> map = Stream.of(1, "2")
    .collect(Collectors.groupingBy(Object::getClass));
List<Object> ints = map.get(Integer.class);
List<Object> strings = map.get(String.class);

可以通过将 List<?> 更改为 List<Object> 来修复第一行:

Map<Class<?>, List<Object>> map = Stream.of(1, "2")
        .collect(Collectors.groupingBy(Object::getClass));

接下来的几行问题更多。我不认为有任何方法可以在 Java 中定义那种通用关系。你将不得不使用丑陋的、不安全的转换:

List<Integer> ints = (List)map.get(Integer.class);
List<String> strings = (List)map.get(String.class);

如果你想让它更安全和用户友好,你可以把它隐藏在一个自定义的后面class:

class TypeToListMap {
    private Map<Class<?>, List<Object>> map = ...

    public <T> List<T> get(Class<T> key) {
        @SuppressWarnings("unchecked")
        List<T> result = (List<T>)map.get(key); 
        return result;
    }
}

好的类型:

Stream.of(1, "2");

实际上是:

Stream<?> stream = Stream.of(1, "2");

在这种情况下,? 的唯一合法替代是 Object

因为类型擦除 ? 将是对象。

这就像你需要在运行时用泛型声明你的列表的类型。就像这样做:

T x;
List<x.getClass()> list = ...; // can't do that

你可以在这里做一个 hack 并施放两次 :)

 @SuppressWarnings("unchecked")
    List<Integer> ints = (List<Integer>) (List<?>) map.get(Integer.class);

其实你可以直接投结果。列表实际上应该由所需类型的对象组成,所以应该没有问题。不过,您可能会收到编译器警告(如果您向编译器指定要接收未经检查的转换警告)。

    Map<Class, ?> map = Stream.of(1, "2").collect(Collectors.groupingBy(Object::getClass));
    List<Integer> ints = (List<Integer>) map.get(Integer.class);
    List<String> strings = (List<String>) map.get(String.class);