Стоит ли использовать distinct() с collect(toSet())
при сборе элементов потока в набор есть ли какие-либо преимущества (или недостатки), чтобы также указать .distinct()
на поток? Например:
return items.stream().map(...).distinct().collect(toSet());
учитывая, что набор уже удалит дубликаты, это кажется избыточным, но предлагает ли он какие-либо преимущества или недостатки производительности? Зависит ли ответ от того, является ли поток параллельным / последовательным или упорядоченным/неупорядоченным?
3 ответов
по словам javadoc, distinct
является промежуточной операцией с состоянием.
если у вас буквально есть .distinct
сразу же за .collect
, это действительно не добавляет никакой пользы. Может быть, если .distinct
реализация более эффективна, чем Set
проверка дублирования, вы можете получить некоторую выгоду, но если вы собираете набор, Вы все равно получите тот же результат.
если, с другой стороны, .distinct
возникает перед вашим .map
операция, и это конкретное отображение является дорогостоящей операцией, вы можете получить некоторые выгоды, потому что вы обрабатываете меньше данных в целом.
в то время как у вас есть тот же результат, они не делают то же самое:toSet()
использовать HashSet
, и вы теряете начальный заказ, который является тем, что distinct может сохранить при необходимости:
С javadoc:
сохранение стабильности для различных () параллельных трубопроводов относительно дорого (требует, чтобы операция действовала как полная барьер, с существенными накладными расходами буферизации), и стабильность часто не требовать. Используя неупорядоченный источник потока (например, generate (Supplier)) или удаление ограничения заказа с помощью BaseStream.неупорядоченный() может привести к значительно более эффективному выполнение для distinct() в параллельных конвейерах, если семантика ваша ситуация позволяет. Если согласованность с порядком встречи требуется, и вы испытываете низкую производительность или память использование с distinct () в параллельных трубопроводах, переключая к последовательное выполнение с BaseStream.последовательная() может улучшить спектакль.
Если вам требуется стабильность, то это distinct()
. Используя toSet()
after было бы бесполезно (если не требуется API).
но это полезно, если у вас есть equals
реализация частичного равенства:
class F {
int a;
int b;
@Override int hashCode() {return Objects.hashCode(a);}
@Override boolean equals(Object other) {
if (other == this) return true;
if (!(other instanceof F)) return false;
return a == ((F)other).a;
}
}
если у вас a = F(10, 1)
и b = F(10, 2)
они равны. Но не все их поля равны.
если в списке у вас есть (b, a)
- С
toSet()
у вас не всегда будет это порядок. У вас может быть (b, a)и т. д. - С помощью distinct () вы сохраняете эту информацию, например:
(b, a)
.
это, однако, предполагает некоторые предварительные условия (последовательные и т. д.).
Примечание: это можно сделать с помощью TreeSet
и соответствующего compareTo
метод.
distinct будет вызывать equals / hashcode для разделения элементов, а позже toSet будет делать то же самое (даже если после distinct нет необходимости, но toSet не может этого знать), поэтому в основном вы просто дублируете вызовы. Он должен быть немного хуже ИМО. Это довольно легко измерить.