是否可以用基于流的构造替换Java中的所有循环结构?

时间:2018-04-04 11:15:36

标签: java java-8 java-stream

我正在探索Java Stream API的可能性,以便找出是否可以用基于流的构造替换所有基于循环的构造。 作为一个可能暗示这实际上是可能的假设的例子,请考虑这个:

是否可以使用Stream API将包含由空格分隔的单词的字符串拆分为字符串数组,如下面的String.split(String)调用那样?

String[] tokens = "Peter Paul Mary".split(" ");

我知道基于流的解决方案可以使用String.split(String)方法,如下所示:

Stream.of("Peter Paul Mary".split(" "))
      .collect(Collectors.toList());

或使用Pattern.splitAsStream(CharSequence)(其实现肯定使用基于循环的方法)但我正在寻找一个仅限流的解决方案,这意味着与Haskell片段类似:

words   :: String -> [String]
words s =  case dropWhile Char.isSpace s of
                  "" -> []
                  s' -> w : words s''
                        where (w, s'') = break Char.isSpace s'

我问这个是因为我仍然想知道Stream API的引入是否会导致我们在Java中处理对象集合的方式发生深刻变化,或者只是为其添加另一个选项,从而使维护更长的代码库,而不是从长远来看简化此任务。

编辑:虽然有一个公认的答案(见下文),但它只表明在这种特殊情况下它是可能的。我仍然对问题中所要求的一般案例的任何提示感兴趣。

2 个答案:

答案 0 :(得分:7)

这里有一个独特的答案:你问的是错误的问题!

所有“循环相关”的Java代码行都可以转换为 streamish

因为:良好的编程能够编写人类可以轻松阅读和理解的代码。

因此,如果有人提出一条规则,即“我们只使用来完成我们所做的一切”,那么该规则会在编写代码时显着减少您的选项。而不是能够仔细地决定“我应该使用流”还是“我应该选择一个简单的老式循环结构”,而是“我如何使用流来处理这个”!

从这个角度来看,您应该专注于提出适用于开发团队中所有人的“规则”。这可能意味着 empasize 使用流构造。但是你绝对想要避免绝对主义,并让每个开发人员以“最可读”的方式编写实现给定需求的代码。如果可以使用溪流,那很好。如果没有,请不要强迫人们这样做。

除此之外:根据您的具体操作,使用还会带来性能成本。因此,即使您可以找到问题的流解决方案 - 您也必须了解其运行时成本。您肯定希望避免在其成本太多的地方使用流(特别是当该代码已经在关于性能的“关键路径”上时,例如:每秒调用数十亿次)。

最后:在某种程度上,这是技能的问题。含义:当您训练使用流时,您可以更轻松地读取其他人编写的“流线型”代码和B)提出“流线型”解决方案,这些解决方案实际上很容易读。换句话说:这又取决于您正在使用的上下文。另一周,我正在教另一个团队“清洁代码”,我的最后一个代码是“Clean Code,Java8 streams / lambdas”。一个人问我“什么是溪流?”没有必要强迫这样的社区明天用流做任何

答案 1 :(得分:3)

只是为了好玩(这是一种可怕的方式),我也不知道这是否符合您的需求:

List<String> result = ",,,abc,def".codePoints()
            .boxed()
            // .parallel()
            .collect(Collector.of(
                    () -> {
                        List<StringBuilder> inner = new ArrayList<>();
                        inner.add(new StringBuilder());
                        return inner;
                    },
                    (List<StringBuilder> list, Integer character) -> {
                        StringBuilder last = list.get(list.size() - 1);
                        if (character == ',') {
                            list.add(new StringBuilder());
                        } else {
                            last.appendCodePoint(character);
                        }
                    },
                    (left, right) -> {
                        left.get(left.size() - 1).append(right.remove(0));
                        left.addAll(right);
                        return left;
                    },
                    list -> list.stream()
                            .map(StringBuilder::toString)
                            .filter(x -> !x.equals(""))
                            .collect(Collectors.toList())

    ));