英文:
How to update HashMap key from value in a class?
问题
我正在尝试动态更新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); // 更新类中的值
System.out.println(hashMap.keySet()); // 输出 5 而不是 0
}
}
**其他类**
class OtherClass {
int id;
OtherClass (int id) {
this.id = id;
}
void setId(int id) {
this.id = id;
}
int getId() {
return id;
}
}
当我更新类中的值时,HashMap中的键不会改变。
我正在尝试的这个操作是否可能?如果不可能,我该如何实现这一点?
英文:
I am trying to dynamically update the keys in a HashMap.
I have created an instance of a class and set the key to get the value from the class.
I am trying to get the value of the key to change when I update the value in the class. The program I am trying to do this for has multiple large hashmaps. However, I have simplified it to the example below.
Main class
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
}
}
Other class
class OtherClass {
int id;
OtherClass (int id) {
this.id = id;
}
void setId(int id) {
this.id = id;
}
int getId() {
return id;
}
}
When I update the value in the class the key in the HashMap does not change.
Is what I'm trying to do even possible and if not how could I achieve this?
答案1
得分: 4
以下是您要求的翻译内容:
如果您希望在修改“OtherClass”对象的“id”时“Map”自动更新,则需要自己编写该代码。
除非地图和对象紧密耦合,否则应保持逻辑解耦,例如通过实现属性更改跟踪。
我建议围绕Java运行时库中的PropertyChangeSupport
类构建它。
<h3>OtherClass</h3>
首先,您需要启用属性更改跟踪。
我添加了“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 + "]";
}
}
<h3>OtherMap</h3>
接下来,您需要封装“Map”,以便正确处理属性更改侦听器。
为了防止内存泄漏,当不再需要“OtherMap”时,重要的是要“清除(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();
}
}
<h3>Test</h3>
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]}
英文:
If you want the Map
to auto-update when the id
of an OtherClass
object is modified, then you need to write the code for that yourself.
Unless the map and the object is tightly coupled, you should keep the logic decoupled, e.g. by implementing property change tracking.
I would recommend building it around the PropertyChangeSupport
class in the Java Runtime Library.
<h3>OtherClass</h3>
First you need to enable property change tracking.
I added name
property to improve test code output at end of this answer.
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 + "]";
}
}
<h3>OtherMap</h3>
Next you need to encapsulate the Map
so the property change listener can be correctly handled.
To prevent memory leaks, it's important to clear()
the OtherMap
when it is no longer needed, otherwise a reference to a single OtherMap
object that is in the OtherMap
will keep the map and all the objects in the map alive in memory. To help with that, I made the object AutoCloseable
, so it could be used with a try-with-resources statement, and to make code analyzers help highlight the need to close/clear the map.
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();
}
}
<h3>Test</h3>
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);
Output
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]}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论