【问题标题】:Any way to make this stream more efficient?有什么方法可以让这个流更有效率吗?
【发布时间】:2020-07-27 08:42:32
【问题描述】:

如何在不向新的 ArrayList 中添加价值而只更新原始列表的情况下优化这一点?

 String filterval = filter.toLowerCase();
 ArrayList<String> filtredArr = new ArrayList<String>();

                        listArray.forEach(
                                valueText -> {
                                        String val = valueText.toLowerCase();
                                    if (val.startsWith(filterval) || val.contains(filterval))
                                        filtredArr.add(valueText);
                                    else {
                                        Arrays.stream(valueText.split(" ")).forEach(
                                                singleWord -> {
                                                    String word = singleWord.toLowerCase();
                                                    if(word.startsWith(filterval) || word.contains(filterval))
                                                        filtredArr.add(valueText);
                                                }
                                        );
                                    }
                                });

【问题讨论】:

  • 看看this。一般来说,在迭代时修改流的源不是一个好主意,因此创建一个新列表会更好。
  • 优化什么?可读性?
  • hmm 而不是使用 foreach 使用其他一些终端操作,如 filter() 或?
  • 如果val.contains(filterval) 为假,val 中是否有一个词为真?
  • @xyz16179 他们指的是如果字符串不包含filterval,则没有子字符串(因此字符串中没有单词)将包含filterval

标签: java arraylist foreach java-stream


【解决方案1】:

在使用流时,最好不要修改流的来源,而后者会迭代前者。 (例如,参见this。)

关于可读性和惯用流操作,您的代码与普通的旧 for 循环几乎没有区别(如果您只使用流来执行 forEach,我建议您将其更改为该循环,但您可以修改它以使用更短且更“原子”的流操作链,如下例所示。

  • 获取至少包含一个词包含的字符串列表filterval

    List<String> filtered = listArray.stream()
                                   .filter(str -> str.toLowerCase().contains(filterval))
                                   .collect(Collectors.toList());
    
  • 获取至少有一个单词开头的字符串列表filterval

    List<String> filtered =
          listArray.stream()
                   .filter(str -> Arrays.stream(str.split(" "))
                                        .map(String::toLowerCase)
                                        .anyMatch(word -> word.startsWith(filterval)))
                   .collect(Collectors.toList());
    
  • 获取包含过滤器值的单词列表(在任何字符串中):

    List<String> filteredWords = listArray.stream()
                                        .map(String::toLowerCase)
                                        .flatMap(str -> Arrays.stream(str.split(" ")))
                                        .filter(word -> word.contains(filterval))
                                        .collect(Collectors.toList());
    

(我假设listArrayList&lt;String&gt;,你没有在你的代码sn-p 中显示。)

注意事项

  • 条件val.startsWith(filterval) || val.contains(filterval) 完全等价于val.contains(filterval)。原因是,如果一个字符串以filterval开头,它也一定意味着它包含它;一个暗示另一个。
  • 无需计算单个单词的小写版本,因为您已经将整个字符串小写(因此其中的任何单词也都是小写的)。
  • 我们可以将split 应用于所有单词,然后使用filterMap“连接”单词序列,而不是单独处理单个单词和空格分隔的字符串。

最小的、完整的、可验证的例子

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;


public class Main {
    public static void main(String[] args) {
        String filterval = "ba";
        List<String> listArray = List.of("foo", "BAR", "spam EGGS ham bAt", "xxbazz");


        List<String> filtered1 = listArray.stream()
                                          .filter(str -> str.toLowerCase().contains(filterval))
                                          .collect(Collectors.toList());

        List<String> filtered2 =
                listArray.stream()
                         .filter(str -> Arrays.stream(str.split(" "))
                                              .map(String::toLowerCase)
                                              .anyMatch(word -> word.startsWith(filterval)))
                         .collect(Collectors.toList());

        List<String> filtered3 = listArray.stream()
                                          .map(String::toLowerCase)
                                          .flatMap(str -> Arrays.stream(str.split(" ")))
                                          .filter(word -> word.contains(filterval))
                                          .collect(Collectors.toList());

        System.out.println(Arrays.toString(filtered1.toArray()));
        System.out.println(Arrays.toString(filtered2.toArray()));
        System.out.println(Arrays.toString(filtered3.toArray()));
    }
}

输出:

[BAR, spam EGGS ham bAt, xxbazz]
[BAR, spam EGGS ham bAt]
[bar, bat, xxbazz]

【讨论】:

  • 这只会输出剥离的值。我需要列表的原始值。将需要整个值,例如。 “垃圾邮件鸡蛋火腿蝙蝠”
  • @xyz16179 我的错。所以:您想要包含至少一个包含filterval 的单词的字符串列表。正确的? (或者这些词是否必须以filterval开头?)
  • 您编写的逻辑工作正常,只是结果格式不正确。您只收集单词,而是想要整个字符串。抱歉刚接触直播...
  • @xyz16179 我在答案中留下了所有三个选项,因此您可以选择最适合的一个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多