Ответ 1
При использовании sorted(comparator)
в потоке перед операцией collect
поток должен буферизовать содержимое всего потока, чтобы иметь возможность сортировать его, и сортировка может включать гораздо большее перемещение данных в этом буфере по сравнению с сортировкой меньшие списки групп впоследствии. Таким образом, производительность не так хороша, как сортировка отдельных групп, хотя реализация будет использовать несколько ядер, если включена параллельная обработка.
Но обратите внимание, что использование sortedListsByGender.values().forEach(…)
не является параллелизуемой операцией, и даже использование sortedListsByGender.values().parallelStream().forEach(…)
допускает только параллельную обработку групп, в то время как каждая операция сортировки по-прежнему является последовательной.
При выполнении операции сортировки в коллекторе, как в
static <T> Collector<T,?,List<T>> toSortedList(Comparator<? super T> c) {
return Collectors.collectingAndThen(
Collectors.toCollection(ArrayList::new), l->{ l.sort(c); return l; } );
}
Map<Gender, List<Person>> sortedListsByGender = roster.stream()
.collect(Collectors.groupingBy(Person::getGender, toSortedList(Person::compareByAge)));
операция сортировки ведет себя одинаково (спасибо Тагиру Валееву за исправление меня), но вы можете легко проверить, как выполняется стратегия сортировки по вставке. Просто измените реализацию коллектора на:
static <T> Collector<T,?,List<T>> toSortedList(Comparator<? super T> c) {
return Collectors.collectingAndThen(
Collectors.toCollection(()->new TreeSet<>(c)), ArrayList::new);
}
Для полноты, если вы хотите, чтобы коллекционер, который вставляет сортировку в ArrayList
, во-первых, чтобы избежать финального этапа копирования, вы можете использовать более продуманный сборщик, например:
static <T> Collector<T,?,List<T>> toSortedList(Comparator<? super T> c) {
return Collector.of(ArrayList::new,
(l,t) -> {
int ix=Collections.binarySearch(l, t, c);
l.add(ix<0? ~ix: ix, t);
},
(list1,list2) -> {
final int s1=list1.size();
if(list1.isEmpty()) return list2;
if(!list2.isEmpty()) {
list1.addAll(list2);
if(c.compare(list1.get(s1-1), list2.get(0))>0)
list1.sort(c);
}
return list1;
});
}
Эффективен для последовательного использования, но его функция слияния не является оптимальной. Алгоритм базового сортирования выиграет от предварительно определенных диапазонов, но сначала должен найти эти диапазоны, несмотря на то, что наша функция слияния действительно знает эти диапазоны. К сожалению, в JRE нет публичного API, позволяющего использовать эту информацию (эффективно, мы можем передать subList
в binarySearch
, но создание нового подписок для каждого элемента list2
может оказаться слишком дорогостоящим). Если мы хотим еще больше повысить производительность параллельного выполнения, мы должны повторно реализовать часть слияния алгоритма сортировки:
static <T> Collector<T,?,List<T>> toSortedList(Comparator<? super T> c) {
return Collector.of(ArrayList::new,
(l,t) -> l.add(insertPos(l, 0, l.size(), t, c), t),
(list1,list2) -> merge(list1, list2, c));
}
static <T> List<T> merge(List<T> list1, List<T> list2, Comparator<? super T> c) {
if(list1.isEmpty()) return list2;
for(int ix1=0, ix2=0, num1=list1.size(), num2=list2.size(); ix2<num2; ix2++, num1++) {
final T element = list2.get(ix2);
ix1=insertPos(list1, ix1, num1, element, c);
list1.add(ix1, element);
if(ix1==num1) {
while(++ix2<num2) list1.add(list2.get(ix2));
return list1;
}
}
return list1;
}
static <T> int insertPos(
List<? extends T> list, int low, int high, T t, Comparator<? super T> c) {
high--;
while(low <= high) {
int mid = (low+high)>>>1, cmp = c.compare(list.get(mid), t);
if(cmp < 0) low = mid + 1;
else if(cmp > 0) high = mid - 1;
else {
mid++;
while(mid<=high && c.compare(list.get(mid), t)==0) mid++;
return mid;
}
}
return low;
}
Обратите внимание, что это последнее решение, в отличие от простой вставки binarySearch
, является стабильной реализацией сортировки, т.е. в вашем случае Person
с тем же возрастом и Gender
не изменит их относительный порядок, если исходный поток имеет определенный порядок встреч.