Когда я должен использовать потоки?
Я просто наткнулся на вопрос при использовании List
и stream()
метод. Пока я знаю как чтобы использовать их, я не совсем уверен о , когда для их использования.
например, у меня есть список, содержащий различные пути в разных местах. Теперь я хотел бы проверить, содержит ли один заданный путь любой из путей, указанных в списке. Я хотел бы вернуть a boolean
в зависимости от того, было ли выполнено условие.
это конечно, это не сложная задача, как таковой. Но мне интересно, следует ли использовать потоки или цикл for(-each).
Список
private static final List<String> EXCLUDE_PATHS = Arrays.asList(new String[]{
"my/path/one",
"my/path/two"
});
Example-Stream
private boolean isExcluded(String path){
return EXCLUDE_PATHS.stream().map(String::toLowerCase).filter(path::contains).collect(Collectors.toList()).size() > 0;
}
Пример - Для Каждого Цикла
private boolean isExcluded(String path){
for (String excludePath : EXCLUDE_PATHS) {
if(path.contains(excludePath.toLowerCase())){
return true;
}
}
return false;
}
Примечание что
4 ответа:
ваше предположение верно. Реализация потока выполняется медленнее, чем цикл for.
это использование потока должно быть так же быстро, как для цикла, хотя:
EXCLUDE_PATHS.stream().map(String::toLowerCase).anyMatch(path::contains);
это повторяется через элементы, применяя
String::toLowerCase
и фильтр к элементам один за другим и оканчивается на первом пункте, который соответствует.и
collect()
&anyMatch()
- Это операции, терминал.anyMatch()
выходит на первый найденный элемент, хотя, в то время какcollect()
требует все элементы для обработки.
решение о том, использовать потоки или нет, должно определяться не соображениями производительности, а скорее удобочитаемостью. Когда дело действительно доходит до производительности, есть и другие соображения.
С
.filter(path::contains).collect(Collectors.toList()).size() > 0
подход, вы обрабатываете все элементы и собираете их во временноеList
, прежде чем сравнивать размер, все же, это вряд ли когда-либо имеет значение для потока, состоящего из двух элементов.используя
.map(String::toLowerCase).anyMatch(path::contains)
можете сохранить циклов процессора и памяти, если у вас есть существенно большее количество элементов. Тем не менее, это преобразует каждыйString
к его строчному представлению, пока не будет найдено совпадение. Очевидно, что есть смысл в использовании. Поэтому вам не нужно повторять преобразование в lowcase при каждом вызове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
. Если количество элементов вEXCLUDE_PATHS
или длины строк становится действительно большим, вы можете рассмотреть возможность использования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
флаг, заставляет его вести себя так же, как обычные строковые операции, но позволяет движку потратить некоторое время на подготовку, например, используя алгоритм Бойера Мура, чтобы быть более эффективным, когда дело доходит до фактического сравнения.конечно, это окупается только в том случае, если есть достаточно последующих тестов, чтобы компенсировать время, затраченное на подготовку. Определение того, будет ли это так, является одним из фактических соображений производительности, помимо первого вопроса о том, будет ли эта операция всегда будет критическая производительность на всех. Не вопрос, Использовать ли потоки или
for
петли.кстати, приведенные выше примеры кода сохраняют логику вашего исходного кода, что кажется мне сомнительным. Ваш
isExcluded
возвращаетtrue
, если указанный путь содержит любой из элементов в списке, поэтому он возвращаетtrue
на/some/prefix/to/my/path/one
, а такжеmy/path/one/and/some/suffix
или даже/some/prefix/to/my/path/one/and/some/suffix
.даже
dummy/path/onerous
считается удовлетворяющим критериям, как этоcontains
в строкаmy/path/one
...
да. Вы правы. Ваш подход к потоку будет иметь некоторые накладные расходы. Но вы можете использовать такую конструкцию:
private boolean isExcluded(String path) { return EXCLUDE_PATHS.stream().map(String::toLowerCase).anyMatch(path::contains); }
основная причина использования потоков заключается в том, что они делают ваш код более простым и легким для чтения.
цель потоков в Java заключается в упрощении сложности написания параллельного кода. Он вдохновлен функциональным программированием. Последовательный поток-это просто сделать код чище.
Если мы хотим производительности, мы должны использовать parallelStream,который был разработан. Серийный, В общем, медленнее.
есть хорошая статья, чтобы прочитать о
ForLoop
,Stream
иParallelStream
производительность.в коде мы можем использовать методы прекращения чтобы остановить поиск на первом матче. (anyMatch...)