如何在Java 8流API中延迟初始化Collectors.toList()?

时间:2018-08-06 10:20:09

标签: java java-stream

我想根据过滤器收集物品。但是,如果找不到匹配项,则不应初始化结果列表。我希望使用null而不是空列表。

flow

问题:大多数情况下,我会找到匹配项,从而导致集合为空。这意味着在大多数情况下,即使我不需要该列表,它也会被实例化。

2 个答案:

答案 0 :(得分:5)

Collecto.toList()使用List分配ArrayList::new,这是一个非常便宜的操作,因为ArrayList直到元素​​才实际分配支持数组被插入。构造函数所做的全部工作就是将内部Object[]字段初始化为静态创建的空数组的值。仅当插入第一个元素时,实际的后备数组才会初始化为其“初始大小”。

那为什么要避免这种结构呢?听起来像是过早的优化。

如果您非常担心GC压力,那就不要使用Streams。流和收集器本身创建起来可能比列表要“昂贵得多”。

答案 1 :(得分:0)

我只想到一种情况,其中Collectors.toList()之外的其他东西计算起来会很昂贵,否则请使用:

... collect(Collectors.collectingAndThen(list -> {
          list.isEmpty() ? null: list;
   }))

但是请记住,使用List的人很可能会在缺少元素的情况下期望为空,而不是null。

创建一个空的ArrayList相当便宜,懒惰只会使情况变得更糟。

否则,这是一个变体,如果您真的想要:

 private static <T> List<T> list(Stream<T> stream) {
    Spliterator<T> sp = stream.spliterator();
    if (sp.getExactSizeIfKnown() == 0) {
        System.out.println("Exact zero known");
        return null;
    }

    T[] first = (T[]) new Object[1];
    boolean b = sp.tryAdvance(x -> first[0] = x);
    if (b) {
        List<T> list = new ArrayList<>();
        list.add(first[0]);
        sp.forEachRemaining(list::add);
        return list;
    }

    return null;
}