Search code examples
sortingjava-8java-streamcollectorsgroupingby

Group the data into a Map<Long, List<Long>> where Lists need to be sorted


Assume I have the following domain object:

public class MyObj {
    private Long id;
    private Long relationId;
    private Long seq;
    
    // getters
}

There is a list List<MyObj> list. I want to create a Map by grouping the data by relationId (*key) and sort values (value is a list of id).

My code without sort values:

List<MyObj> list = getMyObjs();

// key: relationId, value: List<Long> ids (needs to be sorted)

Map<Long, List<Long>> map = list.stream()
    .collect(Collectors.groupingBy(
        MyObj::getRelationId, 
        Collectors.mapping(MyObj::getId, toList())
    ));
public class MyObjComparator{
    public static Comparator<MyObj> compare() {
        ...
    }
}

I have created compare method MyObjComparator::compare, my question is how to sort this map's values in the above stream.


Solution

  • To obtain the Map having the sorted lists of id as Values, you can sort the stream elements by id before collecting them (as @shmosel has pointed out in the comment).

    Collector groupingBy() will preserve the order of stream elements while storing them into Lists. In short, the only case when the order can break is while merging the partial results during parallel execution using an unordered collector (which has a leeway of combining results in arbitrary order). groupingBy() is not unordered, therefore the order of values in the list would reflect the initial order of elements in the stream. You can find detailed explanation in this answer by @Holger.

    You don't need a TreeMap (or a LinkedHashMap), unless you want the Entries of the Map to be sorted as well.

    List<MyObj> list = getMyObjs();
            
    // Key: relationId, Value: List<Long> ids (needs to be sorted)
    
    Map<Long, List<Long>> map = list.stream()
        .sorted(Comparator.comparing(MyObj::getId))
        .collect(Collectors.groupingBy(
            MyObj::getRelationId,
            Collectors.mapping(MyObj::getId, Collectors.toList())
        ));