0

Lets say I have a list of objects List<Person> people. This list is composed of multiple Person objects - each has a name and age (and also implements the respective equals and hashCode methods)

The list is populated with multiple (different in memory) objects that are (same in values)

List<Person> people = new ArrayList<>();
Person person_1 = new Person("Tom", 21);
Person person_2 = new Person("Tom", 21);
Person person_3 = new Person("Tom", 21);

person_1.equals(person_2) // true
person_1 == person_2      // false

people.add(person_1);
people.add(person_2);
people.add(person_3);

The problem I have is that people.remove(person_1) removes person_2 and person_3 also.

What can I do to change this?

AlanSTACK
  • 5,525
  • 3
  • 40
  • 99
  • There is nothing you can do to change it. It is part of the contract of the `List` interface that the `remove()` method removes the first element that `equals()` its argument. Every proper `List` implementation will do that, and especially the ones included in the standard library will do. It is not configurable. – John Bollinger Oct 30 '17 at 20:28
  • @JohnBollinger is it possible to specify a lambda equal function to use for the comparison? – AlanSTACK Oct 30 '17 at 20:29
  • What you should be asking yourself is whether it makes sense for your `Person` class to implement `equals()` such that objects that you want to distinguish are found `equals()` to each other. – John Bollinger Oct 30 '17 at 20:30
  • @JohnBollinger I need to distinguish by `object id` just for this one particular function... – AlanSTACK Oct 30 '17 at 20:32
  • "I need it just for this one particular function" still means "I need it". Anyway, no single method of the `List` interface will do what you want, so if you want to continue with the class you're using, as it stands, then your best bet is probably to iterate over the list elements until you find one that satisfies your predicate, then remove that by index or by `Iterator.remove()`. – John Bollinger Oct 30 '17 at 20:39
  • How about `Collections.newSetFromMap(new IdentityHashMap<>())`? It won't preserve ordering, but it will distinguish items by identity. – shmosel Oct 30 '17 at 21:18

1 Answers1

1

From the Java Documentation on List

boolean remove(Object o);

Removes the first occurrence of the specified element from this list, if it is present (optional operation). If this list does not contain the element, it is unchanged. More formally, removes the element with the lowest index i such that (o==null ? get(i)==null : o.equals(get(i)))


As far as == vs. .equals @brainydexter explained it pretty well here.

Essentially,

== is a comparison of references, i.e. both objects point to the same memory location.

.equals evaluates to the comparison of values in the objects

Since .remove() on a List<> object boils down to using .equals() it will only check for value comparisons, so any object with matching values will evaluate to being equal.

A few work arounds I can come up with off the top of my head:

  • Come up with an Identifier Injection method.

    1. Have a private variable inside of the Person() class.
    2. Have a global reference stored somewhere for tracking Person() identifiers.
    3. When a new Person() is instantiated, use that global tracking variable to determine the identifer for the new Person() and apply it to it's private identifier.
    4. Implement equals() in your Person() class and return the comparison of the private identifiers.

      • This way, each Person() object will be unique and .equals() will not consider them the same.
      • The main issue I could see with this method include overly complicating the identifier tracking for the objects. This seems unnessecary with such simple objects.
  • Remove a similar object

    1. Iterate over the list until you find only one object that matches the one you'd like to remove.
    2. Remove it based on it's index in the list.
      • The main issue with this is that your code becomes nonsense. You're asking to remove person2, but you could run .remove(person2) three times and all would succeed as the it would remove the first element that matches your input object each time it's run. So, person2 would become person1 would become person3 under the scope of your custom .remove() function.
Nathan F.
  • 3,250
  • 3
  • 35
  • 69