17

I know about IdentityHashMap, but I need to use to something like "IdentitySet" (use equals as o1 == o2 ). I'm going to use one to listen Observable list with "extractor" (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());
            }
        }
    });

Person class:

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()};
}

I need to override equals and hashCode to solve my other issues.

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;
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
kozmo
  • 4,024
  • 3
  • 30
  • 48
  • 3
    [`Collections.newSetFromMap`](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#newSetFromMap-java.util.Map-) – Boris the Spider Jan 03 '18 at 18:24
  • What do you want to store in your Set? If you store objects of a class that doesn't override equals(), then a HashSet will do the same thing as an IdentityHashSet, since the default implementation of equals() returns true only if the two objects are the same. – JB Nizet Jan 03 '18 at 18:50
  • 1
    I have to override. – kozmo Jan 03 '18 at 20:24

2 Answers2

31

Make a set from a map

thx, @BoristheSpider

Collections.newSetFromMap(...), returns a Set backed by the specified map (in my case ➡️ IdentityHashMap):

// 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());

stdout (pretty print):

[
  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());

stdout (pretty print):

[
  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());

stdout (pretty print):

[
  Person{
    s=StringProperty [value: a], 
    d=DoubleProperty [value: 1.0]
  }
]
kozmo
  • 4,024
  • 3
  • 30
  • 48
  • Additional info from the javadoc: "The resulting set displays the same ordering, concurrency, and performance characteristics as the backing map. In essence, this factory method provides a Set implementation corresponding to any Map implementation." – armandino Nov 29 '22 at 17:54
9

You can use Guava's Sets.newIdentityHashSet().

https://github.com/google/guava/wiki/CollectionUtilitiesExplained

kichik
  • 33,220
  • 7
  • 94
  • 114
  • 3
    Guava themselves recommend using Java classes in preference to their own where such an option exists. I think `Collections.newSetFromMap` is a better option. – Boris the Spider Jan 03 '18 at 18:26
  • **thx** -> `Set set = Collections.newSetFromMap( new IdentityHashMap<>());` work cool! – kozmo Jan 04 '18 at 11:42
  • @kozmo feel free to [self answer](https://stackoverflow.blog/2011/07/01/its-ok-to-ask-and-answer-your-own-questions/) - you'll get a bit of rep, and maybe some upvotes too ;). – Boris the Spider Jan 04 '18 at 12:37
  • 1
    @BoristheSpider Guava just uses that method anyway, it is just a facade. – Tom Jan 04 '18 at 17:39
  • 4
    @BoristheSpider Guavas version is easier to read (imo). The `Collections` version isn't hard either, but is a bit less obvious. Guava says exactly what you get (a identity set) and you don't have to mind how this is build. The Java version is a bit more verbose on that part. I surely isn't any issue for an experienced programmer, but the code might not only be available for the experienced ones. – Tom Jan 05 '18 at 16:03