Java HashSet contains Object - java

I made my own class with an overridden equals method which just checks, if the names (attributes in the class) are equal. Now I store some instances of that class in a HashSet so that there are no instances with the same names in the HashSet.
My Question: How is it possible to check if the HashSet contains such an object. .contains() wont work in that case, because it works with the .equals() method. I want to check if it is really the same object.
edit:
package testprogram;
import java.util.HashSet;
import java.util.Set;
public class Example {
private static final Set<Example> set = new HashSet<Example>();
private final String name;
private int example;
public Example(String name, int example) {
this.name = name;
this.example = example;
set.add(this);
}
public boolean isThisInList() {
return set.contains(this);
//will return true if this is just equal to any instance in the list
//but it should not
//it should return true if the object is really in the list
}
public boolean remove() {
return set.remove(this);
}
//Override equals and hashCode
}
Sorry, my english skills are not very well. Please feel free to ask again if you don't understand what I mean.

In your situation, the only way to tell if a particular instance of an object is contained in the HashSet, is to iterate the contents of the HashSet, and compare the object identities ( using the == operator instead of the equals() method).
Something like:
boolean isObjectInSet(Object object, Set<? extends Object> set) {
boolean result = false;
for(Object o : set) {
if(o == object) {
result = true;
break;
}
}
return result;
}

The way to check if objects are the same object is by comparing them with == to see that the object references are equal.
Kind Greetings,
Frank

You will have to override the hashCode method also.

try this..
Considering only one property 'name' of your Objects to maintain uniqueness.
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (name == null ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
User other = (User) obj;
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
return true;
}

I made my own class with an overridden equals method which just checks, if the names (attributes in the class) are equal.
This breaks the contract of .equals, and you must never do it no matter how convenient it seems.
Instead, if you want to index and look up elements by a certain attribute such as the name, use a HashMap<Name, YourType> to find them. Alternatively, use a TreeSet and pass it a Comparator that compares the name only. You can then remove the incorrect equals method.
There are then three ways if you want to find objects by reference equality:
Your objects have no inherent or useful notion of equality.
Don't implement equals. Leave it to its default. You can then use a HashSet to look for reference equality, and a HashMap or TreeSet to index them by any specific attributes.
Your objects do have a useful, universal notion of equality, but you want to find equivalent instances efficiently anyways.
This is almost never the case. However, you can use e.g. an Apache IdentityMap.
You don't care about efficiency.
Use a for loop and == every element.

HashSet contains uses the equals method to determine if the object is contained - and duplicates are not kept within the HashSet.
Assuming your equals and hashcode are only using a name field...
HashSet<MyObject> objectSet = new HashSet<MyObject>();
MyObject name1Object = new MyObject("name1");
objectSet.add(new MyObject("name1"));
objectSet.add(name1Object);
objectSet.add(new MyObject("name2"));
//HashSet now contains 2 objects, name1Object and the new name2 object
//HashSets do not hold duplicate objects (name1Object and the new object with name1 would be considered duplicates)
objectSet.contains(new MyObject("name1")) // returns true
objectSet.contains(name1Object) // returns true
objectSet.contains(new MyObject("name2")) // returns true
objectSet.contains(new MyObject("name3")) // returns false
If you wanted to check if the object in the HashSet is the exact object you are comparing you would have to pull it out and compare it directly using ==
for (MyObject o : objectSet)
{
if (o == name1Object)
{
return true;
}
}
If you do this alot for specific objects it might be easier to use a HashMap so you don't have to iterate through the list to grab a specific named Object. May be worth looking into for you because then you could do something like this:
(objectMap.get("name") == myNameObject) // with a HashMap<String, MyNameObject> where "name" is the key string.

Related

TreeSet storing duplicate custom objects

Hello I probably oversaw something, but here it goes.
I have a TreeSet<CustomObject> and I do not want to have duplicates in the Set. My CustomObject class looks like this.
class CustomObject implements Comparable<CustomObject> {
String product;
String ean;
public CustomObject(String ean){
this.ean = ean;
// product is getting set via setter and can be null
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CustomObject that = (CustomObject) o;
return ean.equals(that.ean);
}
#Override
public int hashCode() {
return ean.hashCode();
}
#Override
public int compareTo(CustomObject another) {
if(equals(another)) return 0;
if(product != null && another.product == null) return -1;
if(product == null) return 1;
return product.compareToIgnoreCase(another.product);
}
}
Now I have a add Function for new objects.
private final TreeSet<CustomObject> productCatalog;
public void addObject(SomeData tag) {
CustomObject p = new CustomObject(tag.getEan());
if (productCatalog.contains(p)) { // <-- This checks only one entry of the Set.
for (CustomObject temp : productCatalog) {
if (temp.equals(p)) {
p = temp; // I do stuff with that later which is irrelevent here
}
}
} else {
productCatalog.add(p);
}
}
The method productCatalog.contains(p) calls the compareTo method from the Comparable Interface and does the comparing. The issue here is that it literally only checks I think the last object? in the set. So what happens is that only one unique CustomObject entry is present.
This is the scenario when I follow it with the debugger:
productCatalog.contains(p)
calls compareTo
calls equals to check if ean.equals(that.ean)
returns once true, but every other time false. Since it only checks the last object
How can I have it to check not only one object in step 4, but all the present objects in the Set. What am I missing?
Thx!
EDIT: These are some sample data. For the simplicity SomeData tag is basically a String.
First run:
addObject("Ean1") // success added
addObject("Ean2") // success added
addObject("Ean3") // success added
addObject("Ean4") // success added
Everything gets added into the TreeSet.
Second run:
addObject("Ean1") // failed already in the map
addObject("Ean2") // failed already in the map
addObject("Ean3") // failed already in the map
addObject("Ean5") // success added
addObject("Ean4") // success added
addObject("Ean4") // success added
For testing purpose I manually set product names depending on the String ean.
public CustomObject(String ean){
this.ean = ean;
switch(ean){
case "Ean1": product = "TestProduct"; break;
case "Ean2": product = "ProductTest";break;
case "Ean3": product = "Product";break;
}
The TreeSet acts as a cache.
Edit2: This is how I solved it.
for (CustomObject temp : productCatalog) {
if (temp.equals(p)) {
p = temp; // I do stuff with that later which is irrelevent here
}
}
I removed the if statement with the contains method since that would always return ยด1or-1in my special case. Now I simply iterate over the Set to correctly use theequals` method since the TreeSet uses compareTo() for checking every element in the Set.
The Java Docs state the following
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. (See Comparable or Comparator
for a precise definition of consistent with equals.) This is so
because the Set interface is defined in terms of the equals operation,
but a TreeSet instance performs all element comparisons using its
compareTo (or compare) method, so two elements that are deemed equal
by this method are, from the standpoint of the set, equal. The
behavior of a set is well-defined even if its ordering is inconsistent
with equals; it just fails to obey the general contract of the Set
interface.
The main problem:
compareTo does return 1 if both product and other.product are null. This is wrong because they are actually equal. You probably forgot to set product names for the higher ean values, like "Ean4" and "Ean5".
Old answer:
Your implementations of equals and compareTo do not fit together.
equals works on the ean and compareTo on the product. This only works if you implicitly assume that equal ean imply equal product. If this is not true in your test cases, the result will be wrong.
In either case, it is no good implementation because this can lead to a < b, b < c but a "equals" c.

Divide and conquer method of factorial function using BigDecimal format returns stackoverflow error [duplicate]

I constructed a class with one String field. Then I created two objects and I have to compare them using == operator and .equals() too. Here's what I've done:
public class MyClass {
String a;
public MyClass(String ab) {
a = ab;
}
public boolean equals(Object object2) {
if(a == object2) {
return true;
}
else return false;
}
public boolean equals2(Object object2) {
if(a.equals(object2)) {
return true;
}
else return false;
}
public static void main(String[] args) {
MyClass object1 = new MyClass("test");
MyClass object2 = new MyClass("test");
object1.equals(object2);
System.out.println(object1.equals(object2));
object1.equals2(object2);
System.out.println(object1.equals2(object2));
}
}
After compile it shows two times false as a result. Why is it false if the two objects have the same fields - "test"?
== compares object references, it checks to see if the two operands point to the same object (not equivalent objects, the same object).
If you want to compare strings (to see if they contain the same characters), you need to compare the strings using equals.
In your case, if two instances of MyClass really are considered equal if the strings match, then:
public boolean equals(Object object2) {
return object2 instanceof MyClass && a.equals(((MyClass)object2).a);
}
...but usually if you are defining a class, there's more to equivalency than the equivalency of a single field (a in this case).
Side note: If you override equals, you almost always need to override hashCode. As it says in the equals JavaDoc:
Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.
You should override equals
public boolean equals (Object obj) {
if (this==obj) return true;
if (this == null) return false;
if (this.getClass() != obj.getClass()) return false;
// Class name is Employ & have lastname
Employe emp = (Employee) obj ;
return this.lastname.equals(emp.getlastname());
}
The best way to compare 2 objects is by converting them into json strings and compare the strings, its the easiest solution when dealing with complicated nested objects, fields and/or objects that contain arrays.
sample:
import com.google.gson.Gson;
Object a = // ...;
Object b = //...;
String objectString1 = new Gson().toJson(a);
String objectString2 = new Gson().toJson(b);
if(objectString1.equals(objectString2)){
//do this
}
The overwrite function equals() is wrong.
The object "a" is an instance of the String class and "object2" is an instance of the MyClass class. They are different classes, so the answer is "false".
It looks like equals2 is just calling equals, so it will give the same results.
Your equals2() method always will return the same as equals() !!
Your code with my comments:
public boolean equals2(Object object2) { // equals2 method
if(a.equals(object2)) { // if equals() method returns true
return true; // return true
}
else return false; // if equals() method returns false, also return false
}
The "==" operator returns true only if the two references pointing to the same object in memory. The equals() method on the other hand returns true based on the contents of the object.
Example:
String personalLoan = new String("cheap personal loans");
String homeLoan = new String("cheap personal loans");
//since two strings are different object result should be false
boolean result = personalLoan == homeLoan;
System.out.println("Comparing two strings with == operator: " + result);
//since strings contains same content , equals() should return true
result = personalLoan.equals(homeLoan);
System.out.println("Comparing two Strings with same content using equals method: " + result);
homeLoan = personalLoan;
//since both homeLoan and personalLoan reference variable are pointing to same object
//"==" should return true
result = (personalLoan == homeLoan);
System.out.println("Comparing two reference pointing to same String with == operator: " + result);
Output:
Comparing two strings with == operator: false
Comparing two Strings with same content using equals method: true
Comparing two references pointing to same String with == operator: true
You can also get more details from the link: http://javarevisited.blogspot.in/2012/12/difference-between-equals-method-and-equality-operator-java.html?m=1
Statements a == object2 and a.equals(object2) both will always return false because a is a string while object2 is an instance of MyClass
Your implementation must like:
public boolean equals2(Object object2) {
if(a.equals(object2.a)) {
return true;
}
else return false;
}
With this implementation your both methods would work.
If you dont need to customize the default toString() function, another way is to override toString() method, which returns all attributes to be compared. then compare toString() output of two objects. I generated toString() method using IntelliJ IDEA IDE, which includes class name in the string.
public class Greeting {
private String greeting;
#Override
public boolean equals(Object obj) {
if (this == obj) return true;
return this.toString().equals(obj.toString());
}
#Override
public String toString() {
return "Greeting{" +
"greeting='" + greeting + '\'' +
'}';
}
}
Your class might implement the Comparable interface to achieve the same functionality. Your class should implement the compareTo() method declared in the interface.
public class MyClass implements Comparable<MyClass>{
String a;
public MyClass(String ab){
a = ab;
}
// returns an int not a boolean
public int compareTo(MyClass someMyClass){
/* The String class implements a compareTo method, returning a 0
if the two strings are identical, instead of a boolean.
Since 'a' is a string, it has the compareTo method which we call
in MyClass's compareTo method.
*/
return this.a.compareTo(someMyClass.a);
}
public static void main(String[] args){
MyClass object1 = new MyClass("test");
MyClass object2 = new MyClass("test");
if(object1.compareTo(object2) == 0){
System.out.println("true");
}
else{
System.out.println("false");
}
}
}
the return type of object.equals is already boolean.
there's no need to wrap it in a method with branches. so if you want to compare 2 objects simply compare them:
boolean b = objectA.equals(objectB);
b is already either true or false.
When we use == , the Reference of object is compared not the actual objects. We need to override equals method to compare Java Objects.
Some additional information C++ has operator over loading & Java does not provide operator over loading.
Also other possibilities in java are implement Compare Interface .which defines a compareTo method.
Comparator interface is also used compare two objects
Here the output will be false , false beacuse in first sopln statement you are trying to compare a string type varible of Myclass type to the other MyClass type and it will allow because of both are Object type and you have used "==" oprerator which will check the reference variable value holding the actual memory not the actual contnets inside the memory .
In the second sopln also it is the same as you are again calling a.equals(object2) where a is a varible inside object1 . Do let me know your findings on this .
In short, == compares two POINTERS.
If the two pointers are equal, then they both point to same object in memory (which, obviously has the same value as itself).
However, .equals will compare the VALUES of whatever is pointed to, returning true iff they both evaluate to the same value.
Thus, two separate strings (i.e., at different addresses in memory) are always != but are .equal iff they contain the same (null-terminated) sequence of chars.
IN the below code you are calling the overriden method .equals().
public boolean equals2(Object object2) {
if(a.equals(object2)) { // here you are calling the overriden method, that is why you getting false 2 times.
return true;
}
else return false;
}

HashSet.contains(object) returns false for instance modified after insertion

According to the JavaDoc of java.util.HashSet.contains() the method does following
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)).
However this does not seem to work for following code:
public static void main(String[] args) {
HashSet<DemoClass> set = new HashSet<DemoClass>();
DemoClass toInsert = new DemoClass();
toInsert.v1 = "test1";
toInsert.v2 = "test2";
set.add(toInsert);
toInsert.v1 = null;
DemoClass toCheck = new DemoClass();
toCheck.v1 = null;
toCheck.v2 = "test2";
System.out.println(set.contains(toCheck));
System.out.println(toCheck.equals(toInsert));
}
private static class DemoClass {
String v1;
String v2;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((v1 == null) ? 0 : v1.hashCode());
result = prime * result + ((v2 == null) ? 0 : v2.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DemoClass other = (DemoClass) obj;
if (v1 == null) {
if (other.v1 != null)
return false;
} else if (!v1.equals(other.v1))
return false;
if (v2 == null) {
if (other.v2 != null)
return false;
} else if (!v2.equals(other.v2))
return false;
return true;
}
}
prints out:
false
true
So although the equals method returns true, HashSet.contains() returns false.
I guess this is because I modified the toInsert instance AFTER it was added to the collection.
However this is in no way documented (or at least I wasn't able to find such). Also the documentation referenced above the equals method should be used but it does not seem so.
When an object is stored in a HashSet its put in a data structure that's easily (read: efficiently) searchable by the object's hashCode(). Modifying an object may change its hashCode() (depending on how you implemented it), but does not update its location in the HashSet, as the object has no way of knowing its contained in one.
There are a couple of things you can do here:
Modify the implementation of hashCode() so it isn't affected by the field you're changing. Assuming this field is important to the object's state, and participates in the equals(Object) method, this is somewhat of a code smell, and should probably be avoided.
Before modifying the object, remove it from the set, and then re-add it once you're done modifying it:
Set<DemoClass> mySet = ...;
DemoClass demo = ...;
boolean wasInSet = mySet.remove(demo);
demo.setV1("new v1");
demo.setV2("new v2");
if (wasInSet) {
set.add(demo);
}
HashSet and HashMap use both hashCode and equals methods to locate an object in their inner structure. hashCode is used to find a correct bucket and then equals is consulted to distinguish between different objects with the same hashCode, as the latter is not guaranteed to be unique. In almost any cases this is an extremely bad idea to modify object which serves as a key in a HashMap or is put into a HashSet. If those modifications change either hashCode or semantics of equals method, your object will not be found.
This is by-design behavior.
HashSet uses hashes to identify objects it holds.
So, if you change an object after it was placed into collection, it may be unable to find it.
You should either hold immutable objects only, or make mutable only that part of an object, which is not affect the hash.
I think better is to use HashMap, which clearly separates mutable and immutable parts.
It is quite clear, you are changing toInsert.v1 after adding to the set, and due to DemoClass obtain hashCode from v1 and v2 attributes, it won't find changed hashCode for elementes.

Equals method of Object and Object in Array

I'm simply trying to find the position of an object in a Customer List, although it's constantly returning false when I use the equals() method.
I now understand that you have to create your own equals method to override the automatic one, yet I can't understand how to create one when I'm comparing a Customer inside a Customer array.
Here are my instance variables:
private Customer[] data;
private int size;
private final static int INITIAL_SIZE = 20;
Here is my method to find the position of the object:
public int findCustomerLocation(String name)
{
int spot = -1;
Customer cus = new Customer(name);
for(int i = 0; i < size ; i++)
{
if((data[i].equals(cus)))
{
spot = i;
System.out.println("spot is:" + spot);
}
else
{
System.out.println("spot not found");
}
}
return spot;
}//findCustomerLocation
(It's returning spot not found)
I'm trying to rewrite the equals method, but I'm a bit stuck, I'm trying to use if it's an instance of another, but it's still returning false
Your equals method just uses the same identity operator (==) as the default Object#equals(). Presumably you intend for Customer objects to be considered equal when some particular information inside them, such as an ID field, is equal. If so, you need to compare those fields inside your equals() (and you should always include every field involved in equals() in your hashCode()).
Something like this might be what you're looking for:
public boolean equals(Object other) {
if(!other instanceof Customer) {
return false;
}
Customer that = (Customer) other;
return this.name.equals(that.name);
}
The equals method should implement the logic that dictates whether a given object is "equal to" the current (this) object. Logically it should not rely on fields or parameters that are not members of the class. In your case, you should not be using the data array or the size field in the equals method. Just add the check on the appropriate fields of Customer.
For example, assuming Customer has a field called name, then the following equals checks if two Customers are equal if they have the same name:
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Customer other = (Customer) obj;
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
return true;
}
If you're using an IDE like Eclipse, there are shortcuts for automatically overriding both equals() and hashCode() methods in a consistent way. Usually, both methods should use the same class members so that if two objects are equal, their hashcode values are identical.
You are comparing the Customer objects, not their names. Since they are not the same object, equals() will return false. To fix this add the following code to Customer:
public boolean equals(Object obj){
if(obj instanceof Customer){
Customer c = (Customer)obj;
if(c.name.equals(this.name)
return true;
}
return false;
}
EDIT: Sorry for the duplicate post, look at the post by chrylis, and insert obj != null before the instanceof statement
Good luck!
Note that you have to implement hashCode when you implement equals. There are some things to consider to write these methods correctly. To be on the safe side it's best to let an IDE generate these methods. For example, in Eclipse, go Source > Generate hashCode() and equals()...

Java - HashMap.containsKey returning incorrect values for Objects

Here is my code:
import java.util.HashMap;
class MyString
{
String string;
MyString(String string)
{
this.string = new String(string);
}
}
public class test
{
public void test3()
{
HashMap<MyString, Byte> byteHashMap = new HashMap<MyString, Byte>();
char c = 'A';
byte b = (byte)c;
String string = new String("The Letter A");
MyString myString = new MyString(string);
byteHashMap.put(myString, b);
if (byteHashMap.containsKey(myString))
//if (byteHashMap.containsKey(new MyString(string)))
{
System.out.println("true");
}
else
{
System.out.println("false");
}
}
public static void main(String[] args)
{
test testObject = new test();
testObject.test3();
}
}
I would like to know why the code, as is, returns true. However, when I switch the if statements (see the commented line), it returns false.
I believe it has something to do with overriding the equals method in the MyString class but I am unsure how to approach this.
Regards.
Your belief is correct. Map structures rely on .equals to determine if a key already exists in the map or not. By default, object types have instance equality (meaning a.equals(b) is true if and only if a and b point to the same object).
In your case, you probably want two MyString instances to be equal if the string field is equal. In this case, you would override equals with something like:
#Override
public boolean equals(Object other) {
if (other == null) { return false; }
if (!(other instanceof MyString)) { return false; }
MyString m = (MyString)other;
return this.string.equals(m.string);
}
With this, either of the two if statements would return true.
You should also override hashCode. If you use Eclipse, it can generate these two methods for you quite easily via the Source menu.
Yes, override hashCode and equals.
Right now you will compare a string against the memory reference (standard implementation) of your MyString object.
example
#Override
public bool equals(Object args0) {
if(args0 == this) { return true; }
if(!(args0 instanceof MyString)) { return false; }
MyString obj = (MyString) args0;
return obj.getString().equals(this.string); // Create getter
}
#Override
public int hashCode(){
return this.string.hashCode() * 37;
}
You need to implement hashCode and equals. HashMaps put objects in buckets based on the hashCode. Different objects can have the same hashCode, but the rule is that two equal objects must have the same hashCode. Once the HashMap finds all of the entries with the same hashCode, it then checks for equality using the equals method.
The default implementation checks for object instance equality - that is, are the two objects the same instance. In your case, you're checking two different instances.
Override the equals and hashCode methods to pass through the values returned by hashCode() and equals() for the private String member variable, assuming that's all that determines if two instances of your class are equal.

Categories

Resources