英文:
Any way to make this stream more efficient?
问题
如何在不向新的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);
}
);
}
});
英文:
How would one optimise this without adding value into the new ArrayList instead just having the original list updated?
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);
}
);
}
});
答案1
得分: 0
使用流(Streams)时,最好不要在流迭代过程中修改流的源。有关可参考此链接。
关于代码的可读性和对流操作的惯用使用,你的代码几乎和普通的 for 循环没有区别(如果你只是用流来执行 forEach
操作,我建议你将其改为普通的 for 循环)。但是你可以将其修改为使用一系列更短、更“原子”的流操作,如以下示例所示。
-
获取至少包含一个单词包含
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());
(我假设 listArray
,在你的代码片段中没有展示,是一个 List<String>
。)
注释
- 条件
val.startsWith(filterval) || val.contains(filterval)
与val.contains(filterval)
完全等价。原因是,如果一个字符串以filterval
开头,那么它一定也包含它;一个蕴含着另一个。 - 无需计算单词的小写版本,因为你已经将整个字符串转换为小写(因此其中的任何单词也都是小写的)。
- 我们可以将
split
应用于所有单词和以空格分隔的字符串,然后使用flatMap
来“连接”单词序列,从而不再需要分别处理单个单词和空格分隔的字符串。
最小、完整、可验证示例
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]
英文:
When using streams, it is best not to modify the source of the stream while the latter iterates over the former. (See this for instance.)
Regarding readability and making idiomatic use of stream operations, your code is almost indistinguishable from a plain old for loop (and I would advise you to change it to that if you only use streams to do a forEach
, but you could modify it to use a chain of shorter and more "atomic" stream operations, as in the following examples.
-
To get the list of strings in which at least one word contains
filterval
:List<String> filtered = listArray.stream() .filter(str -> str.toLowerCase().contains(filterval)) .collect(Collectors.toList());
-
To get the list of strings in which at least one word starts with
filterval
:List<String> filtered = listArray.stream() .filter(str -> Arrays.stream(str.split(" ")) .map(String::toLowerCase) .anyMatch(word -> word.startsWith(filterval))) .collect(Collectors.toList());
-
To get the list of words (in any of the strings) that contain the filter value:
List<String> filteredWords = listArray.stream() .map(String::toLowerCase) .flatMap(str -> Arrays.stream(str.split(" "))) .filter(word -> word.contains(filterval)) .collect(Collectors.toList());
(I'm assuming listArray
, which you don't show in your code snippet, is a List<String>
.)
Notes
- The condition
val.startsWith(filterval) || val.contains(filterval)
is completely equivalent toval.contains(filterval)
. The reason is, if a string starts withfilterval
, it must also mean that it contains it; one implies the other. - There's no need to compute the lowercase versions of single words since you've already lowercased the whole string (so any words within it will also be lowercase).
- Instead of treating single words and space-separated strings separately, we can apply
split
to all of them and then "concatenate" the sequences of words by usingfilterMap
.
Minimal, complete, verifiable example
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()));
}
}
Output:
[BAR, spam EGGS ham bAt, xxbazz]
[BAR, spam EGGS ham bAt]
[bar, bat, xxbazz]
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论