Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 创建使用自定义条件而不是等于的哈希集_Java_Equals_Hashset - Fatal编程技术网

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