简体   繁体   中英

How can I group a list of objects by two attributes in one iteration?

I'm trying to group a large list of objects by two of their attributes. To demonstrate what I mean, consider the following example.

public class Foo {

    private String attributeA;
    private String attributeB;
    private String anotherAttribute;
}

I want to group a large list of Foo objects by the attributes attributeA and attributeB . Currently I do the following.

List<Foo> foos = getFoos();
Map<Set<String>, List<String>> groupedFoos = Sets.newHashMap();
Set<String> fooGroup;
for(Foo foo : foos) {
    fooGroup = Sets.newHashMap(foo.getAttributeA(), foo.getAttributeB());

    if (!groupedFoos.containsKey(fooGroup)) {
        groupedFoos.put(fooGroup, Lists.newArrayList(foo));
    } else {
        groupedFoos.get(fooGroup).add(foo);
    }
}

How can I achieve the same result without using a Map like Map<Set<String>, List<String>> ? It is important to do this in one iteration. The values of the attributes attributeA and attributeB can be swapped. So using Pair as the key of the Map is also not an option.

If you want to get rid of Map as key, you can always write your own Key in a way that will compare both attributes (regardless of their order).

public class Key {
    private String a;
    private String b;
    private String c;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Key foo = (Key) o;

        if (a.equals(foo.a) || a.equals(foo.b)) {
            return true;
        }

        return b.equals(foo.b) || b.equals(foo.a);
    }

    @Override
    public int hashCode() {
        int result = a.hashCode();
        result = 31 * result + b.hashCode();
        return result;
    }
}

Add a key method to your class.

public class Foo {

    private String attributeA;
    private String attributeB;
    private String anotherAttribute;

    public final String getKey() {
      return this.attributeA + "$" + this.attributeB; //use $ or any other delimiter as suggested in the comment
    }
}

Then if you can use Java8, use Collectors.groupingBy() method like below

 final Map<String, List<Foo>> result = getFoos().stream().collect(Collectors.groupingBy(Foo:getKey));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM