将流收集回到相同的集合类型中

Leo*_*nid 5 java java-8 java-stream

假设我有一个未知类型的集合.我想要做的是流式传输,在流上做一些事情,并将其收回到与我的原始集合相同的集合类型.例如:

Collection<? extends Integer> getBigger(Collection<? extends Integer> col, int value) {
    return col.stream().filter(v -> v > value).collect(????);
} 
Run Code Online (Sandbox Code Playgroud)

这个不完整的代码示例的想法是返回一个Listif colList类(或它的任何子类),一个Setif colSet类等等...这里的流的方法名称和实际操作并不重要,我是指定它们只是为了说明我的问题.那么,有可能吗?

Hon*_*dek 16

如果不违反构建Java流框架的原则,这是不可能的.它完全违反了从物理表示中抽取流的想法.

批量数据操作的顺序在管道中,请参见下图: 管道:一系列批量数据操作

这条小流在某种程度上类似于薛定谔的猫 - 在你调用终端操作之前它没有实现.流处理完全是抽象的,并与原始流源分离.

管道作为黑匣子

如果您希望在原始数据存储中使用如此低级别的工作,请不要因为简单地避开流而感到羞耻.它们只是一种工具,而不是任何神圣的东西.通过引入流,Good Old Collections仍然像它们一样好,具有内部迭代的附加值 - 新的Iterable.forEach()方法.


添加以满足您的好奇心:)

可能的解决方案如下.我自己并不喜欢它,而且我无法解决那里的所有泛型问题,但它有局限性.

这个想法是创建一个收集器返回与输入集合相同的类型.但是,并非所有集合都提供了一个无效的构造函数(没有参数),没有它,Class.newInstance()方法就不起作用.还有lambda表达式中检查异常的尴尬问题.(在这个很好的答案中提到:https://stackoverflow.com/a/22919112/2886891)

public Collection<Integer> getBiggerThan(Collection<Integer> col, int value) {
    // Collection below is an example of one of the rare appropriate 
    // uses of raw types. getClass returns the runtime type of col, and 
    // at runtime all type parameters have been erased.
    @SuppressWarnings("rawtypes")
    final Class<? extends Collection> clazz = col.getClass();
    System.out.println("Input collection type: " + clazz);
    final Supplier<Collection<Integer>> supplier = () -> {
        try {
            return clazz.newInstance();
        }
        catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(
                    "A checked exception caught inside lambda", e);
        }
    };
    // After all the ugly preparatory code, enjoy the clean pipeline:
    return col.stream()
            .filter(v -> v > value)
            .collect(supplier, Collection::add, Collection::addAll);
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,它通常起作用,假设您的原始集合提供了一个无效的构造函数.

public void test() {
    final Collection<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

    final Collection<Integer> arrayList = new ArrayList<>(numbers);
    final Collection<Integer> arrayList2 = getBiggerThan(arrayList, 6);
    System.out.println(arrayList2);
    System.out.println(arrayList2.getClass());
    System.out.println();

    final Collection<Integer> set = new HashSet<>(arrayList);
    final Collection<Integer> set2 = getBiggerThan(set, 6);
    System.out.println(set2);
    System.out.println(set2.getClass());
    System.out.println();

    // This does not work as Arrays.asList() is of a type
    // java.util.Arrays$ArrayList which does not provide a nullary constructor
    final Collection<Integer> numbers2 = getBiggerThan(numbers, 6);
}
Run Code Online (Sandbox Code Playgroud)