Проблема с ConcurrentSkipListSet remove()

Я не уверен, есть ли проблема с java.util.concurrent.ConcurrentSkipListSet? Я пытаюсь добавить некоторые объекты в ConcurrentSkipListSet (порядок поддерживается моим собственным компаратором). После добавления я изменяю состояние некоторых объектов. Свойства, которые я изменяю, включают те, которые используются в компараторе. Теперь, когда я пытаюсь удалить некоторые объекты, происходит сбой. Объект не удаляется из ConcurrentSkipListSet и remove(Object) возвращает ложь

Если я заменю ConcurrentSkipListSet на TreeSet, я не вижу этого поведения.

Не уверен, что я делаю что-то действительно глупое или что-то упустил:(. Вот пример кода.

public class TreeVsSkip {
static TreeSet ts = new TreeSet(new Comparator(){

    @Override
    public int compare(Object o1, Object o2) {
        if(((Emp)o1).empid == ((Emp)o2).empid){
            return 0;
        }
        if(((Emp)o1).empid > ((Emp)o2).empid){
            return 1;
        }
        return -1;
    }

});

static ConcurrentSkipListSet<Emp> csls = new ConcurrentSkipListSet(new Comparator(){

    @Override
    public int compare(Object o1, Object o2) {
        if(((Emp)o1).empid == ((Emp)o2).empid){
            return 0;
        }
        if(((Emp)o1).empid > ((Emp)o2).empid){
            return 1;
        }
        return -1;
    }

});
public static void main(String ...strings ){
    System.out.println("Testing Tree...");
    Emp e1 = new Emp(1,"abc");

    ts.add(e1);
    ts.add(new Emp(2,"pqr"));
    ts.add(new Emp(3,"xyz"));
    System.out.println(ts);
    e1.setName("test");
    e1.setId(8);
    System.out.println(ts);
    ts.remove(new Emp(3,"xyz"));
    System.out.println(ts);


    System.out.println("Testing ConcurrentSkipSet...");
    e1.setName("abc");
    e1.setId(1);
    csls.add(e1);
    csls.add(new Emp(2,"pqr"));
    csls.add(new Emp(3,"xyz"));
    System.out.println(csls);
    e1.setName("test");
    e1.setId(8);
    System.out.println(csls);
    System.out.println(csls.remove(new Emp(3,"xyz")));
    System.out.println(csls);
}

static class Emp {
    int empid;
    String name;
    Emp(int id, String n){
        empid = id;
        name = n;
    }
    void setName(String pname){
        name = pname;
    }
    void setId(int pID){
        empid = pID;
    }
    public String toString(){
        return "EmpId:"+empid+"Name:"+name;
    }
}

}

Вывод выглядит так:

Testing Tree...
[EmpId:1Name:abc, EmpId:2Name:pqr, EmpId:3Name:xyz]
[EmpId:8Name:test, EmpId:2Name:pqr, EmpId:3Name:xyz]
[EmpId:8Name:test, EmpId:2Name:pqr]
Testing ConcurrentSkipSet...
[EmpId:1Name:abc, EmpId:2Name:pqr, EmpId:3Name:xyz]
[EmpId:8Name:test, EmpId:2Name:pqr, EmpId:3Name:xyz]
false
[EmpId:8Name:test, EmpId:2Name:pqr, EmpId:3Name:xyz]

Обратите внимание, что это поведение не соответствует. Иногда элемент удаляется.

Я использую версию Java "1.8.0_131" на OS X версии 10.11.6.

Извиняюсь за потертый код. Приготовил это в спешке.

Благодарю.

1 ответ

Когда вы добавляете элемент в набор или используете его в качестве ключа на карте, вы не можете изменить любое поле, которое используется для сравнения, например, CompareTo или Comparator или hashCode / equals, в зависимости от ситуации, или вы испортили коллекцию.

Единственный способ удалить такие элементы - это перебрать все элементы и удалить их через итератор.

Кстати, я бы не использовал статическую коллекцию, но если вам нужно, вы можете сделать

static final Set<Emp> csls = new ConcurrentSkipListSet(
                                            Comparator.comparing(e -> e.empid));

Если вы собираетесь изменить empid вам нужно будет сначала удалить его, изменить его, а затем добавить обратно. Чтобы избежать этого с помощью acdient, я бы сделал поле final установка его только в конструкторе.

Другие вопросы по тегам