【问题标题】:How to update HashMap key from value in a class?如何从类中的值更新 HashMap 键?
【发布时间】:2020-06-26 12:04:05
【问题描述】:

我正在尝试动态更新 HashMap 中的键。

我已经创建了一个类的实例并设置了从该类中获取值的键。

我试图在更新类中的值时更改键的值。我正在尝试执行此操作的程序有多个大型哈希图。但是,我已将其简化为下面的示例。

主类

import java.util.HashMap;

public class Main {

    public static void main(String[] args) {
        HashMap<Integer, String> hashMap = new HashMap<>();
        OtherClass otherClass = new OtherClass(5);

        hashMap.put(otherClass.getId(), "a string");
        otherClass.setId(0);   // update value in class

        System.out.println(hashMap.keySet());   // outputs 5 not 0
    }
}

其他类

class OtherClass {
    int id;

    OtherClass (int id) {
        this.id = id;
    }

    void setId(int id) {
        this.id = id;
    }

    int getId() {
        return id;
    }
}

当我更新类中的值时,HashMap 中的键不会改变。

我正在尝试做的事情是否可能,如果不是,我怎么能做到这一点?

【问题讨论】:

  • HashMap 不会以这种方式重新调整它们的键。键是 5,因为这是您将值添加到 Map 时的 ID。
  • 快速搜索显示似乎没有满足此条件的函数,而是removing and re-inserting;具有良好的哈希值,无论如何应该是O(1)。好问题。

标签: java hashmap


【解决方案1】:

如果您希望 MapOtherClass 对象的 id 被修改时自动更新,那么您需要自己编写代码。

除非地图和对象紧密耦合,否则您应该保持逻辑解耦,例如通过实施属性更改跟踪。

我建议围绕 Java 运行时库中的 PropertyChangeSupport 类构建它。


其他类

首先您需要启用属性更改跟踪。

我添加了name 属性以改进此答案末尾的测试代码输出。

public final class OtherClass {
    private final transient PropertyChangeSupport pcs = new PropertyChangeSupport(this);
    private int id;
    private String name;

    public OtherClass(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        this.pcs.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        this.pcs.removePropertyChangeListener(listener);
    }

    public int getId() {
        return this.id;
    }

    public void setId(int id) {
        int oldId = this.id;
        this.id = id;
        this.pcs.firePropertyChange("id", oldId, id);
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        String oldName = this.name;
        this.name = name;
        this.pcs.firePropertyChange("name", oldName, name);
    }

    @Override
    public String toString() {
        return "OtherClass[" + this.id + ", " + this.name + "]";
    }
}

其他地图

接下来需要封装Map,这样才能正确处理属性变化监听器。

为了防止内存泄漏,当不再需要 clear()OtherMap 时很重要,否则对 OtherMap 中的单个 OtherMap 对象的引用将保留映射和所有对象在地图上活在记忆中。为了解决这个问题,我创建了对象 AutoCloseable,因此它可以与 try-with-resources 语句一起使用,并使代码分析器有助于突出关闭/清除地图的需要。

final class OtherMap implements AutoCloseable {
    private final PropertyChangeListener listener = this::onPropertyChange;
    private Map<Integer, OtherClass> map = new HashMap<>();

    public OtherMap() {
    }

    public Set<Integer> keys() {
        return Collections.unmodifiableSet(this.map.keySet());
    }

    public Collection<OtherClass> values() {
        return Collections.unmodifiableCollection(this.map.values());
    }

    public OtherClass get(int id) {
        return this.map.get(id);
    }

    public OtherClass add(OtherClass other) {
        OtherClass prev = this.map.put(other.getId(), other);
        if (prev != null)
            prev.removePropertyChangeListener(this.listener);
        other.addPropertyChangeListener(this.listener);
        return prev;
    }

    public OtherClass remove(int id) {
        OtherClass other = this.map.remove(id);
        if (other != null)
            other.removePropertyChangeListener(this.listener);
        return other;
    }

    public void clear() {
        this.map.values().forEach(other -> other.removePropertyChangeListener(this.listener));
        this.map.clear();
    }

    private void onPropertyChange(PropertyChangeEvent evt) {
        if (! "id".equals(evt.getPropertyName()))
            return;
        Integer oldId = (Integer) evt.getOldValue();
        Integer newId = (Integer) evt.getNewValue();
        if (oldId.equals(newId))
            return;
        OtherClass other = (OtherClass) evt.getSource();
        if (this.map.putIfAbsent(newId, other) != null)
            throw new IllegalStateException("Duplicate key");
        if (! this.map.remove(oldId, other)) {
            this.map.remove(newId);
            throw new IllegalStateException();
        }
    }

    @Override
    public String toString() {
        return this.map.toString();
    }

    @Override
    public void close() {
        clear();
    }
}

测试

OtherClass eeny  = new OtherClass(3, "Eeny");
OtherClass meeny = new OtherClass(5, "Meeny");
OtherClass miny  = new OtherClass(7, "Miny");
OtherClass moe   = new OtherClass(9, "Moe");

OtherMap otherMap = new OtherMap();
otherMap.add(eeny);
otherMap.add(meeny);
otherMap.add(miny);
otherMap.add(moe);
System.out.println("Before: " + otherMap);

meeny.setId(2);
otherMap.remove(miny.getId());
miny.setId(4);
System.out.println("After: " + otherMap);

输出

Before: {3=OtherClass[3, Eeny], 5=OtherClass[5, Meeny], 7=OtherClass[7, Miny], 9=OtherClass[9, Moe]}
After: {2=OtherClass[2, Meeny], 3=OtherClass[3, Eeny], 9=OtherClass[9, Moe]}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-04-11
    • 2016-02-07
    • 1970-01-01
    • 1970-01-01
    • 2015-08-05
    • 1970-01-01
    • 2011-10-09
    • 2023-03-09
    相关资源
    最近更新 更多