不同的集合基于不同的equals/hashcode



我希望有两个Set集合,它们包含基于相同类型对象的equals/hashCode的不同实现的唯一对象。

例如,一个Set将仅用于具有不同时间戳的元素,而equals应返回a.timestamp!=b.timestamp,但另一个集合可能仅用于具有相同名称(如a.name!=b.name)的元素。

有没有一种微不足道的方法可以实现这一点?如何为每个不同的Set自定义相等?

您的建议听起来像是对equals方法和Set接口的滥用。

您的用例定义了两个类别,给定类型的对象可以根据这两个类别进行分组(时间戳和名称),并且您希望有两个集合,每个集合最多包含每个类别的一个实例。

要做到这一点,拥有辅助集会更有意义:

Set<String> timestamps;
Set<String> names;

然后,您可以将名称和时间戳添加到这些集合中,并使用它们来检查是否应将对象添加到目标集合(不必是集合)中。

例如:

List<SomeClass> timestampCategories = new ArrayList<SomeClass>();
List<SomeClass> nameCategories = new ArrayList<SomeClass>();
for (SomeClass object : inputCollection) {
    if (!timestamps.contains(object.getTimestamp()) {
        timestamps.add(object.getTimestamp());
        timestampCategories.add(object);
    }
    if (!names.contains(object.getName()) {
        names.add(object.getName());
        nameCategories.add(object);
    }
}

创建两个包装值类,并将equalshashCode实现放在那里。

    public class Test {

    Set<NameBeanWrapper> names = new HashSet<Test.NameBeanWrapper>();
    Set<TimestampBeanWrapper> timestamps = new HashSet<Test.TimestampBeanWrapper>();
    public void store(Set<Bean> initial) {
      for (Bean bean : initial) {
        names.add(new NameBeanWrapper(bean));
        timestamps.add(new TimestampBeanWrapper(bean));
      }
    }
    public static class Bean {
      String name;
      Long timestamp;
      public String getName() {
        return name;
      }
      public void setName(String name) {
        this.name = name;
      }
      public Long getTimestamp() {
        return timestamp;
      }
      public void setTimestamp(Long timestamp) {
        this.timestamp = timestamp;
      }
    }
    public static class NameBeanWrapper {
      final Bean bean;
      public NameBeanWrapper(Bean bean) {
        this.bean = bean;
      }
      @Override
      public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((bean == null || bean.name == null) ? 0 : bean.name.hashCode());
        return result;
      }
      @Override
      public boolean equals(Object obj) {
        if (this == obj) {
          return true;
        }
        if (obj == null) {
          return false;
        }
        if (getClass() != obj.getClass()) {
          return false;
        }
        NameBeanWrapper other = (NameBeanWrapper) obj;
        if (bean == null) {
          if (other.bean != null) {
            return false;
          }
        }else if (bean.name == null) {
            if (other.bean.name != null) {
              return false;
            }
        } else if (!bean.name.equals(other.bean.name)) {
          return false;
        }
        return true;
      }
    }
    public static class TimestampBeanWrapper {
      final Bean bean;
      public TimestampBeanWrapper(Bean bean) {
        this.bean = bean;
      }
      @Override
      public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((bean == null || bean.timestamp == null) ? 0 : bean.timestamp.hashCode());
        return result;
      }
      @Override
      public boolean equals(Object obj) {
        if (this == obj) {
          return true;
        }
        if (obj == null) {
          return false;
        }
        if (getClass() != obj.getClass()) {
          return false;
        }
        NameBeanWrapper other = (NameBeanWrapper) obj;
        if (bean == null) {
          if (other.bean != null) {
            return false;
          }
        }else if (bean.timestamp == null) {
            if (other.bean.timestamp != null) {
              return false;
            }
        } else if (!bean.timestamp.equals(other.bean.timestamp)) {
          return false;
        }
        return true;
      }
    }
  }

如建议:

您想同时拥有同一对象的两个列表吗?为什么不创建一个包含所有所需元素的父object,然后实现两个对象,而不是使用不同的equals和hash从第一个对象继承呢?

好的,我想知道是否有一种简单的方法可以指定要在Set中使用的equals/hashcode。我看没人听说过这样的事。

如果无法做到这一点,我可以始终使用带有自定义哈希码的Map作为键,然后当我需要获得Collection时,我会获得Map.values()。我想避免这种情况,因为为了保持低内存使用率,但也许这是方法。。。

最新更新