简体   繁体   中英

Java grouping by back to List of objects?

I have a simple pojo:

class Pojo
{
   string key;
   int value;
}

This is in a list:

List<Pojo> myList;

I can have multiple values per key, so for example:

A 5
A 7
B 3

So I want to group by the key and sum the values and put it back into a List<Pojo> with a single entry for A 12, B 3, etc.

This is what I have right now:

myMap.get("xxx")
  .values()
    .stream()
    .collect(Collectors.groupingBy(Pojo::getKey), Collectors.summingLong(Pojo::getValue))

This gets me a Map<String, Int> .

Do I need to do something like:

myMap.get("xxx")
  .values()
    .stream()
    .collect(Collectors.groupingBy(Pojo::getKey), Collectors.summingLong(Pojo::getValue))
    .entrySet().stream.map((entry) -> new Pojo(..)).collect(Collectors.toList())

Or is there a cleaner way to do it?

It should work when using Collectors::reducing .

myMap.get("xxx").values().stream()
    .collect(Collectors.groupingBy(Pojo::getKey, Collectors.reducing(
        new Pojo(null, 0),
        (a, b) -> new Pojo(b.getKey(), a.getValue() + b.getValue())))
    .values()

The call to groupingBy would return a Map<String, List<Pojo>> , but providing a reducing collector as second argument to groupingBy takes the values of the list and reduces them to a single element. The result is a Map<String, Pojo> , and in order to get a Collection<Pojo> , we simply need to call values() on the Map .

There are several ways to do it. Sometimes the simplest is the best and most efficient. Here is one of them.

List<Pojo> list = List.of(new Pojo("A", 5), new Pojo("B", 3),
        new Pojo("A", 7), new Pojo("C", 10), new Pojo("B", 12));

Map<String, Pojo> map = new HashMap<>();

The way this works is as follows:

  • it requires a simple setter in your class to set the value.
  • Iterate thru the list and if the map doesn't contain it, add it.
  • else use the just retreived object and add its value and the current object's value and set the sum in the Map's version of the object. So the map maintains a tally of the update without replacing the object in the map.
for (Pojo p : list) {
    Pojo cp;
    if ((cp = map.get(p.getKey())) != null) {
        cp.setValue(cp.getValue() + p.getValue());
    } else {
        map.put(p.getKey(), p);
    }
}

This next step could be eliminated if you don't mind working with type Collection .

List<Pojo> results = new ArrayList<>(map.values());

Now print them

results.forEach(Sytem.out::println);

Prints

A 12
B 15
C 10  

On the other hand, if you prefer streams this works sans a setter. But it does create a new object to replace the old one.

Map<String, Pojo> map = list.stream()
        .collect(Collectors.toMap(Pojo::getKey, s -> s,
                (a, b) -> new Pojo(a.getKey(),
                        a.getValue() + b.getValue())));

map.values().forEach(Sytem.out::println);

    
The pojo class

class Pojo { String key; int value;

public Pojo(String key, int value) {
    this.key = key;
    this.value = value;
}

public int getValue() {
    return value;       
}

public String getKey() {
    return key;
}

public void setValue(int v) {
    this.value = v;
}
public String toString() {
    return key + " " + value;
}

}

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