Replacing a HashSet Java member - java

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".

Related

Revert Hashmap to previous value

I have a class called Varfoo that stores variables. I have another class called Replacement that uses a hashmap to replace the x into 2. With the forget method, it's meant to forget replacing x with 2.
Varfoo x = new VarFoo("x");
Replacement s = new Replacement();
s.put(new VarFoo("x"), new IntFoo(2));
x.applyReplacement(s);
s.forget(x);
Here's the forget method:
public boolean forget(VarFoo var) {
if (var == null) {
throw new NullPointerException();
} else {
if (replacementMap.containsKey(var)) {
replacementMap.remove(var);
return true;
} else {
return false;
}
}
}
It will result to null because I've removed the key itself, not what I intended. How do I revert it back to how it was?
Equals and hashcodes of Varfoo:
#Override
public boolean equals(Object o) {
if (o == null) return false;
if (!(o instanceof VarFoo))
return false;
if (o == this)
return true;
return name.equals(((VarFoo) o).name);
}
#Override
public int hashCode() {
int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
You can make a new implementation of Map, that contains two (e.g.) HashMaps. In normal mode it forwards every operation to map1. This is the original map. map2 is null.
When you make a savepoint, you assign an empty map to map2. get operations now go first to map2 and then, if not found, to map1. put operations go only to map2. When you call forget, you assign again null to map2.
Of course, you must implement all the other methods of the Map interface. But this should be a simple task. Take care of removes, if needed (maybe you will need a Set of removed keys.
Hint: You can use java.util.AbstractMap as a base for your implementation.

Is it possible to enter duplicate value in HashSet?

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);
}

java HashMap key swap during get()

In a usage case, I have a HashMap which contains 1 entry and the key is A. I need to call get() with key B many many times. A equals() to B but A and B are not the same object. The Key contains a long array so its equals() is expensive. I am trying to improve the performance for this map checking operation. I know there are proper ways to address the performance issue. However, I am considering a hack which is the most expedient.
The following is from HashMap.java:
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
if i change the if block in the for loop to:
if (e.hash == hash) {
if (e.key == key) {
return e.value;
} else if (e.key.equals(key)) {
e.key = (K) key;
return e.value;
}
}
I think it will help the performance a lot. The first time I call get() with key B, B's equals() will be called. For the rest of times, B will be == to the key in the map thus saves the equals() call.
However, it is not possible to just extend HashMap and override get() since HashMap.field is package protected and Entry.key is final.
Questions:
Will this scheme work?
Copying HashMap.java and its related code just to change one method is not very appealing. What is the best way to implement this hack?
Thanks!
This is a horrible idea. You are mutating an entry's key under the covers.
The solution is to create your own internal "identity hash value", something you can calculate and guarantee is unique for each value. Then use this as a proxy for the expensive comparison in your equals() method.
For example (pseudo-Java):
class ExpensiveEquals
{
private class InxpensiveEqualsIdentity
{
...
public InexpensiveEqualsIdentity(ExpensiveEquals obj) { ... }
public boolean equals() { an inexpensive comparison }
}
private InxpensiveEqualsIdentity identity;
public ExpensiveEquals(...)
{
... fill in the object
this.identity = new InexpensiveEqualsIdentity(this);
}
public int hashCode() { return this.identity.hashCode(); }
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || !o instanceof this.getClass()) return false;
return (this.identity.equals(((ExpensiveEquals)o).identity));
}
}
Yes, this should work, if equals is implemented properly (symmetric).
Try to hack equals method in the class of your map's keys:
equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (!(obj instanceof MyClass)) return false;
MyClass other = (MyClass) obj;
if (this.longArray == other.longArray) return true;
if (Arrays.equals(this.longArray, other.longArray)) {
this.longArray = other.longArray;
return true;
}
return false;
}
Since your class is immutable, this trick should be safe. Your should make longArray field non-final, but it won't hurt performance, I promise.
If B is the key you are really interested in, you can just perform the swap from the outside.
V val = map.remove(b);
map.put(b, val);
From then on, reference equality is sufficient for B but you aren't futzing with the internal mechanism.
My simple lazy idea built on top of #JimGarrison's answer:
private long hash0, hash1;
void initHash() {
// Compute a hash using md5 and store it in hash0 and hash1
// The collision probability for two objects is 2**-128, i.e., very small,
// and grows with the square of the number of objects.
// Use SHA-1 if you're scared.
}
void assureHash() {
if (hash0 == 0 && hash1 == 0) initHash();
}
public int hashCode() {
// If both hashes are zero, assume it wasn't computed yet.
assureHash();
return (int) hash0;
}
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ExpensiveEquals)) return false;
ExpensiveEquals that = (ExpensiveEquals) o;
this.assureHash();
that.assureHash();
return this.hash0 == that.hash0 && this.hash1 == that.hash1;
}
This ensures that all equals invocations but the first will be pretty cheap. The changes that two randomly chosen pairs of longs are equals are negligible even when assuming thousands of objects and taking the birthday paradox into account. With a cryptographic hash function the numbers are as good as random.
If md5 and two longs aren't good enough, use SHA-1 and one additional int (that's what git does).

Java: Retrieving an element from a HashSet

Why cannot I retrieve an element from a HashSet?
Consider my HashSet containing a list of MyHashObjects with their hashCode() and equals() methods overridden correctly. I was hoping to construct a MyHashObject myself, and set the relevant hash code properties to certain values.
I can query the HashSet to see if there "equivalent" objects in the set using the contains() method. So even though contains() returns true for the two objects, they may not be == true.
How come then there isn’t any get() method similar to how the contains() works?
What is the thinking behind this API decision?
If you know what element you want to retrieve, then you already have the element. The only question for a Set to answer, given an element, is whether it contains() it or not.
If you want to iterator over the elements, just use a Set.iterator().
It sounds like what you're trying to do is designate a canonical element for an equivalence class of elements. You can use a Map<MyObject,MyObject> to do this. See this Stack Overflow question or this one for a discussion.
If you are really determined to find an element that .equals() your original element with the constraint that you must use the HashSet, I think you're stuck with iterating over it and checking equals() yourself. The API doesn't let you grab something by its hash code. So you could do:
MyObject findIfPresent(MyObject source, HashSet<MyObject> set)
{
if (set.contains(source)) {
for (MyObject obj : set) {
if (obj.equals(source))
return obj;
}
}
return null;
}
It is brute-force and O(n) ugly, but if that's what you need to do...
You can use HashMap<MyHashObject, MyHashObject> instead of HashSet<MyHashObject>.
Calling containsKey() on your "reconstructed" MyHashObject will first hashCode() - check the collection, and if a duplicate hashcode is hit, finally equals() - check your "reconstructed" against the original, at which you can retrieve the original using get()
Complexity is O(1) but the downside is you will likely have to override both equals() and hashCode() methods.
It sounds like you're essentially trying to use the hash code as a key in a map (which is what HashSets do behind the scenes). You could just do it explicitly, by declaring HashMap<Integer, MyHashObject>.
There is no get for HashSets because typically the object you would supply to the get method as a parameter is the same object you would get back.
If you know the order of elements in your Set, you can retrieve them by converting the Set to an Array. Something like this:
Set mySet = MyStorageObject.getMyStringSet();
Object[] myArr = mySet.toArray();
String value1 = myArr[0].toString();
String value2 = myArr[1].toString();
The idea that you need to get the reference to the object that is contained inside a Set object is common. It can be archived by 2 ways:
Use HashSet as you wanted, then:
public Object getObjectReference(HashSet<Xobject> set, Xobject obj) {
if (set.contains(obj)) {
for (Xobject o : set) {
if (obj.equals(o))
return o;
}
}
return null;
}
For this approach to work, you need to override both hashCode() and equals(Object o) methods
In the worst scenario we have O(n)
Second approach is to use TreeSet
public Object getObjectReference(TreeSet<Xobject> set, Xobject obj) {
if (set.contains(obj)) {
return set.floor(obj);
}
return null;
}
This approach gives O(log(n)), more efficient.
You don't need to override hashCode for this approach but you have to implement Comparable interface. ( define function compareTo(Object o)).
One of the easiest ways is to convert to Array:
for(int i = 0; i < set.size(); i++) {
System.out.println(set.toArray()[i]);
}
If I know for sure in my application that the object is not used in search in any of the list or hash data structure and not used equals method elsewhere except the one used indirectly in hash data structure while adding. Is it advisable to update the existing object in set in equals method. Refer the below code. If I add the this bean to HashSet, I can do group aggregation on the matching object on key (id). By this way I am able to achieve aggregation functions such as sum, max, min, ... as well. If not advisable, please feel free to share me your thoughts.
public class MyBean {
String id,
name;
double amountSpent;
#Override
public int hashCode() {
return id.hashCode();
}
#Override
public boolean equals(Object obj) {
if(obj!=null && obj instanceof MyBean ) {
MyBean tmpObj = (MyBean) obj;
if(tmpObj.id!=null && tmpObj.id.equals(this.id)) {
tmpObj.amountSpent += this.amountSpent;
return true;
}
}
return false;
}
}
First of all, convert your set to an array. Then, get the item by indexing the array.
Set uniqueItem = new HashSet();
uniqueItem.add("0");
uniqueItem.add("1");
uniqueItem.add("0");
Object[] arrayItem = uniqueItem.toArray();
for(int i = 0; i < uniqueItem.size(); i++) {
System.out.println("Item " + i + " " + arrayItem[i].toString());
}
If you could use List as a data structure to store your data, instead of using Map to store the result in the value of the Map, you can use following snippet and store the result in the same object.
Here is a Node class:
private class Node {
public int row, col, distance;
public Node(int row, int col, int distance) {
this.row = row;
this.col = col;
this.distance = distance;
}
public boolean equals(Object o) {
return (o instanceof Node &&
row == ((Node) o).row &&
col == ((Node) o).col);
}
}
If you store your result in distance variable and the items in the list are checked based on their coordinates, you can use the following to change the distance to a new one with the help of lastIndexOf method as long as you only need to store one element for each data:
List<Node> nodeList;
nodeList = new ArrayList<>(Arrays.asList(new Node(1, 2, 1), new Node(3, 4, 5)));
Node tempNode = new Node(1, 2, 10);
if(nodeList.contains(tempNode))
nodeList.get(nodeList.lastIndexOf(tempNode)).distance += tempNode.distance;
It is basically reimplementing Set whose items can be accessed and changed.
If you want to have a reference to the real object using the same performance as HashSet, I think the best way is to use HashMap.
Example (in Kotlin, but similar in Java) of finding an object, changing some field in it if it exists, or adding it in case it doesn't exist:
val map = HashMap<DbData, DbData>()
val dbData = map[objectToFind]
if(dbData!=null){
++dbData.someIntField
}
else {
map[dbData] = dbData
}

How to get object from Map with different key?

I have a map that stores my ClassA as a key and some exception as a value.
I also have a list that contains ClassB objects.
ClassA has an entry X (Long) and ClassB has an entry Y(ClassY) and it has field X (String) too.
Now I should find in map where ClassA.getX == ClassB.getY.getX
But problem is I can search ın map only by key and key object must be ClassA. Otherwise it returns null.
Here is my iteration:
list = listModelNewSc;
for (int i = 0; i < exceptionMap.size(); i++) {
for (int k = 0; k < list.getSize(); k++) {
if (((ClassA) exceptionMap.get(i)).getX() == Long
.parseLong((((CLassB) list.getElementAt(k)).getY().getX()))) {
Listitem itemAtIndex = list.getItemAtIndex(i);
if (itemAtIndex != null) {
System.out.print("FOUND");
}
}
}
}
The only way to do this with the setup you described is to iterate through all the keys in the map until you find the one you want.
Alternatively, you could have have a second map with ClassA.getX as the key (mapping to the same value).
Essentially it's a trade off, the first solution is slower but uses less memory, the second solution is faster but uses more memory (you have two copies of the map).
Iterate over keyset or entryset.
A solution, haven't tried but tell me if it works :)
As far as I know when you call Map.get(...) it uses the keys' equals(o) and hashCode() methods. As a solution to your problem you could override these methods of ClassA in a kinda "wrong" way, like this:
#Override
public int hashCode() {
// If you know that the code of a key instance has no logic value anywhere else, give constant value here and in ClassB
return 99827;
}
#Override
public boolean equals(Object o) {
// This will (maybe) make Map.get(ClassB) work with ClassA as a key
if (o instanceof ClassB) {
ClassB cb = (ClassB) o;
return this.getX() == cb.getY().getX();
} else if (o instanceof ClassA) {
// ...
}
return false;
}
So then you'll just do exceptionMap.get(classB) and hopefully get the exception.

Categories