java stream Collectors.groupingBy() multiple fields

佐手、 提交于 2020-01-23 06:50:06

问题


Stream<Map.Entry<String, Long>> duplicates = notificationServiceOrderItemDto.getService()
    .getServiceCharacteristics()
    .stream()
    .collect(
        Collectors.groupingBy(
            ServiceCharacteristicDto::getName, Collectors.counting()
        )
     )
     .entrySet()
     .stream()
     .filter(e -> e.getValue() > 1);

Optional<String> dupName = duplicates.map(Map.Entry::getKey).findFirst();

works perfect. But I wold like to find duplicates not just with name but also name + value + key

That means if name + value + key is the same this is duplicate.

I am looking Collectors.groupingBy()

http://www.technicalkeeda.com/java-8-tutorials/java-8-stream-grouping

but I can not find correct solution


回答1:


Following works for me:

public class Groupingby
{
    static class Obj{
        String name;
        String value;
        String key;

        Obj(String name, String val, String key)
        {
            this.name = name;
            this.value = val;
            this.key = key;
        }
    }

    public static void main(String[] args)
    {
        List<Obj> objects = new ArrayList<>();

        objects.add(new Obj("A", "K", "Key1"));
        objects.add(new Obj("A", "K", "Key1"));
        objects.add(new Obj("A", "X", "Key1"));
        objects.add(new Obj("A", "Y", "Key2"));

        Map<List<String>, Long> collected = objects.stream().collect(Collectors.groupingBy(x -> Arrays.asList(x.name, x.value, x.key), Collectors.counting()));

        System.out.println(collected);
    }

}

// Output
// {[A, K, Key1]=2, [A, Y, Key2]=1, [A, X, Key1]=1}

Note that I am using list of attributes for grouping by, not string concatenation of attributes. This will work with non-string attributes as well.

If you are doing string concatenation, you may have some corner cases like attributes (A, BC, D) and (AB, C, D) will result in same string.




回答2:


Instead of

.collect(Collectors.groupingBy(ServiceCharacteristicDto::getName, Collectors.counting()))

you can write

.collect(Collectors.groupingBy(s->s.getName()+'-'+s.getValue()+'-'+s.getKey(), Collectors.counting()))



回答3:


You can replace ServiceCharacteristicDto::getName with:

x -> x.getName() + x.getValue() + x.getKey()

Use a lambda instead of a method reference.

But also think of what findFirst would actually mean here... you are collecting to a HashMap that has no encounter order, streaming its entries and getting the first element - whatever that is. You do understand that this findFirst can give different results on different input, right? Even re-shuffling the HashMap could return you a different findFirst result.

EDIT

to get away from possible un-intentional duplicates because of String concat, you could use:

x -> Arrays.asList(x.getName(), x.getValue(), x.getKey())


来源:https://stackoverflow.com/questions/50872162/java-stream-collectors-groupingby-multiple-fields

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!