Я пытаюсь выполнить ленивую оценку с помощью поставщика в таком потоке
public static void main(String[] args) {
Supplier<List<String>> expensiveListSupplier= getExpensiveList();
getList().stream()
.filter(s -> expensiveListSupplier.get().contains(s))
.forEach(System.out::println);
}
private static Supplier<List<String>> getExpensiveList() {
return () -> Stream
.of("1", "2", "3")
.peek(System.out::println)
.collect(Collectors.toList());
}
private static List<String> getList() {
return Stream.of("2", "3")
.collect(Collectors.toList());
}
Но это вызовет метод getExrableList () для каждого элемента в списке. Я стараюсь не быть многословным и не хочу писать что-то подобное, т.е. добавлять непустые чеки и прочее.
public static void main(String[] args) {
Supplier<List<String>> expensiveListSupplier = getExpensiveList();
List<String> list = getList();
if (!list.isEmpty()) {
List<String> expensiveList = expensiveListSupplier.get();
list.stream()
.filter(expensiveList::contains)
.forEach(System.out::println);
}
}
private static Supplier<List<String>> getExpensiveList() {
return () -> Stream
.of("1", "2", "3")
.peek(System.out::println)
.collect(Collectors.toList());
}
private static List<String> getList() {
return Stream.of("2", "3")
.collect(Collectors.toList());
}
Можешь просто вызвать в Set<String> expensiveStuff = new HashSet<>(getExpensiveList().get());. Использование набора позволит масштабировать его до любого практического размера с постоянным временем выполнения.
Небольшое примечание: экземпляр поставщика после того, как он был использован, предотвращает его сборку мусора и для параллельного выполнения потоков, вы можете проверить эту ссылку logicbig.com/tutorials/core-java-tutorial/java-util-stream/… Как указал @Hoopje, лучше использовать ваш подробный код.
Какой смысл здесь использовать Supplier? Вам нужно уже получить список для первого элемента, поэтому лень крошечная по сравнению с усложнением вашего кода.




Я не думаю, что это возможно с использованием только стандартных классов Java. Но вы можете написать свой собственный ленивый оценщик и использовать его, например (непроверенный):
public class LazyValue<T> implements Supplier<T> {
private T value;
private final Supplier<T> initializer;
public LazyValue(Supplier<T> initializer) {
this.initializer = initializer;
}
public T get() {
if (value == null) {
value = initializer.get();
}
return value;
}
}
Есть и другие возможности, см., Например, этот вопрос.
Но будьте осторожны! Если вы добавите ленивую оценку, у вас будет изменяемая структура данных, поэтому, если вы используете ее в многопоточной среде (или параллельном потоке), добавьте синхронизацию.
Но я бы использовал вашу подробную версию. Сразу понятно, что он делает, и он всего на четыре строчки длиннее. (В реальном коде я ожидаю, что эти четыре строки не имеют значения.)
Я так и думал!!. Просто хотел проверить, придумал ли кто-нибудь решение, которое является кратким и функциональным.
Второй вариант можно упростить до
List<String> list = getList();
if (!list.isEmpty()) {
list.stream()
.filter(getExpensiveList().get()::contains)
.forEach(System.out::println);
}
Это делает использование Supplier бессмысленным, поскольку даже вызов getExpensiveList() будет выполняться только тогда, когда список не пуст. Но с другой стороны, это максимальная лень, которую вы можете получить в любом случае, то есть не запрашивать дорогостоящий список, когда другой список пуст. В любом случае дорогостоящий список будет запрошен для первого элемента уже, когда список не пуст.
Если список дорогих может стать большим, вам следует использовать
List<String> list = getList();
if (!list.isEmpty()) {
list.stream()
.filter(new HashSet<>(getExpensiveList().get())::contains)
.forEach(System.out::println);
}
вместо этого, чтобы избежать повторного линейного поиска. Или переделайте getExpensiveList(), чтобы в первую очередь вернуть Set.
Причина добавления второго фрагмента кода заключалась в том, чтобы объяснить мой вопрос (первый фрагмент), а не для улучшения его. Я думаю, что ваши ответы не добавляют ценности моему заданному вопросу. В любом случае спасибо за ответ.
Разве это не вызывает getExpensiveList().get() для каждого элемента потока?
@Amardeep Я решил предоставить улучшенный вариант вашего второго фрагмента кода, потому что он выглядит более сложен в вашем вопросе, но на самом деле проще, чем ваш первый вариант. Как уже объяснялось в моем ответе, ваш первый вариант не дает никакой реальной пользы, т.е. касательно желаемой лени. Так что есть вариант попроще, но без недостатков по сравнению с другими. Если это не ответ, возможно, вы задаете неправильный вопрос.
@FedericoPeraltaSchaffner нет, ссылка на метод формы expression::name будет оценивать выражение один раз и фиксировать результат. См. Также Какое эквивалентное лямбда-выражение для System.out :: println…
@FedericoPeraltaSchaffner Да, как я уже отмечал в своем вопросе, будет. И в довершение всего это послужило поводом для постановки этого вопроса.
@Amardeep нет, не будет. Это фундаментальное различие между ссылкой на метод в форме getExpensiveList().get())::contains и лямбда-выражением, например s -> expensiveListSupplier.get().contains(s).
Требуется ли условие !list.isEmpty()?
@ user7 не для потока, а чтобы избежать вызова getExpensiveList() и getExpensiveList().get(). Ссылка на метод в форме expression::name, как и getExpensiveList().get()::contains, немедленно оценит expression и зафиксирует результат, поэтому он выполняется не для каждого элемента потока, а только один раз. Но это также подразумевает, что здесь он будет вычислен ровно один раз, даже если список пуст - если только мы не обернем весь оператор if (!list.isEmpty()). Конечно, если выражение дешевое, как в случае с System.out::println, нам не о чем беспокоиться.
Понятно. Спасибо за объяснение :)
в чем конкретно ваш вопрос? Можно упростить .. Я стараюсь не быть многословным и не хочу писать что-то подобное, т.е. добавлять непустые чеки и прочее.?