将流收集回到相同的集合类型中 [英] Collecting stream back into the same collection type

查看:80
本文介绍了将流收集回到相同的集合类型中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  Collection<?扩展整数> getBigger(Collection<?extends Integer> col,int value){
return col.stream()。filter(v - > v> value).collect(????);
}

这个不完整的代码示例的想法是返回 List 如果 col List class(或者它的任何子类), 设置如果 col 设置类等。这里的流的方法名称和实际操作并不重要,我已经指定它们只是为了说明我的问题。
那么,有可能吗?

解决方案

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



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



该流类似于方法。






为满足您的好奇心而添加:)



随后是可能的解决方案。我自己并不喜欢它,而且我无法解决那里的所有泛型问题,但它有效有限制



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

  public Collection< Integer> getBiggerThan(Collection< Integer> col,int value){
//下面的集合是原始类型罕见的适当
//使用之一的示例。 getClass返回col的运行时类型,并且
//在运行时所有类型参数都已被删除。
@SuppressWarnings(rawtypes)
final Class<? extends Collection> clazz = col.getClass();
System.out.println(输入集合类型:+ clazz);
final Supplier< Collection< Integer>> supplier =() - > {
try {
return clazz.newInstance();
}
catch(InstantiationException | IllegalAccessException e){
throw new RuntimeException(
lambda中捕获的已检查异常,e);
}
};
//在所有丑陋的预备代码之后,享受干净的管道:
return col.stream()
.filter(v - > v> value)
.collect (supplier,Collection :: add,Collection :: addAll);
}

正如你所看到的,它一般都有用,假设您的原始系列提供了nullary构造函数。

  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<>(数字);
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();

//这不起作用,因为Arrays.asList()的类型为
// java.util.Arrays $ ArrayList,它不提供一个无效的构造函数
final Collection< ;整数> numbers2 = getBiggerThan(数字,6);
}


Suppose I have a collection of the unknown type. What I want to do is stream it, do some stuff on the stream, and collect it back into the same collection type as my original collection. For instance:

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

The idea of this incomplete code example is to return a List if col is of List class (or any subclass of it), a Set if col is of Set class, etc... The method name and actual operations on the stream here are not important, I've specified them just to illustrate my question. So, is it possible?

解决方案

It is not possible without violating the principle on which the Java streams framework has been built on. It would completely violate the idea of abstracting the stream from its physical representation.

The sequence of bulk data operations goes in a pipeline, see the following picture:

The stream is somehow similar to the Schrödinger's cat - it is not materialized until you call the terminal operation. The stream handling is completely abstract and detached from the original stream source.

If you want to work so low-level with your original data storage, don't feel ashamed simply avoiding the streams. They are just a tool, not anything sacred. By introducing streams, the Good Old Collections are still as good as they were, with added value of the internal iteration - the new Iterable.forEach() method.


Added to satisfy your curiosity :)

A possible solution follows. I don't like it myself, and I have not been able to solve all the generics issues there, but it works with limitations.

The idea is creating a collector returning the same type as the input collection. However, not all the collections provide a nullary constructor (with no parameters), and without it the Class.newInstance() method does not work. There is also the problem of the awkwardness of checked exceptions within lambda expression. (It is mentioned in this nice answer here: 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);
}

As you can see, it works in general, supposed your original collection provides a nullary constructor.

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);
}

这篇关于将流收集回到相同的集合类型中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆