I am trying to add duplicate values in HashSet by modifying its hashCode() and equals() method()?
I tried below code
public class dupSet {
static Set set= new HashSet();
#Override
public int hashCode() {
return (int) (100*Math.random());
}
#Override
public boolean equals(Object obj) {
return false;
}
public static void main(String[] args) throws ParseException {
set.add("a");
set.add("b");
set.add("a");
System.out.println(set);
}
}
As per my understanding if for two duplicate of "a" HashSet will first get hashCode() to get proper bucket and then check value of equals() if equals returns true then it will not add but if it return false then it will add.
So for adding duplicate value to my Set I override equals() which always return false but still set is not allowing duplicate values?
You hashCode method returns always zero. Have a look at the range of Math.random().
Second, you do not override equals and hashCode of the elements you add. You actually add a String. To make things work, you must implement a class and add instances of that class to you HashSet. The implemented class needs to override the equals and hashSet method, not the main class.
Third, as stated in the comments, you shouldn't do what you are doing. What you realy want is a ArrayList. By implementing the equals and hashCode methods this way, a fundamental contract is broken.
I read source code and from that I am able to understand how its work
so need some help
First of all
Set is a collection of well defined and distinct objects
So there is no question of adding duplicates values. But if you are interested in understanding how java achieve/implement this constraint , then you can start digging in the source code.
A HashSet is backed by HashMap which mean that it delegates it operations like add, remove, etc. to HashMap .Now When you call set.add("a"); then
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
is called, which in turn calls HashMap#put
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
The put method first calcuates the hash code of the object using
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
Once the hashCode is calculated the it calls
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict)
inside this method , it put the value because this condition
if ((p = tab[i = (n - 1) & hash]) == null)
is true and it then increments the modCount(which stores the number of times the HashMap has been structurally modified), checks if we need to resize the map and then call afterNodeInsertion and returns null
Now when you call set.add("b"); then the same logic runs again but this time the condition inside final V putVal method
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
holds true and due to this , the code
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
detects the existing mapping and thus return the oldValue . Hence preventing adding duplicate value.
You want the objects in the Set to include duplicates I assume (if just for curiosity keep reading, otherwise just choose other collection. this might help)
Let me make some corrections:
public class DupSet<E extends Comparable<E>>{
private Set<E> mySet = new HashSet<>();
//Implement add, remove and size
}
public class MyNeverEqualClass implements Comparable<MyNeverEqualClass>{
private static int stupidHash = 0;
private int num;
public MyNeverEqualClass(int num){
this.num = num;
}
#Override
public int compareTo(MyNeverEqualClass other){
double rnd = Math.random()*3 + 1
return (rnd > 1.5)? 1:-1;
}
#Override
public boolean equals(MyNeverEqualClass other){
return false;
}
#Override
public int hashCode(){
return stupidHash++;
}
}
public static void main(String[] args){
MyNeverEqualClass a = new MyNeverEqualClass(1);
MyNeverEqualClass b = new MyNeverEqualClass(1);
DupSet<MyNeverEqualClass> set = new DupSet<>();
set.add(a);
set.add(b);
}
Related
I wrote a dummy program , that adds object in Hash Set. I created a class Car that has capacity of 5 people.
Now issue is i got different out put from different Main programs .
Kindly find the 2-Main programs below.
First Main Program is
public class Main_1 {
static int counter = 0;
public static void main(String args[]) {
Car car = new Car();
for (int i = 0; i < 20; i++) {
car.add(new Person());
}
car.done();
}
}
The out put of Main_1 is : Exception in thread "main" java.lang.IllegalStateException: I'm full
at Car.add(Car.java:10)
at Main_1.main(Main_1.java:8)
Second Main program is
public class Main_2 {
static int counter = 0;
static Car car = new Car();
public static void main(String args[]) {
car.add(new RecursivePerson());
car.done();
}
static class RecursivePerson extends Person {
public int hashCode() {
if (++counter < 20) {
car.add(new RecursivePerson());
}
return super.hashCode();
}
}
}
The out put of Main_2 is I'm a car with 20 people!
Below is the business logic of my program.
import java.util.HashSet;
import java.util.Set;
public class Car {
private static final int CAPACITY = 5;
private Set<Person> people = new HashSet<Person>();
public synchronized void add(Person p) {
if (people.size() >= CAPACITY) {
throw new IllegalStateException("I'm full");
} else {
people.add(p);
}
}
public synchronized void done() {
if (people.size() == 20) {
// The goal is to reach this line
System.out.println("I'm a car with 20 people!");
}
}
}
class Person {
}
Can some one tell my why java is behaving like this.
The difference is because of the way that a HashSet works: if you add an new element to it, it first checks if the object is already in the set, and if it isn't, it adds this to the set. In order to check if the object is in the set, it call hashCode() on the object.
Your second program is specifically designed to bypass the capacity check of the car. You override hashCode() in the objects you add to the hashset. This method is called by the HashSet.add method, but before the object was actually added to the set. In the overridden hashCode() method you add the additional elements to the set. That is, if Car.add() is called, the size of the hash set is always 0, and the capacity check will always pass.
HashSets are implemented using a HashMap. Let us have a look at the source HashSet's source for add, according to GrepCode:
public boolean add(E e)
{
return map.put(e, PRESENT)==null;
}
Let us follow this to the put implementation in HashMap, according to GrepCode:
public V More ...put(K key, V value)
{
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next)
{
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
{
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
}
Your object is added in the last line with the use of addEntry. However, hashCode is called in the 3rd line before the entry is added; in the end, this cause put to be called again. Because your hashCode method adds until you have 20 element the size of the set is 20 in the end.
It is because in Main_2 you are calling add recursively. The initial call to .add() method will not return to actually increment the size of people list. So, people.size() so it will always return 0, altough you added a lot of elements there.
if you do a little debugging, you will see the callstack after a couple of iterations in Main_2 looks like this:
This question already has answers here:
Java HashSet contains duplicates if contained element is modified
(7 answers)
Closed 7 years ago.
In our application I often see generated equals and hashCode methods.
I don't know why they are overriden however I am worried about that they are generated (I think so as the are very similar). The below example shows the problem. One SomeBean instnace is created and it firlsty exists and then the set doesnt containt this object. It is because the change of "a" value and hashcode is changed.
HashSet caches hashcodes wright? So every change of an objec previously put in Hashset is dangerous?
private class SomeBean{
private Integer a = 0;
public void setA(Integer a) {
this.a = a;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SomeBean someBean = (SomeBean) o;
if (a != null ? !a.equals(someBean.a) : someBean.a != null) return false;
return true;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + a.hashCode();
return result;
}
}
#Test
public void test() throws Exception {
SomeBean sb = new SomeBean();
Set set = new HashSet<>();
set.add(sb);
sb.hashCode();
System.out.println( set.contains(sb));;
sb.setA(4);
System.out.println(set.contains(sb));;
}
Should the hashCode be cached by SomeBean class and never changed? It could look like this:
#Override
public int hashCode() {
if (_hashCode == 0) {
final int prime = 31;
int result = 1;
result = prime * result + a.hashCode();
return result;
} else return _hashCode;
}
}
But now I risk that object with "a" = 0 and every other new object created with "a"=0 and then changed are the same objects..
It won't work. The reason for that is that HashSet is based on the assumption that hash code of an object won't change, but you change value of your object:
sb.setA(4);
that changes the value of a hash code.
Internally hash map is based on an array. Hash code is used to select a position in an array that is used for hash-map implementation and since hash-code is changed from one call to another HashSet is looking into a different position in the array. Since another element in the array is empty HashSet assumes that such element does not exists in the datastructure.
I have Set of that structure. I do not have duplicates but when I call:
set.add(element) -> and there is already exact element I would like the old to be replaced.
import java.io.*;
public class WordInfo implements Serializable {
File plik;
Integer wystapienia;
public WordInfo(File plik, Integer wystapienia) {
this.plik = plik;
this.wystapienia = wystapienia;
}
public String toString() {
// if (plik.getAbsolutePath().contains("src") && wystapienia != 0)
return plik.getAbsolutePath() + "\tWYSTAPIEN " + wystapienia;
// return "";
}
#Override
public boolean equals(Object obj) {
if(this == obj) return true;
if(!(obj instanceof WordInfo)) return false;
return this.plik.equals(((WordInfo) obj).plik);
}
#Override
public int hashCode() {
return this.plik.hashCode();
}
}
Do a remove before each add:
someSet.remove(myObject);
someSet.add(myObject);
The remove will remove any object that is equal to myObject. Alternatively, you can check the add result:
if(!someSet.add(myObject)) {
someSet.remove(myObject);
someSet.add(myObject);
}
Which would be more efficient depends on how often you have collisions. If they are rare, the second form will usually do only one operation, but when there is a collision it does three. The first form always does two.
If the set already contains an element that equals() the element you are trying to add, the new element won't be added and won't replace the existing element. To guarantee that the new element is added, simply remove it from the set first:
set.remove(aWordInfo);
set.add(aWordInfo);
I was working on a problem where I had a set then I wanted to replace/override some of the objects with objects from another set.
In my case what I ended up doing was creating a new set and putting the overrides in first then adding the current objects second. This works because a set won't replace any existing objects when adding new objects.
If you have:
Set<WordInfo> currentInfo;
Set<WorldInfo> overrides;
Instead of:
for each override, replace the object in current info
I did:
Set<WordInfo> updated = new HashSet<>();
updated.addAll(overrides);
updated.addAll(currentInfo);
Try something as follows (this will only make sense if the equals and hashCode depends on one field, but the other fields could have different values):
if(!set.add(obj)) {
//set already contains the element (not the same object though)
set.remove(obj); //remove the one in the set
set.add(obj); //add the new one
}
Check out the documentation for the Set.add method
If this set already contains the element, the call leaves the set unchanged and returns false.
Check the HashSet code within the JDK.
When an element is added and is a duplicate, the old value is replaced.
Folk think that the new element is discarded, it's wrong.
So, you need no additional code in your case.
UPDATED---------------------
I re-read the code in JDK, and admit a mistake that I've made.
When put is made, the VALUE is replaced not the KEY from an HashMap.
Why am I talking about Hashmap??!! Because if you look at the HashSet code, you will notice:
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
So the PRESENT value is replaced with the new one as shown in this portion of code:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
But I agree, the key isn't replaced, and since the keys represent the HashSet's values, this one is said to be "untouched".
I am trying to sort a TreeMap on key. Key is some custom DataStructure having int, List, String, etc.
The member on which I am expecting a sort has some duplicates. Let's say that member is Rank. More than 1 object can have same rank.
Simplified version example:
NOTE: in the CompareTo method below 0 is not returned intentionally to NOT ignore duplicates.(Please correct me if this is not the right way to avoid duplicates)
import java.util.TreeMap;
public class TreeTest {
public static void main(String[] args) {
TreeMap<Custom,String> t = new TreeMap<Custom,String>();
Custom c1 = new Custom();
c1.setName("a");
c1.setRank(0);
Custom c2 = new Custom();
c2.setName("b");
c2.setRank(1);
Custom c3 = new Custom();
c3.setName("c");
c3.setRank(0);
t.put(c1, "first");
t.put(c2, "Second");
t.put(c3, "Third");
System.out.println(t.keySet());
for(Custom c:t.keySet()){
System.out.println(t.get(c));
}
}
}
And Custom Object
package com.example.ui;
public class Custom implements Comparable<Custom>{
int rank;
String name;
public int getRank() {
return rank;
}
public void setRank(int rank) {
this.rank = rank;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + rank;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Custom other = (Custom) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (rank != other.rank)
return false;
return true;
}
// 0 is not returned intentionally to NOT ignore duplicates.
public int compareTo(Custom o) {
if(o.rank>this.rank)
return 1;
if(o.rank==this.rank)
return -1;
return -1;
}
}
Output::
[com.example.ui.Custom#fa0, com.example.ui.Custom#fbe, com.example.ui.Custom#f80]
null
null
null
Expected:
First, Second, Third based on Rank 0,1,0 respectively.
I looked at couple of examples on Google. Most of them were basic usage on TreeMap sort using keys or values with primitive datatypes, but none with duplicates when sorting member
is a part of custom key DataStructure.
Please help?
The problem is that your implementation of compareTo is not consistent with equals, which is required by TreeMap. From the API docs:
Note that the ordering maintained by a sorted map (whether or not an
explicit comparator is provided) must be consistent with equals if
this sorted map is to correctly implement the Map interface.
One possible consistent implementation would be to first compare by rank and then by name if the rank values are equal. For two instances of Custom with equal ranks and identical names you should not expect to be able to store them both as keys within the same Map - This violates the contract of Map.
public int compareTo(Custom o) {
int ret = this.rank - o.rank;
// Equal rank so fall back to comparing by name.
if (ret == 0) {
ret = this.name.compareTo(o.name);
}
return ret;
}
As mentioned, your implementation of equals and compareTo are not consistent with each other. If I read your question correctly, what you require is to preserve duplicates that have the same key. I'd recommend you to look into the TreeMultimap of the Google Guava collections. It creates set containers for each value object sothat different values having the same key are preserved.
e.g.
treeMultimap.put ("rank1", "Joe");
treeMultimap.put ("rank1", Jane");
treeMultimap.get ("rank1"); // Set("Joe","Jane");
The constrain in this data structure is that K,V pairs must be unique. That is, you can't insert ("rank1", "Joe") twice in the Multimap.
One important note: The reason why you see so many examples of Map, using simple types and, in particular, strings, is that keys in a map must be immutable. The equals and hashcode values of an object must not change in the time it's used as a key in a map. Translated to your example, you cannot do customObject.setRank(...) and updates a rank value when it's used as a key. To do so, you first need to remove the key and its values, update it and then insert it again.
You can also do it by implementing Comparator as anonymous inner type and override compare() to return desired comparison.
public class TreeMaps
{
public static void main(String[] args)
{
Custom c1 = new Custom(1,"A");
Custom c2 = new Custom(3,"C");
Custom c3 = new Custom(2,"B");
TreeMap<Custom , Integer > tree = new TreeMap<Custom, Integer> (new Comparator<Custom>() {
#Override
public int compare(Custom o1, Custom o2) {
return o1.rank - o2.rank;
}
});
tree.put(c1, 1);
tree.put(c2, 2);
tree.put(c3, 3);
System.out.println(tree);
}
}
class Custom
{
int rank ;
String name ;
public Custom(int rank , String name) {
this.rank = rank ;
this.name = name ;
}
#Override
public String toString()
{
return "Custom[" + this.rank + "-" + this.name + "]" ;
}
}
I am trying to locate a key in a HashMap. I can print the selected key by using 'get' but when I use 'containsKey' in an if statement, it is not found.
I KNOW the key is present in the Map but it keeps returning false. Any ideas people?
My code:
public static boolean checkLowerStructuralSupport(Location location) {
boolean hasSupport = false;
Location supportingLocation = new Location(location.getX(), location.getY(), location.getZ() - 1);
System.out.println(_levels.get(supportingLocation.getZ()).getLevelSites2().get(supportingLocation)); //works
if (_levels.get(supportingLocation.getZ()).getLevelSites2().containsKey(supportingLocation)) {
hasSupport = true;
} else {
hasSupport = false;
}
return hasSupport;
}
Here is the code for the Location class:
public class Location {
protected int _x;
protected int _y;
protected int _z;
public Location(int xAxis, int yAxis, int zAxis) {
this._x = xAxis;
this._y = yAxis;
this._z = zAxis;
}
public void equals() {
//not implemented yet
}
public void HashCode() {
//not implemented yet
}
public String toString() {
String locationString = Integer.toString(_x) + Integer.toString(_y) + Integer.toString(_z);
return locationString;
}
public void setX(int XAxis) {
this._x = XAxis;
}
public int getX() {
return this._x;
}
public void setY(int YAxis) {
this._y = YAxis;
}
public int getY() {
return this._y;
}
public void setZ(int ZAxis) {
this._z = ZAxis;
}
public int getZ() {
return this._z;
}
}
You must ensure that the Location class has properly implemented its hashCode() and equals(Object) methods (documentation). That is, if two Location objects are effectively equal, they should share a common hash code and their equals method should return true.
As descibed here, you have to override the equals(Object) method.
The reason why get(Object) is working is, that HashMap will calculate the Hash for your Location class and returns the Object the hascode points to.
containsKey(Object) calculates the hash key and gets the object the hash is pointed to. The object from the HashMap will compare to the Object you put in. For these comparison the equals method is used.
When you do not override he equals method, true is returned, when the object reference to the same instance.
From HashMap
/**
* Check for equality of non-null reference x and possibly-null y.
*/
static boolean eq(Object x, Object y) {
return x == y || x.equals(y);
}
From Object
public boolean equals(Object obj) {
return (this == obj);
}
From the javadoc of equals
The equals method for class Object
implements the most discriminating
possible equivalence relation on
objects; that is, for any non-null
reference values x and y, this method
returns true if and only if x and y
refer to the same object (x == y has
the value true).
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.
In Location class, make sure you are overriding hashCode and equals methods.
If you are, can you post them?
containsKey uses the method equals to compare the param with the entries in the key set. So the Location class needs to have a equals method that is good. The default equals method in java.lang.Object only returns true when both objects are the same object. In this case you probably have 2 different instances that needs to be compared and need a custom equals method.
The only thing I can think of that will cause this is if the state of supportingLocation is somehow being mutated between the get(...) call and the containsKey(...).
Assuming the code snippet you posted is the exact code that's causing problems, the only place this could occur is if one of Location#getZ(...), Location#hashCode() or Location#equals(Object) mutates the state of Location (or the Location constructor, or one of these methods starts a thread that randomly changes the state of the Location instance, but I think we can rule that out).
Could you verify that none of the methods above are changing the state of the supportingLocation instance? While I am not familiar with the Location class itself, I'd venture to guess that a class like that would ideally be immutable.
Edit:
To clarify, when I say that Location#getZ() etc aren't mutating the Location, what I mean is:
Location x = new Location(1,2,3);
Location y = new Location(1,2,3);
boolean eq1 = x.equals(y);
int hash1 = x.hashCode();
x.getZ(); // this should *not* mutate the state of x
boolean eq2 = x.equals(y);
int hash2 = x.hashCode();
In the end, eq1 should be equal to eq1, and hash1 should be equal to hash2. If this is not the case, getZ() is mutating the state of x (or equals, or hashCode, or worse, those methods are completely off), and will result in the behavior you observed.
To avoid problems, your equals() and hashCode() methods should be consistent and conform to the requirements (as noted elsewhere).
Additionally, hashCode() should not rely on mutable members, otherwise your calculated hash code can change, and this affects the internal workings of the HashMap. That will reveal itself in an inability to retrieve stuff from Hash* collections.
Take a peak at the source code for the HashMap implementation. Both get and containsKey use the hasCode() and equals() methods of your key object.
The only real difference, and as was pointed out, it is a trivial null check, is in the comparisons:
get:
((k = e.key) == key || key.equals(k))
containsKey:
((k = e.key) == key || (key != null && key.equals(k)))
where e is of type Entry for a HashMap.
So, if you do not have a strong implementations of hashCode() and/or equals() you will have a problem. Additionally, if your keys were mutated (I see that you did not declare the class fields final) you could have an issue.
Take the following example:
public class HashMapTest {
static class KeyCheck {
int value;
public KeyCheck(int value) { this.value = value; }
public void setValue(int value) { this.value = value; }
#Override public int hashCode() { return value; }
#Override public boolean equals(Object o) {
return ((KeyCheck)o).value == this.value;
}
}
public static void main(String args[]) {
HashMap<KeyCheck, String> map = new HashMap<KeyCheck, String>();
KeyCheck k1 = new KeyCheck(5);
KeyCheck k2 = new KeyCheck(5);
map.put(k1, "Success");
System.out.println("Key: " + k1 + " Get: " + map.get(k1) +
" Contains: " + map.containsKey(k1));
System.out.println("Key: " + k2 + " Get: " + map.get(k2) +
" Contains: " + map.containsKey(k2));
k1.setValue(10);
System.out.println("Key: " + k1 + " Get: " + map.get(k1) +
" Contains: " + map.containsKey(k1));
System.out.println("Key: " + k2 + " Get: " + map.get(k2) +
" Contains: " + map.containsKey(k2));
}
}
This will print out:
Key: HashMapTest$KeyCheck#5 Get: Success Contains: true
Key: HashMapTest$KeyCheck#5 Get: Success Contains: true
Key: HashMapTest$KeyCheck#a Get: null Contains: false
Key: HashMapTest$KeyCheck#5 Get: null Contains: false
As you can see, in this case the mutability caused the hashCode() to change, which ruined everything.
Both get() and containsKey() are using the Location class's hashCode() method. The equals() method isn't called unless there is a hash collision. (thus, HashMap's get() won't use equals() in every situation.)
For your Location class, did you by chance happen to implement your own version of hashCode()? The hashCode() method should be implemented carefully. Joshua Bloch described all the details in the book Effective Java, portions of which are online... I'll go find the link to those sample chapters: Effective Java Sample Chapters. You want chapter 3.
As I asked in the comment to the question, Where does your _levels variable come from? I don't see it declared inside that method and your naming (underscore prefix, are you importing that convention from some other language?) suggests that it "lives" outside this method. Perhaps other code is changing it during execution? Please let us know when you solve it; the suspense is killing me.
i think sometime you need the hash code and sometimes not so i think in this way you can turn of the hash code checking when you want buy changing the hash code for all objects you want to 0
public class sample(){
#JsonIgnore
private int hashCode = super.hashCode();
public void setHashCode(int hashCode){
this.hashCode = hashCode;
}
#Override
public int hashCode(){
return this.hashCode;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ReflectObject other = (ReflectObject) obj;
if (this.hashCode != other.hashCode) {
return false;
}
return true;
}
}