0

Say I have 2 lists of Nodes (Node is some comparable object, the details don't matter). I'd like to find which Nodes appear on both lists - not necessarily the same Node object, but Nodes whose compareTo returns 0.

For example, nodeA is on list A and nodeB is on list B. They are NOT the same Node but nodeA.compareTo(nodeB) will return 0 (equality).

I'd like to solve this by iterating over list A and putting the nodes in a HashMap/HashSet, then iterating over list B and checking which nodes have already been inserted into the HashMap/HashSet. The issue is, this will not work as the Nodes will not be considered equal by the HashMap unless they are the same actual Node.

How can I solve this problem? Note that I am not looking for other solutions to this problem, but for an understanding of if (and how) I can use a HashMap, HashSet or another similar data structure in the given example scenario.

4
  • What hashcode returns? What nodeA.hashcode() == nodeB.hashcode() returns? Commented Sep 9, 2016 at 7:42
  • In the case I'm interested in, it returns false. Commented Sep 9, 2016 at 7:46
  • 1
    This is the problem! HashMap/HashSet works with hashCode/equals! Commented Sep 9, 2016 at 7:47
  • If you want to use compareTo use a TreeSet. For HashMap you need equals/hashCode to be written correctly. Commented Sep 9, 2016 at 7:54

4 Answers 4

2

You could solve this (if I am understanding this right) by overriding the equals and hashcode methods in the Node object to use the same criteria as the compareTo method.

Sign up to request clarification or add additional context in comments.

Comments

2

You should use TreeSet as it relies on ordering, not on hashing.

Update (WRONG! See update 3): But you still have to make sure a.compareTo(b)==0 iff a.equals(b) because the implementation of contains actually finds the corresponding element by the ordering, and then checks equality using equals!

Update 2: To use only the ordering, you could make the first list sorted (Collections.sort(listA)) and then check the result of Collections.binarySearch(listA, nodeB) for each nodeB from listB. When the result is >=0 the nodeB was in listA, w.r.t their ordering. The implementation of binarySearch uses only ordering, no equals involved. The complexity of this approach is O(n log(n)) since each binary search is O(log(n)) (n being size of the bigger of the lists -- sorting list A is also O(n log(n))), that is the same complexity as using TreeSet (O(log n) for both insert/contains).

Update 3: As pointed out in the comments, the TreeSet should do the work, as the equals is not used in the TreeMap's containsKey, but somewhere else. But the JavaDoc says:

Note that the ordering maintained by a set (whether or not an explicit comparator is provided) must be consistent with equals if it is to correctly implement the Set interface.

And docs for the contains method for TreeSet (!) says:

Returns true if this set contains the specified element. More formally, returns true if and only if this set contains an element e such that (o==null ? e==null : o.equals(e)).

So it is still not a good idea to use TreeSet with incosistent API. Who knows what another Java implementation may do in such a case. So I suggest sticking with the Update 2 solution.

2 Comments

Sounds like a good solution, but this would hurt runtime, correct? TreeSet basic actions are O(log(n)) rather than O(1).
@frrlod Yes, it would be slower than using HashMap, as you say. The HashMap complexity trick is based on having THE HASH (O(1) average, but O(n) worst case). When you have ordering, the best lookup you can have is O(log(n)) (worst case) by binary search (used in TreeSet too), and when you have nothing like that, just equality your lookup time is O(n) -- you have to look on every possible element.
0

My suggestion would be to make your own subclass of HashMap that overrides the containsValue(object value) function with one that fits your criteria.

Comments

0

Use TreeSet/TreeMap. It uses compareTo() method instead of equals() and hashcode() which is what used while assigning Nodes in HashMap/HashSet.

Look at the following code:

import java.util.HashSet; import java.util.TreeSet; public class TestMaps { public static void main(String[] args) { TreeSet<Node> set1 = new TreeSet<>(); HashSet<Node> set2 = new HashSet<>(); Node node1 = new Node(0, "First"); Node node2 = new Node(0, "Second"); System.out.println(set1.add(node1)); //Node1 added in TreeSet System.out.println(set2.add(node1));//Node1 added in HashSet System.out.println(set1.add(node2));//Node2 added in TreeSet System.out.println(set2.add(node2));//Node2 added in HashSet } } class Node implements Comparable<Node>{ int i; String name; Node(int i, String n){ this.i = i; this.name = n; } @Override public int compareTo(Node o) { return this.i - o.i; } } 

Running this results into :

true true false true 

Hope it helps!!

3 Comments

TreeMap uses equals on the object queried by contains, see my answer.
are you sure on that because what I see in TreeSet Implementation. "contains" calls internally TreeMap "ContainsKey" method which in turn calls "getEntry" method which again uses "compareTo" or "compare". I did not see any reference for equals there.
@MartinMilichovsky That is wrong and I already pointed this out in the comments. Basically you ruined your good answer by adding that nonsense to your post.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.