10:08, EET
July 20, 2015
Hello,
why does the hashcode of DataValue change if I change the value itself ?
Regarding Java Spec. the hashcode of an object should not be changed
if properties of this object changes.
With this I have problems iterating over a HashSet of DataValue objects
and after changing a value of the current element the iterator.remove() function
does not work anymore.
kindest regards
10:39, EET
April 3, 2012
I wonder what java spec you are reading.. There would be no point in having a hashcode override anywhere if it would not check the fields of that object as the object itself has no data (except maybe a C++ pointer value or something allocated by the JVM implementations, which is what the unoverridden hashcode returns).
Also Object.hashCode javadoc does state:
” The general contract of hashCode is:
Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
…
“
If you are calling DataValue.setValue you are changing the information (the value) which affects the equals comparison (i.e. the DataValue is not anymore the same, you just changed it’s value), therefore the hashcode is also different.
I would say you are in most cases better creating new DataValue than trying to change the value of an existing one
Also note that that class is part of the Java Stack, not the SDK.
– Bjarne
11:25, EET
July 20, 2015
Thanks for the hint that it comes from the Java Stack. Anyway, this behaviour completely destroys java core functionality (Lists, Sets, Maps).
It makes it impossible to remove modified objects in java standard collections/maps.
I don’t know what intention of the UaStack developers had… maybe for an easy comparison of DataTypes if they are exactly the same but as
I said, this makes DataValue objects nearly worthless in java collections/maps if an object has to be modified and/or remove later on.
I will ask the OPCFoundation about that… thanks for your answer anyway
I looked into the implementation of DataValue :
@Override
public int hashCode() {
return
ObjectUtils.hashCode(value) |
ObjectUtils.hashCode(statusCode) |
ObjectUtils.hashCode(sourceTimestamp) |
ObjectUtils.hashCode(sourcePicoseconds) |
ObjectUtils.hashCode(serverTimestamp) |
ObjectUtils.hashCode(serverPicoseconds);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof DataValue)) return false;
DataValue o = (DataValue) obj;
return
ObjectUtils.objectEquals(o.value, value) &&
ObjectUtils.objectEquals(o.statusCode, statusCode) &&
ObjectUtils.objectEquals(o.sourceTimestamp, sourceTimestamp) &&
ObjectUtils.objectEquals(o.serverTimestamp, serverTimestamp) &&
ObjectUtils.objectEquals(o.sourcePicoseconds, sourcePicoseconds) &&
ObjectUtils.objectEquals(o.serverPicoseconds, serverPicoseconds);
}
12:53, EET
April 3, 2012
You do have wrong assumpion on how have hashcodes works in java, i.e. it works like that in _every_ java class (that overrides the default hashcode/equals implementations in Object, which ,must be done so that the equal check works for different instances of the object and is not pure implementation pointer comparison), if you change something that the object would not be equal to it’s previous state, of course the hashcode changes (unless for a rare occuranse of hash collision). Also note that if 2 java objects are .equals then they are required to have same hashcode. Also if objects have the same hashcode, they are not necessarily equal (because the hashcode is just a hash). A hashcode implementation of return 0; would be valid (a naive implementation, but still valid).
You are not supposed to use something which hashcode can change in a “hashcode-aware” Collection. However note that unless you are doing something weird (like calling .setValue), the hashcode of the DataValue does/should not change if you are using the SDK API. Note that DataValue is used as “return by refence” style in some listener style APIs where you need to call .setValue on the DataValue given as parameter to the listener method (assuming you are starting out with the SDK it is unlikely that you need to do these).
Now for the more interesting part: why would you like to change the value in a DataValue? I see no reason why.
As a technical note most likely the reason why the method exist: I believe that is because of how the opc.tcp binary serialization is implemented
In a sense you do have a valid point, I would much prefer that the DataValue would be immutable and could not be changed. However the current implementation is what it is and changing that would break too many existing code so it most likely wont be changed.
– Bjarne
13:24, EET
July 20, 2015
Dear Bjarne,
thank you for your reply. In short terms :
My UaClient reads values from a given UaNode… user can define custom “postprocessors” which can be, for example manipulate values afterwards or check if a value is in a specific range (otherwise it should be removed from the list of values).
The order in which these postprocessors are executed is user defined. For example we have a value changer processer which multiplies the read value with 5 … and afterwards a “value limit” postprocesser resolves to false cause the value is now out of the configured range, so it shall remove this DataValue from the Collection which
cannot be archieved with the usual java iterator / for style (or simple remove operations on collections / maps).
The workaround now is, that I create a new empty Collection and add only these values which where not filtered out by any defined postprocessors.
friendly regards
14:18, EET
July 20, 2015
Hello Bjarne,
i think it’s not weird to call a public method.
When calling a public method has strong side effects (like changes in hash-code) it
should be mentioned somewhere in the docs.
Otherwise simple code like below does not work:
// basic method which processes a set of DataValues
public void processValues(Set values)
{
// loop
for(Iterator iter = values.iterator(); iter.hasNext();)
{
// get the current value
DataValue value = iter.next();
// process value (may add some variant values)
processValue(value);
// a validity check
boolean isValid = isValidValue(value);
if (!isValid)
{
// this will not work! when processValue(…) has added a Variant to DataValue
// no exception, nothing, current value just remains in collection
iter.remove();
}
}
}
16:32, EET
April 3, 2012
I would say in 99% of the cases of calling a method named setXXX automatically implies it performs a side-effect which changes equals and so does hashcode (with the 1% being immutable classes which return copy of the object with that value changed, I have not seem much of those).
Anyway I suggest that you create your own container class for the value data. Initialize it based on the DataValue and do further processing using the container class.
Most Users Ever Online: 1919
Currently Online:
16 Guest(s)
Currently Browsing this Page:
1 Guest(s)
Top Posters:
Heikki Tahvanainen: 402
hbrackel: 144
rocket science: 88
pramanj: 86
Francesco Zambon: 83
Ibrahim: 78
Sabari: 62
kapsl: 57
gjevremovic: 49
Xavier: 43
Member Stats:
Guest Posters: 0
Members: 729
Moderators: 7
Admins: 1
Forum Stats:
Groups: 3
Forums: 15
Topics: 1529
Posts: 6471
Newest Members:
PromotionToold, HypromeImpupe, toneylapham544, rondawolinski7, Marypof5711, roycedelargie91, kourtneyquisenbe, ellis87832073466, zkxwilliemae, gabriellabachusModerators: Jouni Aro: 1026, Pyry: 1, Petri: 0, Bjarne Boström: 1032, Jimmy Ni: 26, Matti Siponen: 349, Lusetti: 0
Administrators: admin: 1