Java 创建使用自定义条件而不是等于的哈希集
下面是我正在使用的类的结构:Java 创建使用自定义条件而不是等于的哈希集,java,equals,hashset,Java,Equals,Hashset,下面是我正在使用的类的结构: public class Foo { private final Bar bar; //Bar is immutable private int state; public Foo(Bar bar, int state) { this.bar = bar; this.state = state; } public Bar getBar() { return bar;
public class Foo {
private final Bar bar; //Bar is immutable
private int state;
public Foo(Bar bar, int state) {
this.bar = bar;
this.state = state;
}
public Bar getBar() {
return bar;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
@Override
public int hashCode() {
return Objects.hash(bar, state);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Foo)) return false;
Foo other = (Foo) obj;
return Objects.equals(bar, other.getBar()) && state == other.getState();
}
}
我遇到的问题如下:
Bar bar = ...;
Foo foo = new Foo(bar, 0);
Set<Foo> set = new HashSet<>();
set.add(foo);
foo.setState(1);
set.remove(foo); //foo is not removed!
为了实现这一点,我设计了以下课程:
public class KeyExtractedHashSet<T, K> extends AbstractSet<T> implements Set<T> {
private final Map<K, T> map;
private final Function<? super T, ? extends K> keyExtractor;
public KeyExtractedHashSet(Function<? super T, ? extends K> keyExtractor) {
map = new HashMap<>();
this.keyExtractor = keyExtractor;
}
@Override
public int size() {
return map.size();
}
@Override
public Iterator<T> iterator() {
return map.values().iterator();
}
@Override
public boolean add(T t) {
return map.put(keyExtractor.apply(t), t) == null;
}
}
公共类KeyExtractedHashSet扩展了AbstractSet实现集{
私人最终地图;
私有最终函数实现自己的集合实现是一项艰巨的工作,容易出错或效率较低。
例如,HashSet
覆盖并优化父类(AbstractCollection
和AbstractSet
)中实现的多个方法
例如,关于包含(对象o)
如何
您继承了AbstractCollection
中定义的具有O(n)时间复杂性的抽象集合,这是正常的,因为抽象集合没有允许在其元素上更快迭代的抽象级别:
public boolean contains(Object o) {
Iterator<E> it = iterator();
if (o==null) {
while (it.hasNext())
if (it.next()==null)
return true;
} else {
while (it.hasNext())
if (o.equals(it.next()))
return true;
}
return false;
}
但由于foo元素存储在map的值中,因此您应该:
public boolean contains(Object o) {
return map.containsValue(o);
}
但是它不会像预期的那样工作,因为Foo.equals()
考虑了要忽略的状态
字段。此外map.containsValue(o)
也不是o(1),因为它可能会在map的所有元素上迭代
删除(对象o)
的问题相同
再举一个例子,想象一下:
Set<Foo> foos = new KeyExtractedHashSet<Foo, Bar>();
// populate, remove...
someFoos
不再是KeyExtractedHashSet
。它很容易被忘记
长话短说:您应该重新考虑您的设计,并按照建议将不可变对象作为哈希集的键。为什么要在对象中包含状态
。哈希()
调用仅在可以更改状态时才使用条
?似乎最简单的解决方案是让hashcode和相等实现不依赖于您不希望它依赖的状态。
public boolean contains(Object o) {
return map.containsValue(o);
}
Set<Foo> foos = new KeyExtractedHashSet<Foo, Bar>();
// populate, remove...
Set<Foo> someFoos = foos.stream().filter(...).toSet();