Вы должны быть последовательными и не смешивать разные подходы.
Существует подход API Collection, использующий либо
new ArrayList<>(new HashSet<>(someTypeList))
, либо new ArrayList<>(new LinkedHashSet<>(someTypeList))
, когда выхотите сохранить порядок
Тогда есть подход Stream API
someTypeList.stream().distinct().collect(Collectors.toList());
или someTypeList.stream().distinct().collect(Collectors.toCollection(ArrayList::new));
, когда вам нужнов результате получается ArrayList
Когда someTypeList
является обычной реализацией List
, подход Collection не только проще, но и более эффективен. API-интерфейс Stream в любом случае использует HashSet
при реализации distinct()
, но операция collect
немного пострадает от абстракции, так как не имеет подсказки об ожидаемом количестве элементов. Напротив, конструктор ArrayList
будет просто вызывать toArray
для входящего HashSet
и использовать результат в качестве своего резервного массива.
Все меняется, когда someTypeList
является неизвестной коллекцией. В некоторых сценариях Stream API может использовать характеристики источника для оптимизации операции. Если в источнике уже есть отдельные элементы, например, с Set
, накладные расходы на distinct()
будут устранены. Если источник отсортирован, для идентификации дубликатов будет использоваться другой алгоритм, для которого не требуется HashSet
.
Поскольку Stream API инкапсулирует фактическую реализацию, он может получить будущие улучшения без необходимостиадаптировать клиентский код. Напротив, явная операция Collection всегда будет делать именно то, что ей было сказано, никогда не получая выгоды от альтернативных стратегий реализации.