【问题标题】:Collecting stream back into the same collection type将流收集回相同的收集类型
【发布时间】:2014-04-08 15:59:08
【问题描述】:

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

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

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

【问题讨论】:

  • 你想在直播中做什么?这是否意味着您必须具有特定的流格式?

标签: java java-8 java-stream


【解决方案1】:

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

批量数据操作的顺序进入一个管道,见下图:

流在某种程度上类似于Schrödinger's cat - 在您调用终端操作之前它不会实现。流处理是完全抽象的,与原始流源分离。

如果您想对原始数据存储进行如此低级别的工作,请不要因为简单地避开流而感到羞耻。它们只是一种工具,而不是任何神圣的东西。通过引入流,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);
}

如您所见,它通常可以工作,假设您的原始集合提供了一个空构造函数。

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

【讨论】:

  • Javadoc for getClass: "实际结果类型是Class&lt;? extends |X|&gt;},其中|X| 是调用getClass 的表达式的静态类型的擦除。我>”。听起来你必须忍受这个警告。
  • 这种行为很有意义:getClass 返回运行时类,并且在运行时所有类型参数都已被删除。我认为我们已经发现了原始类型的一种罕见的适当用途!
【解决方案2】:

这里有两个问题:(1)输入及其结果的运行时类型(类),以及(2)输入及其结果的编译时类型。

对于 (1),它可能看起来很奇怪,但通常在 Java 中不可能创建任意类的实例的副本。如果类没有可访问的无参数构造函数或者它是不可变的,则使用 getClass().newInstance() 可能不起作用。该对象也可能不是Cloneable。因此,调用者需要传入负责创建正确结果类实例的供应商。

对于 (2),适当剂量的泛型可以在编译时使这种类型安全。

<T extends Comparable<T>, C extends Collection<T>> C getBigger(
        C col, T value, Supplier<C> supplier) {
    return col.stream()
              .filter(v -> v.compareTo(value) > 0)
              .collect(Collectors.toCollection(supplier::get));
}

请注意,类型参数T 上有一个Comparable&lt;T&gt; 的界限,因此调用者只能传递可比较的东西的集合。这让我们可以使用compareTo 来比较这些值。我们还使用了Collectors.toCollection 方法并将供应商的get 方法传递给它。

使用示例:

List<Integer> input1 = Arrays.asList(1, 4, 9, 13, 14, 22);
List<Integer> filtered1 = getBigger(input1, 10, ArrayList::new);

Set<String> input2 = new HashSet<>();
input2.add("foo");
input2.add("bar");
input2.add("baz");
input2.add("qux");
Set<String> filtered2 = getBigger(input2, "c", HashSet::new);

【讨论】:

    【解决方案3】:

    由于实际的底层类型只知道是您的方法的被调用者,因此他们应该负责将 collect 它传递给他们想要的任何类型的 Collection(例如使用 Collectors.toCollection(CustomCollectionType::new);)。所以你的方法应该返回Stream。根据方便,它可以采用CollectionStream

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-07-22
      • 1970-01-01
      • 2018-01-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-14
      • 1970-01-01
      相关资源
      最近更新 更多