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.
Related
Consider that I would like to create a class which manages a TreeSet of custom objects with two keys: A String instance identifier, and a long order identifier. The Long value would be used to determine the order of the elements in the list, while the string would be used to determine if two of these elements are duplicates. To clarify, here is what the methods would look like for the custom object
#Override
public boolean equals(Object o) {
if(o == null)
return false;
if(!(o instanceof CustomObject))
return false;
//Determined by string_id values ONLY. Used in TreeSet implementation
CustomObject obj = (CustomObject) o;
return obj.string_id.equals(this.string_id);
}
#Override
public int compareTo(Object another) {
if(another == null || !(another instanceof CustomObject))
return -1;
CustomObject other = (CustomObject) another;
if(other.long_id > this.long_id)
return -1;
else if(other.long_id < this.long_id)
return 1;
else
return 0;
}
Can my TreeSet function this way? I ask, because while testing this, I've found that my implementation only keeps the latest entry, and discards the rest. I'm looking to find out if this is simply an error with my implementation, or if I'm not properly using the TreeSet class and need to refactor my approach.
I have a Set and I will check if an Object with the same property still exists. My first approach was to iterate over the list and check but I guess there is a better approach in Java 8. Does anyone have any suggestion how to check this in an elegant way?
final Set<UserSelected> preparedUserSelected = new HashSet<>();
UserSelected userSelected1 = new UserSelected();
userSelected1.setInstitutionId("1");
userSelected1.setUserId("1");
userSelected1.setChatMessageFilter(ChatMessageFilterEnum.TYP2);
UserSelected userSelected2 = new UserSelected();
userSelected2.setInstitutionId("2");
userSelected2.setUserId("2");
userSelected2.setChatMessageFilter(ChatMessageFilterEnum.TYP1);
UserSelected userSelected3 = new UserSelected();
userSelected3.setInstitutionId("3");
userSelected3.setUserId("3");
userSelected3.setChatMessageFilter(ChatMessageFilterEnum.TYP2);
preparedUserSelected.add(userSelected1);
preparedUserSelected.add(userSelected2);
preparedUserSelected.add(userSelected3);
UserSelected userSelectedToCheck = new UserSelected();
userSelectedToCheck.setInstitutionId("2");
userSelectedToCheck.setUserId("2");
userSelectedToCheck.setChatMessageFilter(ChatMessageFilterEnum.TYP1);
boolean contains = false;
for (final UserSelected userSelected : preparedUserSelected) {
if (userSelectedToCheck.getInstitutionId().equals(userSelected.getInstitutionId())
&& userSelectedToCheck.getUserId().equals(userSelected.getUserId())
&& userSelectedToCheck.getChatMessageFilter() == userSelected.getChatMessageFilter())
contains = true;
}
System.out.println("contains: " + contains);
You need to properly implement equals and hashCode methods in your UserSelected class. After that you can easily check the existence with preparedUserSelected.contains(userSelectedToCheck).
Here's sample implementation of equals/hashCode for your class:
#Override
public int hashCode() {
return Objects.hash(getUserId(), getInstitutionId(), getChatMessageFilter());
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
UserSelected other = (UserSelected) obj;
return Objects.equals(getChatMessageFilter(), other.getChatMessageFilter()) &&
Objects.equals(getInstitutionId(), other.getInstitutionId()) &&
Objects.equals(getChatMessageFilter(), other.getChatMessageFilter());
}
Try this anyMatch of Lambda Expression(Java 8).
Returns whether any elements of this stream match the provided predicate. May not evaluate the predicate on all elements if not necessary for determining the result. If the stream is empty then false is returned and the predicate is not evaluated.
boolean isExist = preparedUserSelected.stream()
.anyMatch(userSelected -> userSelected.getInstitutionId().equalsuserSelected.getInstitutionId());
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.
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).
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".