When I type
string1 == string2
IntelliJ tells me to switch to equals(), which it says is null-safe.
But is == operator not null-safe?
I tried null == "abc", "abc" == null, null == null, but they consistently gave me right false false true.
What am I missing here?
Since Java 7 you can use the static method java.util.Objects.equals(Object, Object) to perform equals checks on two objects without caring about them being null.
If both objects are null, it will return true, if one is null and the other isn't, it will return false. Otherwise, it will return the result of calling equals on the first object with the second as argument.
This is what Java internal code uses (on other compare methods):
public static boolean compare(String str1, String str2) {
return (str1 == null ? str2 == null : str1.equals(str2));
}
Videos
I've already discovered that comparing a generic type requires you to use an int to restore the value of compareTo, but if I'm trying to check if T value == Null, java throws an error stating that the compareTo (null) method is undefined for type T.
the code I have so far is
int cmp; if(cmp = tree.get(index).compareTo(null) == 0)
to check if the value at the index of tree == null.
Thanks in advance :)
edit: well, I actually just fixed it by deleting cmp XD
You're not comparing the objects themselves, you're comparing their references.
Try
c != null
in your if statement.
!c.equals(null)
That line is calling the equals method on c, and if c is null then you'll get that error because you can't call any methods on null. Instead you should be using
c != null
You can simply use Apache Commons Lang:
result = ObjectUtils.compare(firstComparable, secondComparable)
Using Java 8:
private static Comparator<String> nullSafeStringComparator = Comparator
.nullsFirst(String::compareToIgnoreCase);
private static Comparator<Metadata> metadataComparator = Comparator
.comparing(Metadata::getName, nullSafeStringComparator)
.thenComparing(Metadata::getValue, nullSafeStringComparator);
public int compareTo(Metadata that) {
return metadataComparator.compare(this, that);
}
it is possible to handle null pointer exception using Comparator.comparing static method. If you want to comparing loanAmount fields in your objects and you want that every not null value is greater than null you can use Comparator.nullFirst method in combination with Comparator.comparing like the below code:
Comparator.comparing(LoanAccount::getLoanAmount,
Comparator.nullsFirst(Comparator.naturalOrder()))
NaturalOrder is a method returning a comparator that compares Comparable objects in natural order.
You can also combine more comparators in a chain with method Comparator.thenComparing like the code below:
Comparator
.comparing(LoanAccount::getLoanAmount,
Comparator.nullsFirst(Comparator.naturalOrder()))
.thenComparing(LoanAccount::getCreationDate,
Comparator.nullsFirst(Comparator.naturalOrder()))
.compare(o1, o2);
Now you can rewrite your comparator with equal behaviour shortly:
public class LoanAccountAmountComparator
implements Comparator<LoanAccount> {
@Override
public int compare(LoanAccount o1, LoanAccount o2) {
if (o1 == null && o2 == null) return 0;
if (o1 == null) return -1;
if (o2 == null) return 1;
return Comparator
.comparing(LoanAccount::getLoanAmount,
nullsFirst(naturalOrder()))
.thenComparing(LoanAccount::getCreationDate,
nullsFirst(naturalOrder()))
.compare(o1, o2);
}
}
Note: the use of class Date is discouraged, it is better if you use instead the java.time package classes for time related code.
Here is another twist, roughly similar to dariosicily's proposal.
It differs in the following points:
- It create the aggregated comparator only once, then store it in a static field. Why? Because functions such as
Comparator.nullsFirst()andComparator.comparing()implies object allocation, which you definitely want to avoid if your comparator is called from tight loops (for example, a sort or tree insertion algorithm). - Null checks on the LoadAccount objects themselves have also been delegated to
Comparator.nullsFirst(). That means that absolutely noifstatement is required! - I moved that comparator to a public, static field of a utility class. I have learned from personal experience that comparators on domain model objects very often come in "families". In different places, you want different sorting strategies, for the same objects. Here, for demonstration, I also included one comparator for
loadAmount(that is, without fallback on tie), and another one forcreationDate. But you can see how this idea can be generalized. - In this sample, I have adopted an uncommon indentation strategy. I think this is justified in this case because it helps make it easier to see which fields are sorted by each comparator, and in which order. This is of great importance when you have comparators that involve a significant number of fields.
public class LoanAccountAmountComparators {
/**
* A comparator that sort LoanAccounts, by load's amount, in decreasing order.
*/
public static final Comparator<LoanAccount> BY_AMOUNT =
Comparator.nullsFirst(
Comparator.comparing(
LoanAccount::getLoanAmount, Comparator.nullsFirst(Comparator.reverseOrder())
);
);
/**
* A comparator that sort LoanAccounts, by creation date, in ascending order.
*/
public static final Comparator<LoanAccount> BY_DATE =
Comparator.nullsFirst(
Comparator.comparing(
LoanAccount::getCreationDate, Comparator.nullsFirst(Comparator.naturalOrder())
);
);
/**
* A comparator that sort LoanAccounts, by creation amount (in descending order),
* then by date (in ascending order).
*/
public static final Comparator<LoanAccount> BY_AMOUNT_AND_DATE =
Comparator.nullsFirst(
Comparator.comparing(
LoanAccount::getLoanAmount, Comparator.nullsFirst(Comparator.reverseOrder())
).thenComparing(
LoanAccount::getCreationDate, Comparator.nullsFirst(Comparator.naturalOrder())
);
);
}
It should be noted that, in this example, all fields involved are indeed some kind of objects. If, however, your comparator would involve fields containing primitive types, then you should use the corresponding Comparator.comparing<primitiveType> function (that is, never let some primitive be boxed to object only so it can be compared to Comparator.naturalOrder().
You could write the code like this, it is doing the same, but I think it is more readable, you almost don't need any comment, to assume the return value.
private Integer compareDateStrings(BeanToDoTask arg0, BeanToDoTask arg1, String strProperty) {
String strDate0 = BeanUtils.getProperty(arg0, strProperty);_logger.debug("strDate0 = " + strDate0);
String strDate1 = BeanUtils.getProperty(arg1, strProperty);_logger.debug("strDate1 = " + strDate1);
return compareDateStrings(strDate0, strDate1);
}
private Integer compareDateStrings(String strDate0, String strDate1) {
int cmp = 0;
if (isEmpty(strDate0)) {
if (isNotEmpty(strDate1)) {
cmp = -1;
} else {
cmp = 0;
}
} else if (isEmpty(strDate1)) {
cmp = 1;
} else {
cmp = strDate0.compareTo(strDate1);
}
return cmp;
}
private boolean isEmpty(String str) {
return str == null || str.isEmpty();
}
private boolean isNotEmpty(String str) {
return !isEmpty(str);
}
I would use boolean variables to make the code more readable:
private int compareDateStrings(BeanToDoTask arg0, BeanToDoTask arg1, String strProperty) {
/* Don't worry too much about this part. */
String strDate0 = BeanUtils.getProperty(arg0, strProperty); _logger.debug("strDate0 = " + strDate0);
String strDate1 = BeanUtils.getProperty(arg1, strProperty); _logger.debug("strDate1 = " + strDate1);
boolean isStrDate0Empty = (strDate0 == null || strDate0.isEmpty());
boolean isStrDate1Empty = (strDate1 == null || strDate1.isEmpty());
if (isStrDate0Empty && isStrDate1Empty)
return 0;
// at least one of them is not empty
if (isStrDate0Empty)
return -1;
if (isStrDate1Empty)
return 1;
//none of them is empty
return strDate0.compareTo(strDate1);
}
Check that the String s is not null before doing any character checks. The characters returned by String#charAt are primitive char types and will never be null:
if (s != null) {
...
If you're trying to process characters from String one at a time, you can use:
for (char c: s.toCharArray()) {
// do stuff with char c
}
(Unlike C, NULL terminator checking is not done in Java.)
Default value to char primitives is 0 , as its ascii value. you can check char if it is null. for eg:
char ch[] = new char[20]; //here the whole array will be initialized with '\u0000' i.e. 0
if((int)ch[0]==0){
System.out.println("char is null");
}
From javadoc for Comparable
Note that null is not an instance of any class, and e.compareTo(null) should throw a NullPointerException even though e.equals(null) returns false.
Yes, there is no problem allowing null for instance fields - just make sure its sorting order is defined. Most natural would be putting it either before or after all real strings, but you could do anything here, just do it consistently. (For example, you could sort null like "null".)
Here is an example implementation for a single member:
class Example implements Comparable<Example> {
@Nullable
private String member;
// TODO: getter, setter, constructor, ...
public int compareTo(Example that) {
if(this.member == null)
if(that.member == null)
return 0; //equal
else
return -1; // null is before other strings
else // this.member != null
if(that.member == null)
return 1; // all other strings are after null
else
return this.member.compareTo(that.member);
}
}
Please note that the specification of Comparable.compareTo() only has a constraint for o.compareTo(null) (which should behave just like - null.compareTo(o), i.e. throw a NullPointerException), but not about how null fields are handled (it doesn't mention fields at all, so a class could return whatever it wants, as long as the antisymmetry, reflexivity and transitivity is ensured).