什么时候应该使用stream?
当我使用一个List
及其stream()
方法时,我遇到了一个问题。 虽然我知道如何使用它们,但我不清楚何时使用它们。
例如,我有一个列表,其中包含到不同位置的各种path。 现在,我想检查一个给定的path是否包含列表中指定的任何path。 我想根据是否满足条件返回一个boolean
。
这当然不是一个艰巨的任务本身。 但是我想知道我应该使用stream还是for( – 每个)循环。
列表
private static final List<String> EXCLUDE_PATHS = Arrays.asList(new String[]{ "my/path/one", "my/path/two" });
示例 – stream
private boolean isExcluded(String path){ return EXCLUDE_PATHS.stream().map(String::toLowerCase).filter(path::contains).collect(Collectors.toList()).size() > 0; }
示例 – For-Each循环
private boolean isExcluded(String path){ for (String excludePath : EXCLUDE_PATHS) { if(path.contains(excludePath.toLowerCase())){ return true; } } return false; }
请注意 , path
参数总是小写 。
我的第一个猜测是for-each方法更快,因为如果满足条件,循环将立即返回。 尽pipestream仍然会遍历所有列表条目以完成过滤。
我的假设是否正确? 如果是这样, 为什么 (或者说何时 )我会使用stream()
呢?
你的假设是正确的。 你的stream实现比for循环慢。
这个stream的使用应该和for循环一样快:
EXCLUDE_PATHS.stream().map(String::toLowerCase).anyMatch(path::contains);
这遍历项目,将String::toLowerCase
和筛选器逐一应用于项目,并终止于匹配的第一个项目 。
collect()
和anyMatch()
都是terminal操作。 anyMatch()
退出第一个find的项目,虽然collect()
需要处理所有项目。
是否使用Streams的决定不应该由性能考虑驱动,而应该由可读性驱动。 当它真的涉及到性能时,还有其他的考虑。
使用.filter(path::contains).collect(Collectors.toList()).size() > 0
方法,您将处理所有元素并将它们收集到临时List
,然后比较大小,对于由两个元素组成的stream而言是重要的。
使用.map(String::toLowerCase).anyMatch(path::contains)
可以节省CPU周期和内存,如果你有大量的元素。 不过,这将每个String
转换为小写forms,直到find匹配。 很明显,使用中有一点
private static final List<String> EXCLUDE_PATHS = Stream.of("my/path/one", "my/path/two").map(String::toLowerCase) .collect(Collectors.toList()); private boolean isExcluded(String path) { return EXCLUDE_PATHS.stream().anyMatch(path::contains); }
代替。 因此,您不必在每次调用isExcluded
将转换重复为isExcluded
。 如果EXCLUDE_PATHS
中元素的数量或者string的长度变得很大,可以考虑使用
private static final List<Predicate<String>> EXCLUDE_PATHS = Stream.of("my/path/one", "my/path/two").map(String::toLowerCase) .map(s -> Pattern.compile(s, Pattern.LITERAL).asPredicate()) .collect(Collectors.toList()); private boolean isExcluded(String path){ return EXCLUDE_PATHS.stream().anyMatch(p -> p.test(path)); }
使用LITERAL
标志将string编译为正则expression式,使其像普通的string操作一样工作,但是允许引擎花费一些时间准备,例如使用Boyer Moorealgorithm,以便在实际比较时更高效。
当然,如果有足够的后续testing来弥补准备时间,这只会带来好处。 确定这是否是这种情况,是除了第一个问题,这个操作是否会对性能至关重要的实际性能考虑因素之一。 不是使用Streams还是for
循环的问题。
顺便说一下,上面的代码示例保留了您的原始代码的逻辑,这看起来是值得怀疑的。 如果指定的path包含列表中的任何元素,则isExcluded
方法将返回true
,因此它会为/some/prefix/to/my/path/one
以及my/path/one/and/some/suffix
返回true
甚至/some/prefix/to/my/path/one/and/some/suffix
。
甚至dummy/path/onerous
被认为是符合标准,因为它contains
stringmy/path/one
…
是啊。 你是对的。 你的stream方法会有一些开销。 但是你可以使用这样的结构:
private boolean isExcluded(String path) { return EXCLUDE_PATHS.stream().map(String::toLowerCase).anyMatch(path::contains); }
使用stream的主要原因是它们让你的代码更简单易读。
Java中的stream的目标是简化编写并行代码的复杂性。 它受函数式编程的启发。 串行stream只是为了使代码更清洁。
如果我们想要性能,我们应该使用parallelStream,这是专为。 一般而言,串行的速度较慢。
有一篇关于ForLoop
, Stream
和ParallelStream
性能的好文章。
在你的代码中,我们可以使用终止方法来停止第一场比赛的search。 (anyMatch …)