Собрать, чтобы сопоставить значение порядка/позиции отсортированного потока

Я сортирую заполненный набор MyObject (объект имеет геттер getName()) в потоке, используя предопределенный myComparator.

Затем, после сортировки, есть ли способ собрать на карту имя MyObject и порядок/положение объекта из сортировки?

Вот как я думаю, это должно выглядеть:

Set<MyObject> mySet;  // Already populated mySet 

Map<String, Integer> nameMap = mySet.stream()
        .sorted(myComparator)
        .collect(Collectors.toMap(MyObject::getName, //HowToGetThePositionOfTheObjectInTheStream));

Например, если набор содержит три объекта (объект1 с именем имя1, объект2 с именем имя2, объект3 с именем имя3) и во время потока они сортируются, как мне получить результирующую карту, которая выглядит следующим образом:

name1, 1
name2, 2
name3, 3

Спасибо.


person 000000000000000000000    schedule 25.05.2018    source источник
comment
Используйте TreeMap: stackoverflow.com/a/7427797/5645656   -  person Cardinal System    schedule 25.05.2018
comment
@CardinalSystem Как именно поможет TreeMap?   -  person lexicore    schedule 25.05.2018
comment
@lexicore Я неправильно истолковал вопрос. Хотя он просил отсортированную карту.   -  person Cardinal System    schedule 25.05.2018


Ответы (4)


Java Stream не предоставляет никакого индекса или позиционирования элементов, поэтому я не знаю способа заменить /*HowToGetThePositionOfTheObjectInTheStream*/ магией потоков для получения желаемого числа.

Вместо этого один простой способ — собрать вместо этого List, что дает каждому элементу индекс. Он отсчитывается от нуля, поэтому при преобразовании в карту добавьте 1.

List<String> inOrder = mySet.stream()
     .sorted(myComparator)
     .map(MyObject::getName)
     .collect(Collectors.toList());
Map<String, Integer> nameMap = new HashMap<>();
for (int i = 0; i < inOrder.size(); i++) {
    nameMap.put(inOrder.get(i), i + 1);
}
person rgettman    schedule 25.05.2018
comment
Я думаю, что я с вами на этом. Первоначально я думал, что может быть функция, которая будет раскрывать позиционирование, но поиск в Интернете говорит об обратном. Спасибо за этот ответ. - person 000000000000000000000; 25.05.2018

Попробуй это. вы можете использовать AtomicInteger для значения каждой записи карты. а также гарантировать порядок использования карты LinkedHashMap.

AtomicInteger index = new AtomicInteger(1);
Map<String, Integer> nameMap =  mySet.stream()
    .sorted(myComparator)
    .collect(Collectors
            .toMap(MyObject::getName, value -> index.getAndIncrement(),
                  (e1, e2) -> e1, LinkedHashMap::new));
person Hadi J    schedule 25.05.2018
comment
OP не заботится о порядке карты, только о ее записях; обычный toMap с do. - person Bohemian♦; 26.05.2018
comment
Это не удастся параллельно. - person shmosel; 26.05.2018

Простейшим решением будет цикл, так как формально правильное потоковое решение, которое также будет работать параллельно, требует нетривиальной (по сравнению с остальными) функции слияния:

Map<String,Integer> nameMap = mySet.stream()
    .sorted(myComparator)
    .collect(HashMap::new, (m, s) -> m.put(s.getName(), m.size()),
        (m1, m2) -> {
            int offset = m1.size();
            m2.forEach((k, v) -> m1.put(k, v + offset));
    });

Сравните с операциями цикла/коллекции:

List<MyObject> ordered = new ArrayList<>(mySet);
ordered.sort(myComparator);
Map<String, Integer> result = new HashMap<>();
for(MyObject o: ordered) result.put(o.getName(), result.size());

Оба решения предполагают уникальные элементы (поскольку может быть только одна позиция). Легко изменить цикл для обнаружения нарушений:

for(MyObject o: ordered)
    if(result.putIfAbsent(o.getName(), result.size()) != null)
        throw new IllegalStateException("duplicate " + o.getName());
person Holger    schedule 26.05.2018

Не используйте поток:

List<MyObject> list = new ArrayList<>(mySet);
list.sort(myComparator);
Map<String, Integer> nameMap = new HashMap<>();
for (int i = 0; i < list.size(); i++) {
    nameMap.put(list.get(i).getName(), i);
}

Мало того, что это будет выполняться быстрее, чем подход, основанный на потоках, все знают, что происходит.

Потоки имеют свое место, но код до Java 8 тоже.

person Bohemian♦    schedule 25.05.2018
comment
Цикл может быть еще проще: for(MyObject o: list) nameMap.put(o.getName(), nameMap.size()); - person Holger; 26.05.2018