Итерация двух идентичных Set с использованием Java Streams

У меня есть два HashSet с объектами одного типа. Мои критерии поиска такие: поиск в первом наборе, а если его нет, то поиск в другом наборе. Я пробовал со слоем Stream, выполнив следующие шаги, как указано ниже.

Set<MyObject> firstSet = new HashSet<>();
Set<MyObject> secondSet = new HashSet<>();

и эти два набора имеют некоторые значения.

Predicate<MyObject> match = myObject -> StringUtils.equals(myValue, myObject.getMyValue());

firstSet.values().stream().filter(match).findFirst()
.orElse(secondSet.values().stream().filter(match)
.findFirst().orElseThrow(()-> new MyException()));

Мой соответствующий объект находится в первом наборе, и я попытался получить его вручную, и я его получил ... но, используя указанную выше итерацию, я всегда получаю исключение, даже если первый набор имеет согласованный объект. Пожалуйста, поправьте меня .. спасибо заранее.


person Jijesh Kumar    schedule 07.10.2020    source источник
comment
Когда вы передаете параметр методу, сначала оценивается параметр. Во внешнем (т.е. первом) orElse параметром этого orElse является secondSet.stream().values().filter(match) .findFirst().orElseThrow(()-> new MyException()), который оценивается раньше всего в последних трех строках.   -  person Albert Hendriks    schedule 07.10.2020


Ответы (1)


Ваша проблема в том, что вы не используете Optional.orElse должным образом.

Когда вы используете Optional.orElse, его параметр оценивается с нетерпением. Это означает, что сначала выполняется поиск вашего второго набора, чтобы разрешить параметр Optional.orElse вашего первого набора.

Вместо этого используйте Optional.orElseGet, который получает Supplier, который оценивается лениво:

firstSet.stream()
    .filter(match)
    .findFirst()
    .orElseGet(() -> secondSet.stream()
        .filter(match)
        .findFirst()
        .orElseThrow(()-> new MyException()));

РЕДАКТИРОВАТЬ: Как предложил Хольгер в комментариях, есть более простой способ:

Stream.of(firstSet, secondSet)
    .flatMap(Set::stream)
    .filter(match)
    .findFirst()
    .orElseThrow(MyException::new);

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

person fps    schedule 07.10.2020
comment
спасибо за ваш ответ ... я попробую это сейчас - person Jijesh Kumar; 07.10.2020
comment
теперь это работает .... большое спасибо .. - person Jijesh Kumar; 07.10.2020
comment
В Set нет метода values(). Кроме того, только Stream.concat(firstSet.stream(), secondSet.stream()) .filter(match) .findFirst() .orElseThrow(MyException::new) выполнит работу. - person Holger; 08.10.2020
comment
@Holger Исправлена ​​.values() вещь, спасибо. Я думал в коде, похожем на ваш, и если бы это были списки, я бы так и сделал. Но это наборы. Уверены ли мы на 100% по спецификации, что после вызова Stream.concat в двух потоках, источником которых являются множества, элементы из второго набора появятся после всех элементов первого набора? - person fps; 08.10.2020
comment
Принимая спецификацию в том виде, в каком она написана, вы не можете быть уверены. Итак, если вы хотите быть в безопасности, лучше используйте Stream.of(firstSet, secondSet) .flatMap(Set::stream) .filter(match) .findFirst() .orElseThrow(MyException::new), который гарантированно будет упорядочен по назначению. - person Holger; 08.10.2020