提问人:diziaq 提问时间:6/19/2019 最后编辑:diziaq 更新时间:6/19/2019 访问量:10918
如何检测列表是否已更改?
How to detect if a list is changed?
问:
我有一个由鲜为人知的专有框架管理的类中的字段。List
注解由框架管理,因此基础列表有时会发生变化:它可以被重新创建,或者它的元素可能会改变。@BindMagic
class SharedEntity{
@BindMagic // this annotation does a magic that we cannot control
private List<Map<String,Object>> values;
public boolean isChangedSincePreviousCall(){
// check if "values" have changed since the previous call of this method
}
}
我同意这是一个糟糕的设计,但让我们假设不可能影响它。
不时(不是在每个突变上)需要检查列表是否更改。例如,我想用 .
也许,像哈希和这样的东西会很好。但我很好奇有没有更好的方法。isChangedSincePreviousCall
检测列表是否已更改的最佳做法是什么?
答:
我会尝试使用对象。下面是一个类示例。您可以对存储在列表中的对象应用相同的操作。PropertyChangeListener
SharedEntity
class SharedEntity {
private List<Map<String,Object>> values;
private PropertyChangeSupport pcs = new PropertyChangeSupport();
public void setValues(List<Map<String,Object>> values) {
List<Map<String,Object>> oldValues = this.values;
this.values= values;
pcs.firePropertyChange("values",oldValues, values);
}
public void addValue(Map<String, Object> value) {
// store old
// add new element
// fire change
}
public void removeValue(Map<String, Object> value) {
// store old
// remove value
// fire change
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
}
}
评论
问题可能出在访问列表的线程上。它很可能不应该被某种侦听器解析所困扰,这就是为什么没有将侦听器附加到列表的正确方法。
但是,如果您可以控制 SharedEntiry 类,则可以使用 synchronized “入侵”列表的访问权限。但是您明确表示,可以重新创建列表,因此我认为存储在后面的实例实际上可以替换。values
基本上你有三种情况:
1 -List 被新 List 替换:values
通过在 List 上进行第二次引用来解决这个问题:
private List<Map<String,Object>> valuesPrevious;
每当检查更改时,请先检查列表的身份。如果它们不匹配,您可以确定列表已更改(至少是实例,如果不是内容)。
if (values != valuesPrevious) {
// handle change.
}
是的,您仍然需要定期检查,但身份比较相对便宜,因此可以在后台运行一个负担得起的线程。
2 -List 将替换为新列表(未设置的类型):values
如果发生这种情况,请将 API 列表中的所有值移动到可观察列表的实例(如下所述),将值设置为该实例,然后等待下一次更改发生。
3 值已更改,但实例相同:
通过使用 ObservableList(如果您在 Java10+ https://docs.oracle.com/javase/10/docs/api/javafx/collections/ObservableList.html 中实现)或自己实现这样的 List(可能通过扩展现有的 List 类型)来解决这个问题。
然后,该侦听器仅设置一个“脏”标志,并且您的方法知道发生了更改(并重置该标志)。
无论如何,我的建议是确保处理更改的线程仅触发另一个线程来处理更改,而不是锁定访问线程,因为我怀疑您的 @BindMagic-API 具有某种与运行时相关的因素(例如,它是与网络或数据库相关的影子)。 如果你只是锁定线程,直到你处理好你的反应,你可能会得到奇怪的效果,断开连接,或者最终意外地阻止了你正在访问的服务器。
评论
使用哈希值不是确定的,因为相同的哈希值可以从不同的输入中产生,尽管几率很小。
“被改变”和“与众不同”意味着不同的东西。考虑其中一个映射中的条目,该条目在调用方法之间从 to 更改,然后返回 again - 它已更改,但并无不同。我假设你的意思是“不同”。"A" -> 1
"A" -> 2
"A" -> 1
检查时制作副本并将其与当前状态进行比较。假设映射值是不可变的:
class SharedEntity {
@BindMagic
private List<Map<String, Object>> values;
private List<Map<String, Object>> valuesCopy;
public boolean isChangedSincePreviousCall() {
newCopy = new ArrayList<>(values);
boolean result = !Objects.equals(valuesCopy, newCopy);
valuesCopy = newCopy;
return result;
}
}
如果 Map 值是(或包含)可变对象,则在创建副本时必须对它们进行深层复制。
仅供参考,如果两个参数都为 null,则返回 Objects#equals()。
true
您可以使用 Observer Pattern 来检测 中的更改。values
您需要创建 Observable。
package com.psl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
public class MyList extends Observable{
private List<Map<String,Object>> values;
public List<Map<String, Object>> getValues() {
return values;
}
public void setValues(List<Map<String, Object>> values) {
if(getValues()==null && values!=null){
setChanged();
notifyObservers();
}
else if( !this.values.equals(values)){
setChanged();
notifyObservers();
}
this.values = values;
}
public static void main(String[] args) {
MyList myList = new MyList();
List<Map<String, Object>> values = new ArrayList<Map<String, Object>>();
Notify notify = new Notify();
myList.addObserver(notify);
Map<String, Object> map = new HashMap<String, Object>();
map.put("string_value", null);
myList.setValues(values);
}
}
您必须创建观察器,该观察器将观察 MyList 中的更改
package com.psl;
import java.util.Observable;
import java.util.Observer;
public class Notify implements Observer{
@Override
public void update(Observable o, Object arg) {
System.out.println("List has been changed");
}
}
有关可观察模式的更多信息,请 https://springframework.guru/gang-of-four-design-patterns/observer-pattern/
上一个:具有可变成员字段的不可变类
评论
@BindMagic
values
lastSeenValue
Object