Group by in java 8 with value is as custom Map

Viewed 100

Is there any other way to refactor below code in single line in java 8. To get final output map I am performing two phase operation.

class Pair<X, Y> {

      public X first;
      public Y second;
      public Pair(X first, Y second) {
        this.first = first;
        this.second = second;
      }
     //.... other stuff
}

class InvHistory{
  
    private Integer sysId;
    private Integer sourceId;  
    public InvHistory(Integer sysId, Integer sourceId) {         
        this.sysId = sysId;
        this.sourceId = sourceId;
    }
    //... other stuff   
} 

    public static void main(String[] args) {
    List<InvHistory> invHisLst=Arrays.asList(new InvHistory(1,101),new InvHistory(2,102),new InvHistory(3,102));
        
    Map<Integer, List<InvHistory>> grpMap=invHisLst.stream().collect(Collectors.groupingBy(InvHistory::getSourceId));
        
    System.out.println("Group Map:"+grpMap);
        
    Map<Integer,Pair<Boolean,List<InvHistory>>> newMap=new HashMap<>();
    
    grpMap.forEach((key,value)->{
            Pair<Boolean,List<InvHistory>> accociatedValue=new Pair<>(Boolean.TRUE,value);
            newMap.put(key, accociatedValue);
    });
    System.out.println(newMap);
}

thanks in advance

1 Answers

You're still going to need to backreference the list you made to accommodate duplicate source ids. Once you build the first map, simply stream the entrySet and build the second. Here is how that would be done.

Map<Integer, Pair<Boolean, List<InvHistory>>> grpMap =
        invHistLst.stream()
                .collect(Collectors
                        .groupingBy(InvHistory::getSourceId))
                .entrySet().stream()
                .collect(Collectors.toMap(Entry::getKey,
                        e -> new Pair<>(Boolean.TRUE,
                                e.getValue())));

But all this is doing is combining what you already have. I would recommend the following approach.

Map<Integer, Pair<Boolean, List<InvHistory>>> map =
        new HashMap<>();
  • Now iterate thru your history list.
  • if the sourceId is not present, add it and create the pair entry with the boolean and an empty list.
  • this also returns the just added Pair so you can use a getter to retrieve the associated list add the inventory item.
  • if the sourceID is present, the existing pair is returned and you can add it just as before.
for (InvHistory h : invHistLst) {
    map.compute(h.getSourceId(),
            (id, pair) -> pair == null ? new Pair<>(
                    Boolean.TRUE, new ArrayList<>()) : pair)
            .getSecond().add(h);
}

map.entrySet().forEach(System.out::println);

With the appropriate toString methods, this is the result for both methods.

SourceId = 101, Pair = true [{sysID = 1, sourceId = 101}]
SourceId = 102, Pair = true [{sysID = 2, sourceId = 102}, {sysID = 3, sourceId =
 102}]

And as I stated, this requires you to access the second value from the Pair object. Check out Map.compute for more about the method.

Related