【问题标题】:Multiple HashCodes for Java ObjectsJava 对象的多个 HashCode
【发布时间】:2016-02-22 06:52:32
【问题描述】:

我正在尝试优化一些代码,当我这样做时,我通常最终会从哈希结构中获得帮助。

我想要做的是基于某些属性以非常快速的方式将对象分成多个集合。基本上类似于 SQL GROUP BY 语句,但用于 Java。

问题是我想使用HashMap<Object, ArrayList<Object>> 来执行此操作。我想使用多种分组方式,但Object 只能有一个hashCode()

有没有办法拥有多个hashCodes() 以便能够通过多种方法进行分组?是否有其他结构可以解决此类问题?我可以使用 Java 8 lambda 表达式在 HashMap 参数中发送 hashCode() 吗?我是不是傻了,有没有这么复杂的超快速方法?

注意:我想要的 hashCodes 使用多个不恒定的属性。例如,创建一个唯一代表这些属性的字符串是行不通的,因为我必须每次都刷新字符串。

【问题讨论】:

  • 我不明白。假设对象 A 根据其当前属性分配给组 12。您将其存储在 HashMap 中。然后属性改变:这将改变对象的组。因此,您存储在 HashMap 中的内容现在已无用。 Map 键应该是不可变的。
  • 你不应该关心哈希码的值。每个分组键集都有一个 MultiMapMultiMap 键是描述分组键的对象,值是该组中的对象列表。

标签: java group-by hashmap hashcode


【解决方案1】:

假设您有一个对象集合,并且您想要生成类似于 SQL GROUP BY 的不同分组。每个分组依据由一组公共值定义。为每个不同的分组类型创建一个 group-by-key 类,每个类都有一个适当的 hashCode()equals() 方法(根据 Map 合同的要求)。

对于以下伪代码,我假设存在一个 MultiMap 类,该类封装了对地图的 List<Object> 值的管理。你可以使用 Guava 的 MultiMap 实现。

// One group key
public class GroupKey1 {
    ...
    public GroupKey1(MyObject o) {
        // populate key from object
    }
    public GroupKey1(...) {
        // populate from individual values so we can create lookup keys
    }
    public int hashCode() { ... }
    public boolean equals() { ... }
}

// A second, different group key
public class GroupKey2 {
    ...
    public GroupKey2(MyObject o) {
        // populate key from object
    }
    public GroupKey2(...) {
        // populate from individual values so we can create lookup keys
    }
    ...
}
...
MultiMap<GroupKey1,MyObject> group1 = new HashMultiMap<>();
MultiMap<GroupKey2,MyObject> group2 = new HashMultiMap<>();

for (MyObject m : objectCollection)
{
    group1.put(new GroupKey1(m), m);
    group2.put(new GroupKey2(m), m);
}
...
// Retrieve the list of objects having a certain group-by key
GroupKey2 lookupKey = new Groupkey2(...);
Collection<MyObject> group = group2.get(lookupKey);

【讨论】:

  • 当然,如果 group by 是单个字段,您可能只使用值类型本身,例如String, Integer, Date, ...
【解决方案2】:

您所描述的内容听起来像是一个相当复杂的模式,并且可能是premature optimization。询问有关如何在 Java 中有效地复制 GROUP BY 样式的查询的问题可能会更好。

也就是说,拥有多个哈希码的最简单方法是拥有多个类。这是一个简单的例子:

public class Person {
  String firstName;
  String lastName;

  /** the "real" hashCode() */
  public int hashCode() {
    return firstName.hashCode() + 1234 * lastName.hashCode();
  }
}

public class PersonWrapper1 {
  Person person;

  public int hashCode() {
    return person.firstName.hashCode();
  }
}

public class PersonWrapper2 {
  Person person;

  public int hashCode() {
    return person.lastName.hashCode();
  }
}

通过使用包装类,您可以以类型安全的方式重新定义相等的概念。请注意您让这些类型交互的方式;您只能将PersonPersonWrapper1PersonWrapper2 的实例与其他相同类型的实例进行比较;如果传入不同的类型,则每个类的 .equals() 方法应返回 false


您还可以查看hashing utilities in Guava,它们提供了几种不同的散列函数,以及BloomFilter 实现,这是一种依赖于能够使用多个散列函数的数据结构。

这是通过将散列函数抽象为Funnel 类来完成的。 Funnel-able 类只需将它们用于相等性的值通过管道传递到Funnel,然后调用者(如BloomFilter)实际计算哈希码。


您的最后一段令人困惑;您不能希望将对象存储在基于散列的数据结构中,然后然后更改用于计算散列码的值。如果您这样做,该对象将不再在数据结构中被发现。

【讨论】:

  • 感谢前两点。最后一个不适用于我的代码,因为值更改后,我将只使用另一个 HashMap。我知道这听起来很奇怪,但它是策略的一部分,HashMaps 只能分区一次。
  • 我还找到了使用 Java 8 进行 Group By 的方法。只是用谷歌搜索它,但我不会删除这个问题,因为我认为你为非 java8 用户提供了一个非常有效的答案.
【解决方案3】:

考虑您的想法:

我想要做的是基于某些属性以非常快速的方式将对象分成多个集合。基本上类似于 SQL GROUP BY 语句,但用于 Java。

Map<City, Set<String>> lastNamesByCity
     = people.stream().collect(groupingBy(Person::getCity,
                                          mapping(Person::getLastName, toSet())));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 2020-01-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多