0

I have an object of type

Couple(String person1, String person2),

and an ArrayList<Couple> relationshipList = new ArrayList<Couple>(); that has various items of Couple type, where all Couple objects are duplicated once in the list. For example, this is my sample arrayList:

relationshipList.add(new Couple("John", "Amy"));
relationshipList.add(new Couple("Eliot", "Megan"));
relationshipList.add(new Couple("Billy", "Rachel"));
relationshipList.add(new Couple("Amy", "John"));
relationshipList.add(new Couple("Jim", "Kate"));
relationshipList.add(new Couple("Kate", "Jim"));
relationshipList.add(new Couple("Megan", "Eliot"));
relationshipList.add(new Couple("Rachel", "Billy"));

I'm trying to find a way to remove the duplicate couples, because as in this example, John and Amy are the same couple added twice in the list, with just their names swapped in the column.(assuming two people with the same names doesn't exist in this scenario and John refers to only the "John and Amy" couple) Can anyone help me on this?

  • 1
    Is there an implicit `new Couple(` in front of all of those strings in your add statement? – Makoto May 14 '18 at 19:33
  • Originally, yes I have them. I was trying to keep it short and simple, so added a sample list.This is how I'm really doing it: `private void saveRelationshipList(String firstPerson, String secondPerson, String relationshipValue) throws Exception { relationshipList.add(new FamilialRelations(firstPerson, secondPerson, relationshipValue)); } ` – StephGonnaSteph May 14 '18 at 19:38
  • can you alter your `Couple` class? add equals/hashCode? – Eugene May 14 '18 at 19:41
  • Possible duplicate of [Java Lambda Stream Distinct() on arbitrary key?](https://stackoverflow.com/questions/27870136/java-lambda-stream-distinct-on-arbitrary-key) – Imaskar May 14 '18 at 19:42
  • 2
    @Imaskar who said that OP searching for lambda approach? – J-Alex May 14 '18 at 19:44
  • He looks for any approach and one is already present. – Imaskar May 14 '18 at 19:47

4 Answers4

0

You may

  1. override equals() method to compare your objects as you want. Then

    relationshipList.stream().distinct().collect(Collectors.asList());
    
  2. make a custom filter class, which holds a map of encountered values. Then

    relationshipList.stream().filter(yourFilter::compare).collect(Collectors.asList());
    
Imaskar
  • 2,773
  • 24
  • 35
  • Yes, I agree. But I'm not able to understand how that loop would go because I cannot delete an object from the list while iterating. Suppose I compare the first item (of my first loop) that is "john and amy" and iterate through another loop to find "amy and john", then I'll find them once but on my 4th iteration my "amy and john" would be found again as the first item in the second loop. It keeps getting redundant. I might be missing out something really stupid, but I'm really stuck here. – StephGonnaSteph May 14 '18 at 19:46
  • Why filter ? don't see where it's asked – azro May 14 '18 at 19:46
  • 1
    Don'r delete, you will have a new list with only distinct values as a result. you may just discard the original list if it is not needed. – Imaskar May 14 '18 at 19:49
0

You firstly need to implement equals method for couple like below

PS: You can also put null checks

public boolean equals(Object otherCouple){
  if(otherCouple != null && otherCouple instanceof Couple){
    return (this.person1.equals(otherCouple.getPerson1())
        && this.person2.equals(otherCouple.getPerson2()))
        || (this.person1.equals(otherCouple.getPerson2())
        && this.person2.equals(otherCouple.getPerson1()))

  }
  return false;
}

And then you can just add each of the couple to Set<Couple> all the duplicates will be removed.

Gagan Chouhan
  • 312
  • 1
  • 6
0

The fundamental problem is duplicates, and there's only one data structure that guarantees the removal of duplicates: sets.

In order to leverage sets, you're going to have to provide implementations for equals and hashCode in your Couple class.

To determine equality, there are about two states you'll want to validate (outside of if the object is actually a Couple):

  • this.person1 == other.person1 && this.person2 == other.person2
  • this.person2 == other.person1 && this.person1 == other.person2

You'd express those as Objects.equals(this.person1, other.person1) && Objects.equals(this.person2, other.person2), but the full writing out of that is an exercise for the reader.

As for the hash code, you can use Objects.hashCode to get that value for you.

@Override
public int hashCode() {
    return Objects.hashCode(person1, person2);
}
Makoto
  • 104,088
  • 27
  • 192
  • 230
  • you don't *have to* override equals/hashCode I guess... https://stackoverflow.com/a/50338161/1059372 – Eugene May 14 '18 at 20:04
  • @Eugene: Now author a test for that and wait until `Person` adds a few more fields. – Makoto May 14 '18 at 20:11
  • point fully valid, can not agree more, but sometimes you can't override hashCode/equals - that was my main point – Eugene May 14 '18 at 20:12
  • @Eugene: Given that the OP appears to be in full control of this object, I see no validity to the assumption that they *can't* override those methods. – Makoto May 14 '18 at 20:13
  • I will upvote as soon as the OP confirms this or you can provide the proper proof for this – Eugene May 14 '18 at 20:14
0

If you can't override equals/hashCode and you are willing to use java-8 and you are willing to add a utility method, taken from Stuart Mark's answer:

public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
    Set<Object> seen = ConcurrentHashMap.newKeySet();
    return t -> seen.add(keyExtractor.apply(t));
}

This could be done via:

relationshipList.stream()
            .map(x -> new SimpleEntry<>(x,
                    Stream.of(x.getPerson1(), x.getPerson2()).sorted().collect(Collectors.joining())))
            .filter(distinctByKey(Entry::getValue))
            .map(Entry::getKey)
            .collect(Collectors.toList())
Eugene
  • 117,005
  • 15
  • 201
  • 306