简体   繁体   中英

Sorting and Grouping Multi-field Objects

I have List<MyObjects> with 4 fields:

String fieldText;
double fieldDoubleOne;
double fieldDoubleTwo;
double fieldDoubleThree;

What would be the best (Java-8) way of collecting and printing out the list, sorted according to Average(fieldDoubleOne) so that the console output looks something like: fieldText → Average(fieldDoubleOne) → Average(fieldDoubleTwo) → Average(fieldDoubleThree) Assume public getters for all fields. For one double field, I had, from my previous question :

Map<String, List<MyObject>> groupByFieldText = myObjectList.stream().collect(groupingBy(MyObject::getFieldText));
Map<String, Double> averageMap =   groupByFieldText.entrySet().stream()
        .map(e -> new AbstractMap.SimpleEntry<>(e.getKey(),
                e.getValue().stream()
                        .mapToDouble(d -> d.getDoubleOne()).average()
                        .orElse(0d)))
        .sorted(Comparator.comparingDouble(Map.Entry::getValue))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                (v1, v2) -> v1, LinkedHashMap::new));
averageMap.entrySet().stream().sorted(Comparator.comparingDouble((Map.Entry::getValue)))
        .forEach(System.out::println);

You just need to sort the entries instead of mapping their value to double , like that:

Map<String, List<MyObject>> sortedByAverageOne = groupByFieldText.entrySet().stream()
        .sorted(Comparator.comparingDouble(e -> average(e, MyObject::getDoubleOne)))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                (v1, v2) -> v1, LinkedHashMap::new));

Note that if you have a lot of entries, it won't be very efficient because it calls average for every comparison.

Then, having such a sorted map you print it as follows:

sortedByAverageOne.entrySet().stream()
        .map(e -> String.format(
                "%s → %f → %f → %f",
                e.getKey(),
                average(e, MyObject::getDoubleOne),
                average(e, MyObject::getDoubleTwo),
                average(e, MyObject::getDoubleThree)
        ))
        .forEach(System.out::println);

Finally, average is defined as:

private static double average(Map.Entry<String, List<MyObject>> entry, ToDoubleFunction<MyObject> getter) {
    return average(entry.getValue(), getter);
}

private static double average(List<MyObject> list, ToDoubleFunction<MyObject> getter) {
    return list.stream().mapToDouble(getter).average().orElse(0);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM