Получить обновленный объект после сопоставления с помощью лямбда-выражения

#java #java-stream

Вопрос:

У меня есть список следующих объектов,

 ArrayList<Student> students = new ArrayList<>(
        List.of(
                Student.builder().id(1L).name("Joe").build(),
                Student.builder().id(2L).name("Jan").build()
        )
);
 

Я хочу обновить один из этих объектов, и у меня есть следующая реализация

 return findAll().stream()
            .filter(s -> s.getId() == studentId)
            .map(s -> students.set(students.indexOf(s), Student.builder().id(s.getId()).name(studentPayload.getName()).build()))
            .findFirst()
            .orElseThrow(() -> new StudentNotFoundException(String.format("Student with id [%d] not found", studentId)));
 

Это возвращает объект, который удовлетворяет условию, основанному на filter . К сожалению, это не самый современный объект!

Как я могу получить обновленный объект после сопоставления?

Комментарии:

1. Кажется, что сложность возникает в результате усилий, направленных на то, чтобы сделать его единым. Эта проблема в значительной степени исчезнет, если вы отфильтруете, чтобы найти свой объект, а затем обновите его как отдельные инструкции… и полученный код был бы более читабельным!

2. Переопределяет ли тип вашего построенного Student объекта equals() и hashCode() ?

3. Эмпирическое правило: если вы имеете дело не более чем с одним элементом, не используйте потоки.

4. Привет @AmalK, да, это так.

Ответ №1:

Это связано с тем, что set метод a List вернет ПРЕДЫДУЩИЙ элемент в этой позиции, а не вновь созданный. Ссылаться

Предполагая, что ваше идентификационное значение уникально, я не думаю, что это удобно использовать map , а затем findFirst , даже если вы могли бы достичь желаемого результата.

Вместо этого используйте findFirst и получите элемент, а затем обновите детали в качестве второго шага.

 Student sOne = findAll().stream()
                      .filter(s -> s.getId() == studentId)
                      .findFirst()
                      .orElseThrow(() -> new StudentNotFoundException(String.format("Student with id [%d] not found", studentId)));
    
Student sTwo = students.get(students.indexOf(sOne));
if(sTwo!=null) {
    sTwo.setName(studentPayload.getName());
}

return sTwo;
 

Если вы все еще хотите сделать это в одну строку, то используйте:

 map(s -> {
    Student stu = students.get(students.indexOf(s));
    stu.setName(studentPayload.getName());
    return stu;
}
 

Комментарии:

1. Насколько я понимаю код в вопросе, он обновляет как соответствующий объект из findAll() списка, так и соответствующий объект из students . Я думаю, что этот код будет обновлять только первый.

2. @ernest_k Да, я как-то пропустил эту findAll часть (я обновил свой ответ). Но обновляется только объект из students , верно?

3. Ах, вы правы. Только students обновляется.