Как определить, изменился ли список? - PullRequest
2 голосов
/ 19 июня 2019

У меня есть поле 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 ]

1 голос
/ 19 июня 2019

Использование хэша не является окончательным, поскольку один и тот же хэш может быть получен из разных входных данных, хотя и с очень малой вероятностью.

«Быть ​​измененным» и «быть другим» означают разные вещи. Рассмотрим запись в одной из карт, которая изменяется с "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;
    }
}

Если значения карты являются (или содержат) изменяемые объекты, вам придется делать их глубокую копию при создании копии.

К вашему сведению Objects#equals() возвращает true, если оба параметра равны нулю.

0 голосов
/ 19 июня 2019

Вы можете использовать Шаблон наблюдателя для обнаружения изменений в 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/

0 голосов
/ 19 июня 2019

Возможно, проблема в том, что поток обращается к списку. Скорее всего, он не должен быть захвачен каким-либо разрешением для прослушивателя, поэтому не существует надлежащего способа присоединить слушателя к списку.

Однако, если у вас есть контроль над классом SharedEntiry, вы можете «взломать» доступ к списку, используя синхронизированный. Однако вы прямо заявили, что список может быть воссоздан, поэтому я предполагаю, что экземпляр, сохраненный за values, действительно может быть заменен.

В основном у вас есть три случая:

1 Список values заменен новым списком:

Решите это, сделав вторую ссылку в Списке:

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 имеет своего рода фактор, связанный со временем выполнения (например, это тень сети или базы данных). Если вы просто заблокируете поток, пока не обработаете свою реакцию, вы можете получить странные эффекты, отключиться или в итоге случайно заблокировать сервер, к которому вы обращаетесь.

0 голосов
/ 19 июня 2019

Я бы попробовал использовать 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);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...