如何在一次迭代中通过两个属性对对象列表进行分组?

3

我尝试按照两个属性对一个大列表的对象进行分组。为了说明我的意思,请考虑以下示例。

public class Foo {

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

我希望能够按照attributeAattributeB这两个属性对大量的Foo对象进行分组。目前我的做法如下。

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);
    }
}

如何在不使用像Map<Set<String>, List<String>>这样的映射的情况下实现相同的结果?重要的是在一次迭代中完成。属性attributeAattributeB的值可以互换。因此,使用Pair作为Map的键也不是选择。


1
如果您可以覆盖 Foo.hashCode,为什么不实现对两个属性进行排序组合的哈希,并将其用作映射键呢?或者只需像您一样使用 Sets.newHashMap(),但事先对属性进行排序即可。 - Xavi López
我也无法覆盖哈希码,因为在这种情况下无法修改实体。因为我无法访问它们。 - Said Savci
@maraca 我不想排序,我只想按两个参数分组。 - Said Savci
那么也许可以实现自己的键类或将它们哈希化并使用哈希作为键。排序是满足“a”和“b”值可互换要求的一种方法。 - Xavi López
1
我是唯一一个认为这个用例可以使用lambda的人吗?我认为Java 8的Collectors可能会对你有所帮助。这里有相关文档。 - Arthur Eirich
简单的解决方案是使用Map,并将属性连接起来作为键,使用一个保证不会出现在任何用于键的属性中的分隔符。 - maraca
2个回答

3
如果你想要摆脱以Map作为键,你总是可以编写自己的Key,以一种比较两个属性的方式(无论它们的顺序如何)。
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;
    }
}

1
给你的类添加一个key方法。
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
    }
}

如果您可以使用Java8,请使用以下方式使用Collectors.groupingBy()方法进行分组。
 final Map<String, List<Foo>> result = getFoos().stream().collect(Collectors.groupingBy(Foo:getKey));

谢谢您的回答,但正如我在问题中提到的那样:属性的值可以交换。 - Said Savci
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - maraca

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接