#java #java-stream
#java #java-stream
Вопрос:
Я пытаюсь лучше понять потоки в Java, и я просто пытаюсь представить вложенный цикл for в виде потока, но я изо всех сил пытаюсь понять это.
Я перепробовал несколько вещей, но, похоже, не могу этого понять.
Например, что-то вроде этого.
for (Profile profile : profiles) {
for (User user : users) {
if (user.getUserId().equals(profile.getProfileId())) {
profile.setField(false);
}
}
}
Как бы это сделать в виде потока на Java?
Комментарии:
1. В этом конкретном примере потоки вообще не приносят пользы и сводятся к простым вложенным вызовам
forEach
, которые выглядят почти точно так же, как оригинал.2. Поскольку вы выполняете итерацию по двум несвязанным коллекциям, потоки здесь действительно не могут быть использованы
Ответ №1:
Я думаю, вы пытаетесь понять поток, но позвольте мне сказать, что for
версия не хуже, а Stream
версия не лучше: тест производительности и интуиция, вероятно, помогут вам в этом, а также в удобочитаемости.
Тем не менее, ваша проблема здесь в том, что для вашего второго вида требуется, чтобы первый работал:
profiles.stream()
.filter(profile -> users.stream()
.anyMatch(user -> user.getUserId().equals(profile.getProfileId())))
.forEach(profile -> profile.setField(false));
Это строго не то же самое, что ваш цикл for: вы вызываете setField
несколько раз для каждого профиля, в то время как в потоковой версии это выполняется только в том случае, если есть один соответствующий пользователь. Я предположил, что setField
это установщик, и, как таковой, поскольку его значение является постоянным, не должно иметь значения, вызывается ли оно один или несколько раз.
Я бы посоветовал вам не использовать первый поток в этом случае или ограничиться forEach
:
profiles.forEach(profile -> profile.setField(!users.stream()
.anyMatch(user -> user.getUserId().equals(profile.getProfileId())));
Который можно было бы упростить, сначала получив все идентификаторы пользователей и используя contains из сгенерированного набора:
var userIds = users.stream().map(User::getUserId).collect(toSet());
profiles.forEach(profile -> profile.setField(!userIds.contains(profile.getProfileId()));
Комментарии:
1.
anyMatch
не совпадает с последним совпадением, это может быть несовместимым изменением. С другой стороны, почему используется предположение о сравненииid
с==
рассматриваемымequals
файлом? И ваш последний код также устанавливаетfield
атрибут некоторых профилей наtrue
, что опять же несовместимо с существующим поведением кода.2. Это вопрос логики и лени: логика здесь заключается в чтении
for*2
версии, которая вызываетprofile.setField(false)
, если есть соответствующий пользователь. Я ожидал, что метод будет простым установщиком: если бы он подсчитывал количество вызовов, это было бы действительно по-другому, но я бы предпочел выбрать для него лучшее название. Что касаетсяuser.id == profile.id
, как уже было сказано , я упростил геттер из лени, так что да, так и должно бытьuser.getUserId().equals(profile.getProfileId())
.3. Упрощение метода получения не означает, что вы не можете сравнивать
user.id.equals(profile.id)
. На самом деле я не имел в виду подсчет звонков. Просто протестируйте свой метод и тот, о котором идет речь, на примере данных, где некоторые профили не выбраныuserIds
, но им ранее было присвоено значение атрибутаfalse
forfield
.