Если я понимаю логику в коде вопроса, вы хотите удалить последовательные повторяющиеся комментарии, но сохраните дубликаты, если в списке ввода есть несколько других комментариев.
В этом случае просто используйте .distinct()
(и после того, как equals
и hashCode
) будут правильно определены, не будут работать так, как предполагалось, поскольку будут устранены непоследовательные дубликаты.
Более «потоковое» решение здесь заключается виспользуйте пользовательский Collector
, который при сворачивании элементов в аккумулятор удаляет только последовательные дубликаты.
static final Collector<Comment, List<Comment>, List<Comment>> COMMENT_COLLECTOR = Collector.of(
ArrayDeque::new, //// supplier.
(list, comment) -> { /// folder
if (list.isEmpty() || !Objects.equals(list.getLast().getComment(), comment.getComment()) {
list.addLast(comment);
}
}),
(list1, list2) -> { /// the combiner. we discard list2 first element if identical to last on list1.
if (list1.isEmpty()) {
return list2;
} else {
if (!list2.isEmpty()) {
if (!Objects.equals(list1.getLast().getComment(),
list2.getFirst().getComment()) {
list1.addAll(list2);
} else {
list1.addAll(list2.subList(1, list2.size());
}
}
return list1;
}
});
Обратите внимание, что Deque
(в java.util.*
) - это расширенный тип List, в котором есть удобные операции дляполучить доступ к первому и последнему элементу списка.ArrayDeque
является реализацией на основе массива с ограничением (эквивалентно ArrayList
- List
).
По умолчанию сборщик всегда получает элементы в порядке входного потока, поэтому это должно работать.Я знаю, что это не намного меньше кода, но он настолько хорош, насколько это возможно.Если вы определите статический метод компаратора Comment
, который может обрабатывать null
элементов или комментарии с изяществом, вы можете сделать его немного более компактным:
static boolean sameComment(final Comment a, final Comment b) {
if (a == b) {
return true;
} else if (a == null || b == null) {
return false;
} else {
Objects.equals(a.getComment(), b.getComment());
}
}
static final Collector<Comment, List<Comment>, List<Comment>> COMMENT_COLLECTOR = Collector.of(
ArrayDeque::new, //// supplier.
(list, comment) -> { /// folder
if (!sameComment(list.peekLast(), comment) {
list.addLast(comment);
}
}),
(list1, list2) -> { /// the combiner. we discard list2 first element if identical to last on list1.
if (list1.isEmpty()) {
return list2;
} else {
if (!sameComment(list1.peekLast(), list2.peekFirst()) {
list1.addAll(list2);
} else {
list1.addAll(list2.subList(1, list2.size());
}
return list1;
}
});
----------
Возможно, вы предпочтете объявить правильное (именованное)класс, который реализует Collector, чтобы сделать его более понятным и избегать определения лямбда-выражений для каждого действия Collector.или, по крайней мере, реализовать лямбда-выражения, переданные в Collector.of
статическими методами, для улучшения читабельности.
Теперь код для выполнения реальной работы довольно тривиален:
List<Comment> unique = dbCommentHistory.stream()
.collect(COMMENT_COLLECTOR);
Вот и все.Однако, если это может стать немного более сложным, если вы хотите обрабатывать null
экземпляры комментариев (элементов).Код выше уже обрабатывает строку комментария как ноль, считая ее равной другой пустой строке:
List<Comment> unique = dbCommentHistory.stream()
.filter(Objects::nonNull)
.collect(COMMENT_COLLECTOR);