Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the most efficient way to combine objects in a List?

Let's say I have the following list.

List<StringInteger> test = new ArrayList<>(); //StringInteger is just a pojo with String and int
test.add(new StringInteger("a", 1));
test.add(new StringInteger("b", 1));
test.add(new StringInteger("a", 3));
test.add(new StringInteger("c", 1));
test.add(new StringInteger("a", 1));
test.add(new StringInteger("c", -1));

System.out.println(test); // [{ a : 1 }, { b : 1 }, { a : 3 }, { c : 1 }, { a : 1 }, { c : -1 }]

I need to write a method that would unite items by String key and add integers. So that the result list would be [{ a : 5 }, { b : 1 }, { c : 0 }]

I could do it using HashMap, but if I go that way - I'll have to create a Map, then use enhanced for-loop with if(containsKey(...)) and then convert it back to List. It just seems like an overkill.

Is there a more elegant solution? I thought that flatMap from Stream API should do the thing, but I cannot figure out how.

Here's my clumsy solution. It works, but I believe that it can be done more simple than that.

Map<String, Integer> map = new HashMap<>();
for (StringInteger stringInteger : test) {
    if (map.containsKey(stringInteger.getKey())) {
        int previousValue = map.get(stringInteger.getKey());
        map.put(stringInteger.getKey(), previousValue + stringInteger.getValue());
    } else {
        map.put(stringInteger.getKey(), stringInteger.getValue());
    }
}

List<StringInteger> result = map.entrySet()
    .stream()
    .map(stringIntegerEntry -> new StringInteger(stringIntegerEntry.getKey(), stringIntegerEntry.getValue()))
    .collect(Collectors.toList());

System.out.println(result); // [{ a : 5 }, { b : 1 }, { c : 0 }]
like image 593
user3280842 Avatar asked Oct 25 '25 19:10

user3280842


1 Answers

The simplest way to accomplish this is likely

List<StringInteger> combined = test.stream()
  .collect(
     Collectors.groupingBy(
        StringInteger::getKey,
        Collectors.summingInt(StringInteger::getValue)))
  .entrySet()
  .stream()
  .map(entry -> new StringInteger(entry.getKey(), entry.getValue()))
  .toList();
like image 175
Louis Wasserman Avatar answered Oct 27 '25 10:10

Louis Wasserman