Предикаты Java 8, использующие тела методов, вызываются только один раз?

Я изучил следующий фрагмент:

public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
    Map<Object, Boolean> computed = new ConcurrentHashMap<>();/*IS THIS LINE CALLED ONCE ON STREAM->FILTER NOT MATTER HOW LONG THE STREAM IS*/
    return t -> {return computed.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;};
}

private void test(){
    final long d = Stream.of("JOHN","STEPHEN","ORTIZ","RONDON")
            .filter(distinctByKey(Function.identity()))
            .count();
    System.out.println("d = " + d);
}

Этот код не мой. Я знаю, что использование ConcurrentMap не является правильным выбором в этом примере, и я должен использовать ConcurrentMap вместо Map в этом случае, но сейчас это не моя забота.

Я думал, что метод distinctByKey вызывается или интерпретируется в каждой итерации Stream. Я имею в виду, что Map создается на каждом ходу, но это не так!

Тело метода Predicate вызывается только один раз?

В итерации Stream это утверждение?

Потому что, когда я пробую следующий код:

final Function<String,Integer>a = (name)->name.length();
System.out.println(distinctByKey(a).test("JOHN"));
System.out.println(distinctByKey(a).test("STEPHEN"));
System.out.println(distinctByKey(a).test("ORTIZ"));
System.out.println(distinctByKey(a).test("RONDON"));

Я вижу, что тело метода действительно вызывается в каждой строке. Что заставляет тело фильтра вызываться только один раз?

Я думаю, вы взяли свой код из здесь, и при чтении его другого связанный ответ он, вероятно, должен проясниться. distinctByKey() называется только однажды. Но возвращаемый им предикат вызывается для элемента каждый.

Lino 27.05.2019 15:05

эй, приятель, я нашел этот код в проекте, над которым я работаю, возможно, он был скопирован тогда

chiperortiz 27.05.2019 15:07

И честно: рассмотрите возможность переписать свой вопрос вокруг темы минимальный воспроизводимый пример. Типа: какой смысл в том, чтобы метод test() печатал что-то ... пошел, что распечатка нигде не отображается. И что еще хуже: наличие этого метода с именем test ... что немного сбивает с толку, учитывая, что вы также вызываете предикатные test() методы).

GhostCat 27.05.2019 15:08

Что вы имеете в виду, когда говорите: «Я знаю, что использование ConcurrentMap — неправильный выбор в этом примере, и я должен использовать ConcurrentMap …»? Это не имеет никакого смысла. Кроме того, t -> {return computed.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;} излишне сложен. Можно просто написать t -> computed.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null.

Holger 28.05.2019 09:25
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
3
4
579
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

distinctByKey возвращает экземпляр ОдинPredicate, который кэширует ConcurrentHashMap. Вы можете добиться почти того же самого, если замените создание Predicate через лямбду, например, анонимным внутренним классом.

Ответ принят как подходящий

Когда вы вызываете .filter(distinctByKey(Function.identity())), аргумент, переданный filter(), оценивается. Это единственный раз, когда distinctByKey(Function.identity()) выполняется и возвращает экземпляр Predicate<String>.

Затем этот Predicate оценивается (то есть выполняется его test() метод) несколько раз, каждый раз для другого элемента Stream.

Чтобы ваш последний фрагмент вел себя подобно конвейеру Stream, он должен выглядеть так:

final Function<String,Integer> a = (name)->name.length();
Predicate<String> pred = distinctByKey(a);
System.out.println(pred.test("JOHN"));
System.out.println(pred.test("STEPHEN"));
System.out.println(pred.test("ORTIZ"));
System.out.println(pred.test("RONDON"));

да, я так и думал, что это работает. но я столкнулся с этим фрагментом, и я сказал, что карта является экземпляром каждого хода, но я был неправ.

chiperortiz 27.05.2019 15:13

I thought that the distinctByKey method is called or interpreted in each iteration of the Stream i mean the Map being instance in each turn but it's not! my question is the body of the Predicate method call only one time? in the Stream iteration is this a assertion?

Нет. Потоки — это не волшебство, и они не нарушают стандартную семантику Java. Рассмотрим представленный код:

    final long d = Stream.of("JOHN","STEPHEN","ORTIZ","RONDON")
            .filter(distinctByKey(Function.identity()))
            .count();

Убрав из картины конкретные типы и методы, которые имеют следующую общую форму:

long x = A.b(y).c(z).d(w);

Нет причин ожидать, что любой из a(), b() или c() будет вызываться более одного раза в этой цепочке или что их аргументы оцениваются более одного раза каждый. На это не влияют некоторые типы Stream.

Вместо этого в вашем случае происходит то, что Predicate, возвращаемый (единственным вызовом) вашего метода distinctByKey(), является использовал более одного раза, поскольку поток, в который он встроен, обрабатывается. Этот Predicate содержит ссылку на Map, который он использует и модифицирует при выполнении своей работы.

Другие вопросы по теме