Java HashMap在哈希更改后删除项目

时间:2019-07-09 20:01:31

标签: java hashmap

我有一个HashMap,其中的键是可变的复杂对象-哈希在其生命周期内发生变化。我确切地知道更改了哪些对象,但是只有在事实之后-使用map.remove(object)将其删除将不起作用,因为哈希已更改。地图中的对象数量大约在[10,10000]范围内,问题在于更改和访问的数量。

在更改对象之前,需要对每个对象进行“是否会更改”检查-将工作加倍,更不用说需要它的一堆代码了。

我稍后会在地图中进行迭代,因此我发现我可以简单地标记要删除的对象并使用iterator.remove()摆脱它们,但不幸的是HashMap$HashIterator#remove调用hash(key)。 / p>

我想到的一个选择是扔掉原始地图,并将所有未标记为要删除的对象重新散列到新地图中,但这会产生大量额外的时间和内存垃圾-希望避免它。

另一种选择是编写我自己的HashMap,以跟踪每个元素的确切存储位置(例如,由2D对象数组形成的地图=两个int坐标)。这样会更高效,但编写和测试也更多。

有什么更简单的方法可以让我错过吗?

编辑:

我对复杂对象使用包装器,这些包装器根据属性的子集提供不同的哈希/等值对。每个对象可能在多个地图中。假设我在地图中寻找 red 对象,该对象使用带有颜色的哈希值/等于值的包装器,创建 red 虚拟对象并执行map.get(dummy)。

哈希/等于的实现以及它们接触的特定属性不属于我的代码。

所有映射都是映射到自身的对象(例如Set实现,但是我确实需要映射访问方法)。我可以将哈希存储在这些包装中,然后从哈希的角度来看,它们将遵守合同,但是平等仍然会让我失望。

我确实知道通过更改哈希/等于输出是未定义的行为,但是从理论上讲,这实际上并不重要-我更改了对象,然后我不想使用该映射,直到更改后的对象脱离它为止。哈希映射实际上不需要为已经使用迭代器指向的对象调用equals()或hash()。

2 个答案:

答案 0 :(得分:0)

  

所有映射都是映射到自身的对象(例如Set实现,但是我确实需要映射访问方法)。我可以将哈希存储在这些包装中,然后从哈希的角度来看,它们将遵守合同,但是平等仍然会让我失望。

正如其他人所说,尝试找到不可变的密钥(例如,生成的密钥或某些不可变属性的子集)或查看其他数据结构,这是不看代码的一些一般建议。

我不太了解为什么可以“将哈希存储在这些包装中”,但是在equals方法上仍然遇到麻烦。 (我想存储的哈希值不会唯一,因此可以在equals方法中检查它们吗?)

但是,如果您具有不可变的哈希值,并且每个“相等”对象只有一个实例(没有一个实例存储在地图中,另一个却是相等的实例用于查找),则可以查看{{3} }类。

答案 1 :(得分:0)

先前状态:

用户提供对复杂对象起作用的equals / hash lambda,以将每个地图放置在正确的位置(在恒定时间内查找相似属性的对象)。

复杂的对象确实在不方便的时间内发生了更改,从而导致重新插入的问题-对象更改,将其拉出,并使用新的哈希将其返回。

当前解决方案:

理论上可以通过自定义实现哈希映射来解决(请注意,非哈希映射接口,将不遵守其约定)。该映射将缓存其内容的哈希值以进行重新哈希处理,并在底层结构中维护坐标,因此对于使用值迭代器进行删除来说,equals是不必要的。稍后可以实施以减少内存占用。

使用过的解决方案迫使用户提供包含所有使用过的属性的密钥,并添加考虑这些属性的哈希/等号。现在,即使复杂的对象发生了变化,它的键也保持不变,直到提示更新为止(更新时不在地图内)。

public class Node {
    public HashMap<Key, Node> map;
    public Data<T> data;
    public Key key;
    public Node parent;
    public void update() {
        if (parent != null) parent.map.remove(key);
        key.update(data);
        if (parent != null) parent.map.put(key, this);
    }
}

public abstract class Key {
    public abstract void update(Data data);
    public abstract int hashCode();
    public abstract boolean equals(Object obj);
}

public class MyKey extends Key {

    private Object value = null;

    public final void update(Data data) {
        value = data.value;
    }

    public final boolean equals(Object obj) {
        IdentityKey that = (IdentityKey)obj;
        return this.value == that.value;
    }

    public final int hashCode() {
        return value == null ? 0 : value.hashCode();
    }
}

这需要很多原始的Key实现,但是至少可以使用。可能会寻找更好的东西。