IdentitySet / IdentityHashSet(使用IdentityHashMap)

20

我了解 IdentityHashMap,但我需要使用类似于 "IdentitySet" 的东西(使用 equals 作为 o1 == o2)。我打算使用其中一个来监听带有“提取器”(JavaFX)的可观察列表:

 List<Person> deleteList = new ArrayList<>();
 List<Person> addList = new ArrayList<>();

 ObservableList<Person> list = FXCollections.observableArrayList(Person.extractor());
    list.add(new Person("a",1));
    list.add(new Person("b",2));
    list.add(new Person("c",3));

    list.addListener((ListChangeListener<Person>) observable -> {
        if(observable.next()) {
            if (observable.wasAdded()) {
                addList.addAll(observable.getAddedSubList());
            }
            if (observable.wasUpdated()) {
                deleteList.add(list.get(observable.getFrom()));
            }
            if (observable.wasRemoved()) {
                deleteList.addAll(observable.getRemoved());
            }
        }
    });

人类:

public class Person {
private final StringProperty s;
private final DoubleProperty d;

//Getters and Setters
// ...

@Override
public int hashCode() {
    int result = s != null ? s.getValue().hashCode() : 0;
    result = 21 * result + (d != null ? Double.hashCode(d.getValue()) : 0);
    return result;
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Person person = (Person) o;
    if (!s.getValue().equals(person.s.getValue())) return false;
    return d.getValue().equals(person.d.getValue());
}


/**
 *Extractor to observe changes in "Property" fields.
 * @return extractor
 */
public static Callback<Person, Observable[]> extractor() {
    return (Person p) -> new Observable[]{p.sProperty(), p.dProperty()};
}

我需要覆盖equalshashCode来解决我的其他问题。

Person p = new Person("a",1);
Set<Object> persons = new HashSet<>();
persons.add(p);
p.setD(999);
persons.add(p);
System.out.println(persons.size());  // size = 2;

persons.remove(p);
System.out.println(persons.size());  // size = 1;

3
Collections.newSetFromMap 是一个静态方法,用于创建一个基于指定映射的新 Set。返回的 Set 是可序列化和线程安全的,并且支持所有 Set 操作,除非在其底层映射上禁用了相应操作。 - Boris the Spider
你想在Set中存储什么?如果你存储的是一个没有重写equals()方法的类的对象,那么HashSet将会和IdentityHashSet做相同的事情,因为默认的equals()实现只有在两个对象是相同的时候才返回true。 - JB Nizet
1
我必须覆盖。 - kozmo
2个回答

33

从映射创建集合

谢谢,@BoristheSpider

Collections.newSetFromMap(...) 可以返回一个基于指定映射(在我的情况下是 IdentityHashMap)的 Set

// get IdentytitySet wich wrap IdentityHashMap
Set<Person> persons = Collections.newSetFromMap(new IdentityHashMap<>()) 

// test 
Person p  = new Person("a",1);
Person p2 = new Person("a",1);
persons.add(p);
persons.add(p2);
System.out.println(persons.toString());

标准输出(漂亮打印):

[
  Person{
    s=StringProperty [value: a],
    d=DoubleProperty [value:1.0]
  }, 
  Person{
    s=StringProperty [value: a], 
    d=DoubleProperty [value: 1.0]
  }
]
p.setD(999);
persons.add(p);
System.out.println(persons.toString());

标准输出(漂亮打印):

[
  Person{
    s=StringProperty [value: a],
    d=DoubleProperty [value: 999.0]
  }, 
  Person{
    s=StringProperty [value: a], 
    d=DoubleProperty [value: 1.0]
  }
]
persons.remove(p)
System.out.println(persons.toString());

标准输出(美观的打印):

[
  Person{
    s=StringProperty [value: a], 
    d=DoubleProperty [value: 1.0]
  }
]

来自javadoc的附加信息:“生成的集合显示与支持映射相同的排序,并发性和性能特征。本质上,此工厂方法提供了与任何Map实现对应的Set实现。” - armandino

10

3
Guava本身建议在存在此选项的情况下优先使用Java类,我认为Collections.newSetFromMap是更好的选择。 - Boris the Spider
Set<Person> set = Collections.newSetFromMap( new IdentityHashMap<>()); 很棒! - kozmo
@kozmo 可以自己回答问题,尽管放心 - 这将会为你赚取一些声望,并且可能还会得到一些赞同;)。 - Boris the Spider
1
@BoristheSpider Guava只是使用那个方法而已,它只是一个门面。 - Tom
4
@BoristheSpider认为Guava版本更易于阅读(在我看来)。 Collections 版本也不难,但不太明显。 Guava清楚地说明了你得到什么(一个身份集),而且你不必担心这是如何构建的。 Java版本在这方面有点啰嗦。对于有经验的程序员来说,这肯定不是问题,但该代码可能不仅适用于有经验的人。 - Tom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接