为什么Stream没有toList()方法?

Jir*_*ser 34 java java-8 java-stream

使用Java 8流时,采用列表,从中创建流,执行业务并将其转换回来是很常见的.就像是:

 Stream.of(-2,1,2,-5)
        .filter(n -> n > 0)
        .map(n -> n * n)
        .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

为什么' .collect(Collectors.toList())'部分没有捷径/方便的方法?在Stream接口上,有将方法转换为数组的方法toArray(),为什么toList()缺少?

恕我直言,将结果转换为列表比数组更常见.我可以忍受这一点,但称这种丑陋是令人讨厌的.

有任何想法吗?

Zhe*_*lov 13

Java 16 引入了Stream.toList()

Stream.of(-2,1,2,-5)
    .filter(n -> n > 0)
    .map(n -> n * n)
    .toList();
Run Code Online (Sandbox Code Playgroud)

新方法与现有方法略有不同collect(toList()):它返回一个不可修改的列表。


Tag*_*eev 12

最近我写了一个名为StreamEx的小型库,它扩展了Java流,并在许多其他功能中提供了这种精确的方法:

StreamEx.of(-2,1,2,-5)
    .filter(n -> n > 0)
    .map(n -> n * n)
    .toList();
Run Code Online (Sandbox Code Playgroud)

此外,toSet(),toCollection(Supplier),join(),groupingBy()和其他快捷方法也可用.


Fed*_*ner 5

至于"为什么",我相信评论中有很多争论.但是,我同意你的意见,因为没有toList()方法会很烦人.同样的情况与toIterable()方法.

所以我会向你展示一个技巧,让你无论如何都可以使用这两种方法.幸运的是,Java非常灵活,可以让你做各种有趣的事情.大约10年前,我读到了这篇文章,它描述了一种将方法"插入"到任何给定接口的诙谐技巧.诀窍在于使用代理来调整没有所需方法的接口.多年来,我发现它具有所有适配器模式的优点,而它缺乏所有的缺点.这就是我称之为重要的事情.

这是一个示例代码,只是为了表明这个想法:

public class Streams {

    public interface EnhancedStream<T>
        extends Stream<T> {

        List<T> toList();

        Iterable<T> toIterable();
    }

    @SuppressWarnings("unchecked")
    public static <T> EnhancedStream<T> enhance(Stream<T> stream) {

        return (EnhancedStream<T>) Proxy.newProxyInstance(
            EnhancedStream.class.getClassLoader(),
            new Class<?>[] {EnhancedStream.class}, 
            (proxy, method, args) -> {

            if ("toList".equals(method.getName())) {

                return stream.collect(Collectors.toList());

            } else if ("toIterable".equals(method.getName())) {

                return (Iterable<T>) stream::iterator;

            } else {
                // invoke method on the actual stream
                return method.invoke(stream, args);
            }
        });
    }

    public static void main(String[] args) {

        Stream<Integer> stream1 = Stream.of(-2, 1, 2, -5).
            filter(n -> n > 0).map(n -> n * n);
        List<Integer> list = Streams.enhance(stream1).toList();
        System.out.println(list); // [1, 4]

        Stream<Integer> stream2 = Stream.of(-2, 1, 2, -5).
            filter(n -> n > 0).map(n -> n * n);
        Iterable<Integer> iterable = Streams.enhance(stream2).toIterable();
        iterable.forEach(System.out::println); // 1
                                               // 4
    }
}
Run Code Online (Sandbox Code Playgroud)

我们的想法是通过定义要添加的方法来使用EnhancedStream扩展Java Stream接口的接口.然后,动态代理通过将原始Stream方法委托给正在调整的实际流来实现此扩展接口,而它只是为新方法(未定义的方法)提供内联实现Stream.

此代理可通过静态方法获得,该方法透明地执行所有代理内容.

请注意,我并未声明这是最终解决方案.相反,它只是一个可以高度改进的示例,即对于每个Stream返回另一个方法的方法Stream,您也可以返回该代理.这将允许EnhancedStreams被链接(您需要在EnhancedStream接口中重新定义这些方法,以便它们返回EnhancedStream协变返回类型).此外,缺少适当的异常处理,以及更强大的代码来决定是否将方法的执行委托给原始流.