java - How to find matching objects between two lists? - java

Given two Lists, each list holding the same object type, I would like to find objects between the two lists that match, based on some property values.
e.g. an object from List1, L1Obj, matches an object from List2, L2Obj, if L1Obj.a == L2Obj.a AND L1Obj.b == L2Obj.c AND L1Obj.c == L2Obj.c
These properties are not the only properties of the of the class, but are all that is needed to uniquely identify an object within a list.
My question is - what is the best way to achieve this?
One way would be to construct to HashMaps based on the lists, with the concataned String value of a+b+c used as the key to index an object. That way I could iterate through the first list, and attempt to lookup an object in the second list with the same key.
How does this sound? Is there a better way of achieving this??
All help is much appreciated!
UPDATE:
Okay, so actually I need a bit more. Upon finding a match, I want to overwrite properties L1Obj.x, L1Obj.y, L1Obj.z with those of L2Obj. HashSet sounds great for finding that matches, but if I'm right it doesn't actually allow me to access these matches.
What can I do about this?

Do the objects you want to look at implement equals(Object) and hashCode() that only take into account the fields you care about? If so, you can create a new HashSet from the first list, and then call retainAll() passing in the second list.
If they don't implement equals(Object) and hashCode() with respect to the properties you care about, you can create a TreeSet and pass in a Comparator that looks at the properties you care about.

Rather than use the String repesntation, use the equals() method a HashSet as so:
class MyObj {
Property a;
Property b;
Property c;
public boolean equals(Object o) {
// use == if Property is primitive, like int or something
return o instanceof MyObj && a.equals(o.a) && b.equals(o.b) && c.equals(o.c);
}
// edit - when you override equals, also override hashcode
public int hashCode() {
return a.hashCode() ^ b.hashCode() ^ c.hashCode();
}
public String toString() {
return a.toString() + " " + b.toString() + " " + c.toString();
}
}
// later in your main method
Set<MyObj> objSet = new HashSet<MyObj>();
for(MyObj o : list1) objSet.add(o);
for(MyObj o : list2) if(objSet.contains(o)) System.out.println(o + " is a match!");

You can do one thing. Have two lists with these objects and override the equals method of the class to which these objects belong.
Your equals method should look like
#Override
public boolean equals(Object obj)
{
return (this.a == obj.a && this.b == obj.b && this.c == obj.c)
}
Also remember, once you override equals method, you need to override int hashCode() method as well.
One thing to note is while implementing hashCode() is that 2 equal objects will have same hashCode, while the converse is not true.

I don't know if I thinking to easy but I would try it like that:
Override the equals method of the object to implement your comparison to check if it is the same object
Then I would iterate over the first list and check with the contains method if the object is also contained in the second list.
Then I would iterate through the second list and check if the object is also in the first list and not already in the result list.

The object in question should implement the boolean equals(Object) method. E.g.:
L1Obj.equals(L2Obj);
You could overload that method so that you can implement the equality operations that you want.

Related

How HashSet of collection framework identifies duplicate objects? How Hashcode and equals work with custom objects?

I was looking at some code and found one program where they have extended HashSet class of collection. They have created custom employee objects and inserted into Set without implementing Comparable or Comparator. They implemented logic to identify duplicate employee object so if object is already there They can perform update operation if object is not present they can do insert operation. I was trying to understand how set will identify duplicate object without implementiong Comparable or Comparator interface.
I have tried same and also tried to override Hashcode and equals method to understand how Set comparing objects. One thing I have found that while I am adding same object to set its generating same Hashcode. But its not calling equals method I have overrided.
class Employee {
int id;
String name;
Employee(int id, String name) {
this.id = id;
this.name = name;
}
#Override
public int hashCode() {
System.out.println("HashCode : " + super.hashCode());
return super.hashCode();
}
#Override
public boolean equals(Object o) {
System.out.println("Equals : " + super.equals(o));
return super.equals(o);
}
}
class LimitedHashSet extends HashSet<Employee> {
public boolean add(Employee e) {
if(!super.add(e)){
return false;
}
return true;
}
}
public class ExtendingHashSet {
public static void main (String[] args) {
Employee e0 = new Employee(1,"A");
Employee e1 = new Employee(2,"B");
LimitedHashSet obj = new LimitedHashSet();
System.out.println("Element added ? " + obj.add(e0) + "\n");
System.out.println("Element added ? " + obj.add(e1) + "\n");
System.out.println("Element added ? " + obj.add(e0) + "\n");
}
}
Hashcode is use to calculate bucket and equals method identifies its equals or not. So how its working here. Please check output below.
Output:
HashCode : 914424520
Element added ? true
HashCode : 110718392
Element added ? true
HashCode : 914424520
Element added ? false
Fundamentally, there is no magic.
HashSet and all JDK hash-based collections blindly use the implementations of hashCode and equals definitions for your objects to determine if objects are the same or not. They just call the hashCode and equals methods, and however those work, that's what the hash collections use.
Part of the point of the use of the hashCode method -- part of the point of hash-based data structures in the first place -- is to make it unlikely that the hash-based collection even has to bother calling the equals method to check if two objects are the same. If the hashes aren't the same, then the objects are definitely not equal. So it would call the equals method you have overridden if it needed to, but in your code, it doesn't need to bother calling equals. Furthermore, it also checks reference equality before calling the equals methods, because if two objects are == then they are definitely .equals to each other.
Currently, you are adding e0 twice. By reference equality, e0 == e0, so it doesn't bother calling .equals -- it just finds the matching entry, checks that they're reference-equal, and keeps the existing entry without adding a new element. This is the correct behavior, by the way, since e0 is a duplicate of itself.

Check if element exists in HashSet by its hash

Can I check if an object is in a HashSet if I have only the object's hash code, but not the object itself?
You can't, since the hashCode is only used to locate the bin in which the object is stored within the HashSet. There may be multiple elements in the same bin, and HashSet uses equals to determine if the searched element exists in the bin that the hashCode was mapped to.
The bin may contain multiple elements having the same hashCode, so knowing only the hashCode is not enough.
You can make a special object, something like this
int hashCode = 1; // your
new Object() {
#Override
public boolean equals(Object obj) {
return true;
}
#Override
public int hashCode() {
return hashCode;
}
};
No you cant. Both equals and hashcode are required for finding an object in a hashed collection. The hashcode method tells which bucket to find the object in and the equals method finds the object in the bucket.
A real world example would be a house with many people in it. If you tell me to go find someone in some house, I am going to ask you two questions :
What house do I go to?
Who do I find?
Here (1) will be answered by hashcode and (2) will be answered by equals method.
A hashCode is not a uniquely identifying property, so no.
No, because 2 objects can have the same hashcode. The unicity of objects in a Set (or keys in a Map) is checked against hashCode() and equals().
All Objects have method hashCode(), called by HashSet, HashMap, ecc..
You can override this method to define your own logic
#Override
public int hashCode() {
return <<MY CODE HERE>>;
}
or, more simple, iterate your HashSet and check hashCode() value
int myHash = 123;
for(Object o:mySet){
if(myHash == o.hashCode()){
// do something..
}
}

In Java how can I check if an object is in a linked list?

Below is my class. The insertSymbol method is supposed to add an object to the linked list which is then added to a hash table. But when I print the contents of the hash table it has double entries. I tried to correct this by using "if(temp.contains(value)){return;}" but it isn't working. I read that I need to use #override in a couple of places. Could anyone help me know how and where to use the overrides? Thank you!
import java.util.*;
public class Semantic {
String currentScope;
Stack theStack = new Stack();
HashMap<String, LinkedList> SymbolTable= new HashMap<String, LinkedList>();
public void insertSymbol(String key, SymbolTableItem value){
LinkedList<SymbolTableItem> temp = new LinkedList<SymbolTableItem>();
if(SymbolTable.get(key) == null){
temp.addLast(value);
SymbolTable.put(key, temp);
}else{
temp = SymbolTable.get(key);
if(temp.contains(value)){
return;
}else{
temp.addLast(value);
SymbolTable.put(key, temp);
}
}
}
public String printValues(){
return SymbolTable.toString();
}
public boolean isBoolean(){
return true;
}
public boolean isTypeMatching(){
return true;
}
public void stackPush(String theString){
theStack.add(theString);
}
}
You have multiple options here. You'll need at least to add an equals (and therefor also a hashcode) method to your class.
However, if you want your collection to only contain unique items, why not use a Set instead?
If you still want to use a List, you can use your current approach, it just that the characteristics of a Set are that all items in a Set are unique, so a Set might make sense here.
Adding an equals method can quite easily be done. Apache Equalsbuilder is a good approach in this.
You don't need the 2nd line when you add a new value with the same key:
temp.addLast(value);
SymbolTable.put(key, temp); // <-- Not needed. Its already in there.
Let me explain something that #ErikPragt alludes to regarding this code:
if(temp.contains(value)){
What do you suppose that means?
If you look in the javadocs for LinkedList you will find that if a value in the list is non-null, it uses the equals() method on the value object to see if the list element is the same.
What that means, in your case, is that your class SymbolTableItem needs an equals() method that will compare two of these objects to see if they are the same, whatever that means in your case.
Lets assume the instances will be considered the same if the names are the same. You will need a method like this in the 'SymbolTableItem` class:
#Overrides
public boolean equals(Object that) {
if (that == null) {
return false;
}
if (this.getName() == null) {
return that.getName() == null;
}
return this.getName().equals(that.getName());
}
It it depends on more fields, the equals will be correspondingly more complex.
NOTE: One more thing. If you add an equals method to a class, it is good programming practice to add a hashcode() method too. The rule is that if two instances are equal, they should have the same hashcode and if not equal they don't have to be different hashcodes but it would be very nice if they did.
If you use your existing code where only equals is used, you don't need a hashcode, stricly. But if you don't add a hashcode it could be a problem someday. Maybe today.
In the case where the name is all that matters, your hashcode could just return: this.getName().hashcode().
Again, if there are more things to compare to tell if they are equal, the hashcode method will be more complex.

HashSet equality called with passed object instead of stored item

In the following code, the output shows CONTAINS for each object, whereas commenting out the anonymous object's equals() method results in MISSING, which leads me to believe the second equality pass (hashCode() -> equals()) actually calls the equality method of the supplied object instead of the object within the collection being tested.
List<String> strings = Arrays.asList("Hello", "there", "Qix");
HashSet<String> set = new HashSet<>(strings);
for(final String s : strings)
{
boolean contains = set.contains(new Object(){
#Override
public int hashCode() {
return s.hashCode();
}
#Override
public boolean equals(Object obj) {
return true;
}
});
System.out.format("%s: %s\n",
s,
contains ? "CONTAINS" : "MISSING");
}
Why is this? Is it because the equals() method, by principle, should be symmetric between the two objects?
The HashSet either has to do a.equals(b) or b.equals(a). And as they should be written to be symmetric*, it shouldn't matter which it chooses.
But for reference, the documentation states:
returns true if and only if this set contains an element e such that (o==null ? e==null : o.equals(e))
* See http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#equals(java.lang.Object).
It's an implementation detail which we shouldnt worry about since it's never said in public API how it uses equals. Like you said it's supposed to be symmetric anyway. If we go into src we'll see that it is really passedObject.equals(storedObject)
Becaue AFAIK the default Object.equals implementation uses reference comparison so it checks if the two references refer to the same object.
Here you are comparing instances of Strings with another custom classe, they can't be equal, whatever the way this is done.

How to find difference between two arraylist in java

I have two arraylist with a number of model objects.I want to find the difference of these arraylists.When I use strings instead of models, I got the difference with removeall function in collection framework. But for model objects it doesnot work. Please any one help me
Implement equals and hashCode in your custom object and you can use the same approach as you did with Strings.
Well, the removeAll method is a generic library method which doesn't know anything about your model class. So if you think about it for a second, how is it going to know which ones are "the same"?
The short answer is that you need to override the equals() method in your Model class, as this is what the checks are based on. The implementation should return true for any pair of model instances that you wish to be considered the same - the default inherited behaviour returns true only if they're the same object in memory. (And as always, when you override equals() you must override hashCode() too).
String class has already overridden version of equals and hashCode method so you are able to use remove() method. If you have to use your class in collection (List or Set) then you will have to override these methods in your class otherwise it will use default implementation of these methods.
If two objects are logically equal that means their hashCode must be equal as well as they satisfy equals().
For comparing two ArraList you need two compare two objects.In your case it is your model object,for that you need to override equals method.
Try this code
#Override
public boolean equals(Object compareObj)
{
if (this == compareObj)
return true;
if (compareObj == null)
return false;
if (!(compareObj instanceof MyModel))
return false;
MyModel model = (MyModel)compareObj;
return this.name.equals(model.name); // Are they equal?
}
#Override
public int hashCode()
{
int primeNumber = 31;
return primeNumber + this.name.hashCode();
return 0;
}

Categories

Resources