Comparing two objects, either of which could be null - java

I have a function in one of my classes that compares itself with another instance of the same class - and finds out which variables differ. This is for the purpose of minimizing network load with a main database (by only uploading data that needs to be uploaded, instead of uploading the whole object).
For this, I have been trying to make use of the object.equals() function to compare the two objects.
I soon found that the object.equals() does not handle nulls, and after reading this question, I understand why.
So an example of my broken code is as follows:
public class MyObject {
String myString;
String myString2;
public String getChangedVars(MyObject comparisonObj) {
ArrayList<String> changedVars = new ArrayList<String>();
if (!this.myString.equals(comparisonObj.myString))
changedVars.add("myString");
if (!this.myString2.equals(comparisonObj.myString2))
changedVars.add("myString2");
return changedVars.toString();
}
}
My question is - on the basis that either one of the variables being compared could be null, what is a simple way to compare two variables whilst avoiding a NullPointerException?
Edit:
Simply checking for null on both objects first doesn't work well, as I still want to compare if the object has a reference or not. Eg, if one item is null and the other is not, I want this to resolve to true, as the variable has changed.

There is new utility class available in jdk since 1.7 that is Objects .
This class consists of static utility methods for operating on
objects. These utilities include null-safe or null-tolerant methods
for computing the hash code of an object, returning a string for an
object, and comparing two objects.
You can use Objects.equals, it handles null.
Objects.equals(Object a, Object b) Returns true if the arguments are equal to each other and false
otherwise. Consequently, if both arguments are null, true is returned
and if exactly one argument is null, false is returned. Otherwise,
equality is determined by using the equals method of the first
argument.
if(Objects.equals(myString,myString2)){...}

You can use Apache object utils
ObjectUtils.equals(Object object1, Object object2) -- Returns boolean
this method has been replaced by java.util.Objects.equals(Object, Object) in Java 7

if (this.myString != null && this.myString.equals(comparisonObj.myString))
{
changedVars.add("myString");
}

Use StringUtils provided by apache
StringUtils.equals(myString,comparisonObj.myString);

Related

skipping a condition scope in java [duplicate]

This question already has answers here:
comparing arrays in java
(6 answers)
Closed 1 year ago.
I have a program with 2 classes - a Node class and a Problem class.
The Problem class has an PriorityQueue attribute.
The Node class has an char [] state attribute.
I'm trying to run a function at the Problem class with the next input:
(Node node , PriorityQueue q) and check if there is any node in q with the same state as the node. state
public boolean insideQueue (Node node, PriorityQueue<Node> q){
for (Node ele : q) {
if(ele.state == node.state) {
return true;
}
}
return false;
}
I tried to use toString() method to solve the problem, and Im having a problem to understand how I can solve this problem with another way (maybe because I'm using python 95% of my time)
I'm adding a photo from my debugger.
Thank you very much
Java == compares the object references for you, which are char[7]#840 and char[7]#835, so they are different.
Converting to String, and comparing the result as a.equals(b) could work (== would not), but there are pre-defined methods for comparing arrays in the Arrays class, https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html#equals(char[],%20char[]) is the one you could use in particular, so
if(ele.state == node.state) {
becomes
if(Arrays.equals(ele.state, node.state)) {
I have absolutely no idea what you mean by
skipping a condition scope in java
However, the code you wrote makes no sense, so presumably the problem with it is what you're struggling with.
A char[] is a java array; an object (all things in java are objects, except primitives, which are a hardcoded list of 8 types: int, double, boolean - the lowercased stuff. char is a primitive, but arrays of primitives are objects).
All expressions that resolve to an object type are references - they are like an entry in a phone book, and not the phone itself. Merely instructions on how to get there.
== in java compares the raw value. For primitives, that just compares values, but for references (and, remember, all expressions of non-primitive types are references), it compares references, i.e. it doesn't check if it's the same phone number, it checks if it's the same entry in the same phone book.
You can therefore have 2 different objects whose contents are identical; with ==, that would return false, because == checks if they are the same object reference, not if the content inside them is equal.
For that you ordinarily want .equals, but arrays in java are low-level primitive things designed for speed and for access to low-level hardware, and not for actual normal use. I have no idea why you don't just have strings here. At any rate, for normal objects, you just call .equals() on it. For arrays, no such luck, their equals method also is a check for same object identity (for somewhat solid reasons). To compare content, you need Arrays.equals.
So:
if (Arrays.equals(ele.state, node.state)) { ... }
will return true even if ele.state and node.state are pointing at different char arrays, but those char arrays have the exact same content. Which, according to your debugger paste, they do.
Alternatively, don't use arrays unless you really know what you are doing. Those should be strings. Then all you really need is ele.state.equals(node.state).

Comparing String and Integer with equals

The output of the below code is false
String str = "3456";
String str1 = "3456";
System.out.println(Integer.valueOf(str).equals(str1));
I didn't understand it. I thought it will return true. As I am preparing for SCJP, understanding the reason behind it may be helpful. Can someone please help?
An Integer will never be equal to a String.
Both classes have very strict equals() definitions that only accept objects of their respective types.
Integer.equals():
The result is true if and only if the argument is not null and is an Integer object that contains the same int value as this object.
String.equals():
The result is true if and only if the argument is not null and is a String object that represents the same sequence of characters as this object.
That's actually a quite common way to implement equals(): only objects of the same class (and occasionally subclasses) can be equal. Other implementations are possible, but are the exception.
One common exception are the collections such as List: every List implementation that follows the convention will return true when compared to any other implementation, if it has the same content in the same order.
Usually, when implementing equals(), one of the first things to do is to check whether the objects are of one and the same type.
public boolean equals(Object obj) {
if (!(obj instanceof SomeType)) return false;
...
}
This is also applied in the Integer and String classes, which answers the question why do you receive false as a result.
The general contract of the equals() methods states (among other things) that the objects that are being compared need to be of the same class. That's why you'll never be able to compare Apples with Oranges.
For the full contract of the equals() method, see the javadocs.
a Integer Object can't equals with String Object
use :
boolean a = str.equals(str1);
OR
boolean a = (Integer.parseInt(str) == Integer.parseInt(str1));

Must the .equals method for objects be overriden for instances of that object type in order for them to be used as keys in a HashMap

I am having trouble with Hashmaps. Currently, my HashMap is a HashMap of an enum called Names, using a key of Key Signatures, or HashMap<KeySignature, Names>. Currently, the Name Enum stores values of KeySignatures, or C_FLAT_MAJOR(new KeySignature(7, Accidental.FLAT, Scale.MAJOR);. To get the Enum version of a given KeySignature, I've created the HashMap explained above:
private static final HashMap<KeySignature, Names> lookup = new HashMap<KeySignature, Names>();
static {
for (Names name : Names.values()){
lookup.put(new KeySignature(name.getKeySig()), name);
}
}
So, when I need to check what is the Enum version of a KeySignature, I call a method, located in the KeySignature class:
public Names getCommonName() {
return Names.lookup.get(this);
}
However, the value returned is always null.
I cannot figure out what is causing this, but is seems as if the HashMap.get() method is comparing the key and the argument by reference rather than value. Do I have to override the .equals and .hash methods of KeySignature, or am I looking in the entirely wrong direction?
The answer is yes.
If you are going to create instances of KeySignature on the fly, then the equals method needs to compare them "by value". The default implementation of equals simply tests to see if the objects are ==. So, you need get the hashmap to work, you need to override the default equals AND hashcode methods.
The other alternative would be to replace your code that creates new instances of KeySignature with alternative code that looks up an existing KeySignature instance for the given combination of note, Accidental and Scale.

Why isn't my Java LinkedHashSet removing an object it contains?

I have an object in a LinkedHashSet that implements equals, hashCode and compareTo (in a superclass) but when I try to remove that exact object from the set set.remove(obj) the remove method returns false and the object remains in the set. Is the implementation of LinkedHashSet supposed to call the equals() method of its objects? Because it doesn't. Could this be a java bug? I'm running 1.6.0_25.
My guess would be that your object's hashCode() implementation is returning a different value than when you added the object to the set.
LinkedHashSet works fine for me:
import java.util.*;
public class Test {
public static void main( String[] args ) {
LinkedHashSet<String> lhs = new LinkedHashSet<String>();
String s = "hi";
lhs.add( s );
System.out.println( lhs );
lhs.remove( s );
System.out.println( lhs );
}
}
Perhaps you're passing in a reference to a different object to the remove method? Are you sure you didn't change the reference in any way?
Also make sure that hashCode() returns the same value when you insert it as when you are trying to remove it.
The chances of this being a bug in LinkedHashSet are infinitessimnally small. You should dismiss this as a plausible explanation of your problem.
Assuming that this is a bug in your code, then it could be due to a number of things. For instance:
Your equals and hashCode methods are returning contradictory answers for the object.
Your equals or hashCode methods depend on mutable fields and those fields are being changed while the object is in the set. (For instance, if the hashcode value changes, the object is likely to be on the wrong hash chain, causing the remove method to not find it.)
You have declared the equals method as an overload, not an override of equals(Object). (That could explain why your equals is not being called ... assuming that your assertion is factually correct.)
The object you are trying to remove is (in reality) not the one you inserted.
Something else has already removed the object.
You are running a different version of some class that does not match the source code you have been examining.
Now, I know that you have dismissed some of these explanations. But that may have been premature. Review the evidence that you based that dismissal on.
Another approach you could use is to use a Java debugger to forensically examine the data structures (e.g. the innards of the LinkedHashSet) and single-step the code where the deletion is supposed to be happening.

Is Java's assertEquals method reliable?

I know that == has some issues when comparing two Strings. It seems that String.equals() is a better approach. Well, I'm doing JUnit testing and my inclination is to use assertEquals(str1, str2). Is this a reliable way to assert two Strings contain the same content? I would use assertTrue(str1.equals(str2)), but then you don't get the benefit of seeing what the expected and actual values are on failure.
On a related note, does anyone have a link to a page or thread that plainly explains the problems with str1 == str2?
You should always use .equals() when comparing Strings in Java.
JUnit calls the .equals() method to determine equality in the method assertEquals(Object o1, Object o2).
So, you are definitely safe using assertEquals(string1, string2). (Because Strings are Objects)
Here is a link to a great Stackoverflow question regarding some of the differences between == and .equals().
assertEquals uses the equals method for comparison. There is a different assert, assertSame, which uses the == operator.
To understand why == shouldn't be used with strings you need to understand what == does: it does an identity check. That is, a == b checks to see if a and b refer to the same object. It is built into the language, and its behavior cannot be changed by different classes. The equals method, on the other hand, can be overridden by classes. While its default behavior (in the Object class) is to do an identity check using the == operator, many classes, including String, override it to instead do an "equivalence" check. In the case of String, instead of checking if a and b refer to the same object, a.equals(b) checks to see if the objects they refer to are both strings that contain exactly the same characters.
Analogy time: imagine that each String object is a piece of paper with something written on it. Let's say I have two pieces of paper with "Foo" written on them, and another with "Bar" written on it. If I take the first two pieces of paper and use == to compare them it will return false because it's essentially asking "are these the same piece of paper?". It doesn't need to even look at what's written on the paper. The fact that I'm giving it two pieces of paper (rather than the same one twice) means it will return false. If I use equals, however, the equals method will read the two pieces of paper and see that they say the same thing ("Foo"), and so it'll return true.
The bit that gets confusing with Strings is that the Java has a concept of "interning" Strings, and this is (effectively) automatically performed on any string literals in your code. This means that if you have two equivalent string literals in your code (even if they're in different classes) they'll actually both refer to the same String object. This makes the == operator return true more often than one might expect.
In a nutshell - you can have two String objects that contain the same characters but are different objects (in different memory locations). The == operator checks to see that two references are pointing to the same object (memory location), but the equals() method checks if the characters are the same.
Usually you are interested in checking if two Strings contain the same characters, not whether they point to the same memory location.
public class StringEqualityTest extends TestCase {
public void testEquality() throws Exception {
String a = "abcde";
String b = new String(a);
assertTrue(a.equals(b));
assertFalse(a == b);
assertEquals(a, b);
}
}
The JUnit assertEquals(obj1, obj2) does indeed call obj1.equals(obj2).
There's also assertSame(obj1, obj2) which does obj1 == obj2 (i.e., verifies that obj1 and obj2 are referencing the same instance), which is what you're trying to avoid.
So you're fine.
Yes, it is used all the time for testing. It is very likely that the testing framework uses .equals() for comparisons such as these.
Below is a link explaining the "string equality mistake". Essentially, strings in Java are objects, and when you compare object equality, typically they are compared based on memory address, and not by content. Because of this, two strings won't occupy the same address, even if their content is identical, so they won't match correctly, even though they look the same when printed.
http://blog.enrii.com/2006/03/15/java-string-equality-common-mistake/
"The == operator checks to see if two Objects are exactly the same Object."
http://leepoint.net/notes-java/data/strings/12stringcomparison.html
String is an Object in java, so it falls into that category of comparison rules.

Categories