Подсчет нескольких полей объекта с использованием потоков - PullRequest
0 голосов
/ 01 февраля 2019

Мне было интересно, как считать различные поля объекта, используя один поток.Я знаю, что могу легко посчитать одно свойство объекта, используя потоки (countedWithStream) или даже используя for для подсчета сразу нескольких (countedWithFor).Но мне бы очень хотелось узнать, возможно ли достичь того же, что и countedWithFor, но с использованием одного потока, генерирующего тот же результат.

import com.google.common.collect.ImmutableMap;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.LongStream;

import static java.util.stream.Collectors.*;

class Scratch {
public static void main(String[] args) {

    List<AnObject> objects = createObjects();

    Map<String, Map<Long, Long>> countedWithStream = countUsingStream(objects);
    Map<String, Map<Long, Long>> countedWithFor = countUsingFor(objects);
}

private static Map<String, Map<Long, Long>> countUsingStream(List<AnObject> objects) {
    BiFunction<List<AnObject>, Function<AnObject, Long>, Map<Long, Long>> count = (ojs, mpr) -> ojs.stream()
                                                                                                   .collect(groupingBy(mpr, counting()));

    return ImmutableMap.<String, Map<Long, Long>>builder().put("firstId", count.apply(objects, AnObject::getFirstId))
                                                          .put("secondId", count.apply(objects, AnObject::getSecondId))
                                                          .build();
}
private static Map<String, Map<Long, Long>> countUsingFor(List<AnObject> objects) {
    Map<Long, Long> firstIdMap = new HashMap<>();
    Map<Long, Long> secondIdMap = new HashMap<>();

    final BiFunction<Long, Map<Long, Long>, Long> count = (k, m) -> k != null ? m.containsKey(k) ? m.put(k, m.get(k) + 1L) : m.put(k, 1L) : null;

    for (AnObject object : objects) {
        count.apply(object.firstId, firstIdMap);
        count.apply(object.secondId, secondIdMap);
    }

    return ImmutableMap.<String, Map<Long, Long>>builder().put("firstId", firstIdMap)
                                                          .put("secondId", secondIdMap)
                                                          .build();
}

private static List<AnObject> createObjects() {
    return LongStream.range(1, 11)
                     .mapToObj(Scratch::createObject)
                     .collect(toList());
}

private static AnObject createObject(long id) {
    return new AnObject(id, id);
}

private static class AnObject {
    public final long firstId;
    public final long secondId;

    public AnObject(long firstId, 
                    long secondId) {
        this.firstId = firstId;
        this.secondId = secondId;
    }

    public long getFirstId() {
        return firstId;
    }

    public long getSecondId() {
        return secondId;
    }
}

Ответы [ 2 ]

0 голосов
/ 21 февраля 2019

Вы можете реализовать пользовательский сборщик , см. Пример в этой статье :

public class Scratch {

    public static final String FIRST_ID = "firstId";

    public static final String SECOND_ID = "secondId";

    private static class AnObjectFieldCounter implements Collector<AnObject, Map<String, Map<Long, Long>>, Map<String, Map<Long, Long>>> {
        @Override
        public Supplier<Map<String, Map<Long, Long>>> supplier() {
            return HashMap::new;
        }

        @Override
        public BiConsumer<Map<String, Map<Long, Long>>, AnObject> accumulator() {
            return (map, obj) -> {
                Map<Long, Long> inner;

                inner = map.getOrDefault(FIRST_ID, new HashMap<>());
                inner.compute(obj.getFirstId(), (id, count) -> (count == null) ? 1 : count + 1);
                map.put(FIRST_ID, inner);

                inner = map.getOrDefault(SECOND_ID, new HashMap<>());
                inner.compute(obj.getSecondId(), (id, count) -> (count == null) ? 1 : count + 1);
                map.put(SECOND_ID, inner);
            };
        }

        @Override
        public BinaryOperator<Map<String, Map<Long, Long>>> combiner() {
            return (a, b) -> {
                Map<Long, Long> firstIdCountMap = Stream
                        .concat(a.get(FIRST_ID).entrySet().stream(), b.get(FIRST_ID).entrySet().stream())
                        .collect(groupingBy(Map.Entry::getKey, Collectors.summingLong(Map.Entry::getValue)));

                Map<Long, Long> secondIdCountMap = Stream
                        .concat(a.get(SECOND_ID).entrySet().stream(), b.get(SECOND_ID).entrySet().stream())
                        .collect(groupingBy(Map.Entry::getKey, Collectors.summingLong(Map.Entry::getValue)));

                Map<String, Map<Long, Long>> result = new HashMap<>();
                result.put(FIRST_ID, firstIdCountMap);
                result.put(SECOND_ID, secondIdCountMap);
                return result;
            };
        }


        @Override
        public Function<Map<String, Map<Long, Long>>, Map<String, Map<Long, Long>>> finisher() {
            return Function.identity();
        }

        @Override
        public Set<Characteristics> characteristics() {
            return new HashSet<>(Arrays.asList(UNORDERED, IDENTITY_FINISH));
        }
    }

    public static void main(String[] args) {

        List<AnObject> objects = createObjects();

        Map<String, Map<Long, Long>> countedWithCollector = countUsingCollector(objects);
        Map<String, Map<Long, Long>> countedWithStream = countUsingStream(objects);
        Map<String, Map<Long, Long>> countedWithFor = countUsingFor(objects);
    }

    private static Map<String, Map<Long, Long>> countUsingCollector(List<AnObject> objects) {
        Map<String, Map<Long, Long>> result = objects.stream().collect(new AnObjectFieldCounter());
        return ImmutableMap.<String, Map<Long, Long>>builder().putAll(result).build();
    }

    //...
}
0 голосов
/ 01 февраля 2019

Вы можете использовать сокращение, чтобы выполнить работу за n итераций с чем-то вроде этого:

Supplier<Map<String, Map<Long, Long>>> mapSupplier = () -> {
    Map<String, Map<Long, Long>> outputMap = new HashMap<>();
    outputMap.put("firstId", new HashMap<>());
    outputMap.put("secondId", new HashMap<>());
    return outputMap;
};

Map<String, Map<Long, Long>> reduce = objects.stream().collect(mapSupplier,
        (acc, obj) -> {
            acc.get("firstId").merge(obj.firstId, 1L, (curv, incr) -> curv + incr);
            acc.get("secondId").merge(obj.secondId, 1L, (curv, incr) -> curv + incr);
        }
        , (acc1, acc2) -> {
            acc2.get("firstId").forEach((k, v) -> acc1.get("firstId").merge(k, v, (v1, v2) -> v1 + v2));
            acc2.get("secondId").forEach((k, v) -> acc1.get("secondId").merge(k, v, (v1, v2) -> v1 + v2));
        });

Но это может быть не так кратко, как вы хотите.

...