如何检测列表是否已更改?

时间:2019-06-19 06:34:03

标签: java hash mutable

我在一个由鲜为人知的专有框架管理的类中有一个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来做。 可能,像哈希和这样的东西会很好。但是我很好奇还有更好的方法。

检测列表是否更改的最佳实践是什么?

4 个答案:

答案 0 :(得分:1)

使用哈希值不是确定的,因为可以从不同的输入产生相同的哈希值,尽管机会很小。

“被改变”和“与众不同”意味着不同的事物。考虑一下其中一个映射中的条目,该条目从"A" -> 1更改为"A" -> 2,然后在调用方法之间再次返回"A" -> 1-已被更改,但没有't 不同。我假设你的意思是“不同”。

检查时制作一个副本,并将其与当前状态进行比较。假设映射值为immutable

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值是(或包含)可变对象,则在创建副本时必须对其进行深层复制。

如果两个参数都为空,则FYI Objects#equals()返回true

答案 1 :(得分:0)

我会尝试使用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);
    }
}

答案 2 :(得分:0)

问题可能是线程正在访问列表。很有可能不应该以某种侦听器分辨率来解决它,这就是为什么没有适当的方法将侦听器附加到列表的原因。

但是,如果您可以控制SharedEntiry类,则可以使用同步来“侵入”列表的访问权限。但是,您明确指出,可以重新创建该列表,因此我假设存储在values后面的实例实际上可以被替换。

基本上,您有以下三种情况:

1 values-列表已替换为新列表:

通过在List上进行第二次引用来解决此问题:

private List<Map<String,Object>> valuesPrevious;

每当您检查更改时,请首先检查列表的身份。如果它们不匹配,则可以确定列表已更改(至少是实例,如果不是内容的话)。

if (values != valuesPrevious) {
    // handle change.
}

是的,您仍然需要定期检查,但是身份比较相对便宜,因此可以在后台运行负担得起的线程。

2 values-列表被新的列表(您未设置的类型)替换:

如果发生这种情况,请将所有值从API列表移至可观察列表的一个实例(如下所述),为该实例设置值,然后等待下一次更改。

3值已更改,但实例相同:

使用ObservableList(如果您使用Java10 + https://docs.oracle.com/javase/10/docs/api/javafx/collections/ObservableList.html实现)或自己实现这样的List(可能通过扩展现有的List类型)来解决。

然后,该侦听器仅设置一个“脏”标志,并且您的方法知道发生了更改(并重置了标志)。

无论如何,我的建议是确保处理更改的线程仅触发另一个线程来处理更改,而不是锁定访问线程,因为我怀疑您的@ BindMagic-API具有某种运行时相关因素(例如,它是某物与网络或数据库相关的影子)。 如果只是简单地锁定线程,直到处理完您的反应,您可能会得到怪异的效果,断开连接或最终意外阻塞正在访问的服务器。

答案 3 :(得分:0)

您可以使用观察者模式来检测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/

的更多信息