/* package whatever; // don't place package name! */

import java.util.*;
import java.lang.*;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
	public static void main (String[] args) throws java.lang.Exception
	{
		//--------------------- Test Data ---------------------
        List<DummyObj> dataToBeAdded = new ArrayList<>(List.of(
                new DummyObj("uuid1", "abcd", "mer1", 20D),
                new DummyObj("uuid1", "pqrs", "mer1", 25D),
                new DummyObj("uuid2", "xyz", "mer1", 18D)
        ));

        List<DummyObj> dataToBeSubtracted = new ArrayList<>(List.of(
                new DummyObj("uuid1", "abcd", "mer1", 5D),
                new DummyObj("uuid1", "pqrs", "mer1", 2D),
                new DummyObj("uuid3", "xyz", "mer2", 10D)
        ));

        //--------------------- Original Code ---------------------
        Map<String, DummyObj> dataToBeAddedMap = dataToBeAdded.stream()
                .collect(Collectors.toMap(obj -> obj.getAttr1() + obj.getAttr2() + obj.getAttr3(), item -> item));

        Map<String, DummyObj> dataToBeSubtractedMap = dataToBeSubtracted.stream()
                .collect(Collectors.toMap(obj -> obj.getAttr1() + obj.getAttr2() + obj.getAttr3(),
                        item -> new DummyObj(item.getAttr1(), item.getAttr2(), item.getAttr3(), -1 * item.getAttr4())));

        Map<String, DummyObj> resultantData = Stream.of(dataToBeAddedMap, dataToBeSubtractedMap)
                .flatMap(map -> map.entrySet().stream())
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        (v1, v2) -> new DummyObj(v1.getAttr1(), v1.getAttr2(), v1.getAttr3(), v1.getAttr4() + v2.getAttr4())
                ));

        System.out.println("Output w Original Solution: ");
        for (DummyObj obj: resultantData.values()){
            System.out.println(obj);
        }


        //--------------------- Optimized Version ---------------------
        Map<String, DummyObj> mapRes = Stream.concat(dataToBeAdded.stream(), dataToBeSubtracted.stream().map(obj -> {
                    obj.setAttr4(-1 * obj.getAttr4());
                    return obj;
                }))
                .collect(Collectors.toMap(obj -> String.format("%s%s%s", obj.getAttr1(), obj.getAttr2(), obj.getAttr3()),
                        Function.identity(),
                        (obj1, obj2) -> new DummyObj(obj1.getAttr1(), obj1.getAttr2(), obj1.getAttr3(), obj1.getAttr4() + obj2.getAttr4())
                ));

        System.out.println("\nOutput w Optimized Solution: ");
        for (DummyObj obj: mapRes.values()){
            System.out.println(obj);
        }
    }

    static class DummyObj {
        private String attr1, attr2, attr3;
        private Double attr4;

        public DummyObj(String attr1, String attr2, String attr3, Double attr4) {
            this.attr1 = attr1;
            this.attr2 = attr2;
            this.attr3 = attr3;
            this.attr4 = attr4;
        }

        public String getAttr1() {
            return attr1;
        }

        public void setAttr1(String attr1) {
            this.attr1 = attr1;
        }

        public String getAttr2() {
            return attr2;
        }

        public void setAttr2(String attr2) {
            this.attr2 = attr2;
        }

        public String getAttr3() {
            return attr3;
        }

        public void setAttr3(String attr3) {
            this.attr3 = attr3;
        }

        public Double getAttr4() {
            return attr4;
        }

        public void setAttr4(Double attr4) {
            this.attr4 = attr4;
        }

        @Override
        public String toString() {
            return String.format("%s %s %s %g", attr1, attr2, attr3, attr4);
        }
    }
}