How to write a generic isEmpty method which can check for null, empty?
Solution 1
This sounds like a bad design to me. Null is null, empty is empty, if it's a string it's a string, and so on. Don't try to jam everything up in one method. It's bad for maintainability and readability.
if (str == null || str.isEmpty())
...
and
if (coll == null || coll.isEmpty())
are both perfectly fine.
Personally however, I try to never ever equate null
with an empty string or empty collection. I think it's a bad practice. A null
collection is no collection at all, an empty collection is in fact still a collection. You can avoid many if (coll == null)
checks by keeping a collection non-null. If you're worried about memory consumption, use use Collections.emptySet
et al.
That being said, if you still want to go in this direction, I'd suggest you use plain method overloading and create one isEmpty(Collection<?> coll)
and one isEmpty(String str)
to avoid instanceof and casting.
Regarding your edit:
Don't do for instance
if (value == null || value.isEmpty()) {
return true;
}
return false;
just do
return value == null || value.isEmpty();
Solution 2
For collections, you'll want to use isEmpty()
instead of size()
. For some collection types (such as LinkedList), size() is more expensive than isEmpty().
Solution 3
I like to have a utility class in a common library that handles this. Note that we use the object's own isEmpty, length, or size methods (in that order) if the object has one (after determining the object isn't null). By making calls to this, one doesn't need to worry about NPEs anymore -- you make your calls to this and you are good-to-go -- if it's true, then your collection/map/etc isn't null and has something in it; if it's false, skip over the item (it's either null or empty by it's own account). The second method checks an array to see if it's null or empty, but doesn't check the contents. When you iterate over the array, you simply do a check, then iterate, and as you iterate, check each element.
/**
* Provides methods to perform input validation and boundary validation.
*/
public final class ValidationUtils {
/**
* Check to see if Object is empty or null.
*
* @param object
* The object to check
* @return boolean {@code true} iff the Object is null or determined to be empty (using methods that it provides --
* if it doesn't provide such methods, it's only empty if it's null)
*/
public static boolean isEmpty(@Nullable final Object object) {
if (object == null)
return true;
try {
// Try to use the object class's isEmpty method to check if we're empty, now that we know we're
// not NULL
final Method method = object.getClass().getMethod("isEmpty");
final Object result = method.invoke(object);
if (result instanceof Boolean)
return Boolean.class.cast(result).booleanValue();
} catch (@NotNull final NoSuchMethodException | InvocationTargetException | IllegalArgumentException
| IllegalAccessException | SecurityException ignored) {
// We couldn't invoke... let's go to the next common method
}
try {
// Try to use the object class's length method to check if we're empty, now that we know we're
// not NULL
final Method method = object.getClass().getMethod("length");
final Object result = method.invoke(object);
if (result instanceof Integer)
return Integer.class.cast(result).intValue() <= 0;
if (result instanceof Long)
return Long.class.cast(result).longValue() <= 0L;
} catch (@NotNull final NoSuchMethodException | InvocationTargetException | IllegalArgumentException
| IllegalAccessException | SecurityException ignored) {
// We couldn't invoke... let's go to the next common method
}
try {
// Try to use the object class's size method to check if we're empty, now that we know we're
// not NULL
final Method method = object.getClass().getMethod("size");
final Object result = method.invoke(object);
if (result instanceof Integer)
return Integer.class.cast(result).intValue() <= 0;
if (result instanceof Long)
return Long.class.cast(result).longValue() <= 0L;
} catch (@NotNull final NoSuchMethodException | InvocationTargetException | IllegalArgumentException
| IllegalAccessException | SecurityException ignored) {
// We couldn't invoke... but we're not null... treat it like an Object
}
// Let's treat it like an Object... we're not null, so we're not empty
return false;
}
/**
* Check to see if the array of Objects is empty or null.
*
* @param obj
* Object Array to check
* @return boolean true if empty
*/
public static boolean isEmpty(@Nullable final Object... obj) {
return ((obj == null) || (obj.length == 0));
}
}
Example uses:
final Map<String, String[]> postData = ServletActionContext.getRequest().getParameterMap();
// We're testing if the map is null or empty... we could just do a null check here because of how we're using the map after, but...
if (!ValidationUtils.isEmpty(postData)) {
for (final Map.Entry<String, String[]> reqKey : postData.entrySet()) {
// We're checking if the array is null or doesn't have any length; again, the foreach does the latter, but this is perfectly fine
if (!ValidationUtils.isEmpty(reqKey.getValue())) {
for (final String value : reqKey.getValue()) {
// Checking the value
if (ValidationUtils.isEmpty(value)) {
continue;
}
...
}
}
}
}
Solution 4
As I just wrote in my answer to your other question posted 30 minutes before this one, it is wasteful to check everything every time.
However, these types of functions are still useful in some situations. Instead of using an "is-valid" function, however, I would implement it as a "crash-if-bad" function. Also note that this function is for collections only.
An example use is
CrashIfCollection.badNullLength(coll, "coll", Null.BAD, 1);
Code:
import java.util.Arrays;
import java.util.Collection;
enum Null {OK, BAD};
public class CrashIfCollection {
public static final void main(String[] ignored) {
test(null);
test(Arrays.asList(new String[] {}));
test(Arrays.asList(new String[] {"one element"}));
}
private static final void test(Collection<?> to_test) {
System.out.println("Testing " + ((to_test == null) ? "null"
: Arrays.toString(to_test.toArray())));
try {
CrashIfCollection.badNullLength(to_test, "to_test", Null.BAD, 1);
} catch(Exception x) {
System.out.println(x);
}
}
public static final void badNullLength(Collection<?> coll, String coll_name, Null nullness, int min_len) {
try {
if(nullness == Null.OK) {
if(coll == null) {
return;
}
if(coll.size() < min_len) {
throw new IllegalArgumentException(coll_name + ".size() (" + coll.size() + ") is less than min_len (" + min_len + ")");
}
}
} catch(NullPointerException npx) {
if(nullness == null) {
throw new NullPointerException("nullness");
}
throw npx;
}
//Null.BAD
try {
if(coll.size() < min_len) {
throw new IllegalArgumentException(coll_name + ".size() (" + coll.size() + ") is less than min_len (" + min_len + ")");
}
} catch(NullPointerException npx) {
throw new NullPointerException(coll_name);
}
}
}
Output:
Testing null
java.lang.NullPointerException: to_test
Testing []
java.lang.IllegalArgumentException: to_test.size() (0) is less than min_len (1)
Testing [one element]
john
Updated on July 05, 2022Comments
-
john almost 2 years
I am writing a utility method which can check for empty and null string, or collection or an object or any general types -
public static boolean isEmpty(Object obj) { if (obj == null) return true; if (obj instanceof Collection) return ((Collection<?>) obj).size() == 0; // is below line expensive? final String s = String.valueOf(obj).trim(); return s.length() == 0 || s.equalsIgnoreCase("null"); }
How can I make my above method efficient, since above
isEmpty
method will be called multiple times from the application which is very performance critical?I am suspecting below line will be expensive because of heavy toString methods and it will create temporary garbage as well that might cause GC and slow down the performance?
final String s = String.valueOf(obj).trim();
Update:-
I have separated isEmpty method for each type now. Below is what I got after simplifying the above isEmpty method.
public static boolean isEmpty(Object obj) { if (obj == null) { return true; } return false; } public static boolean isEmpty(Collection<?> value) { if (value == null || value.isEmpty()) { return true; } return false; } public static boolean isEmpty(String value) { if (value == null || value.isEmpty()) { return true; } return false; }
Update 2:-
If I need to check for map null or empty, should I keep both collection isEmpty and Map isEmpty method both or Collection isEmpty method will be fine for that?
public static void main(String[] args) { Map<String, String> hello = new HashMap<String, String>(); System.out.println(isEmpty(hello)); Map<String, HashMap<Integer, String>> primary = new HashMap<String, HashMap<Integer, String>>(); System.out.println(isEmpty(primary)); } public static boolean isEmpty(Collection<?> value) { return value == null || value.isEmpty(); } public static boolean isEmpty(Map<?, ?> value) { return value == null || value.isEmpty(); }
-
aioobe almost 10 yearsThere may be cases where you're right, but I'd be extremely surprised if
size()
was noticeably slower thanisEmpty()
for linked list. -
Steve Kuo almost 10 yearsMany (if not all) of Google Guava's collections are lazily evaluated. Calling size() will cause it to evaluate (walk all elements in the case of a filtered collection). In this case, all one cares about is 0 or non-0. Why compute the exact size only to not use that information?
-
aioobe almost 10 yearsGood to know. I still stand by my comment though.
-
john almost 10 years@aioobe Thanks for your suggestion. I learned something new today. I have updated the question with your suggestion. Can you let me know that's what you meant?
-
aioobe almost 10 yearsYea. Your update is nicer than your original suggestion, but I'd still reconsider allowing
null
at all. -
john almost 10 yearsThanks aioobe. I didn't understand this line of yours
still reconsider allowing null at all
. What do you mean by that? -
aioobe almost 10 yearsIf you let
null
and""
mean the same thing, you'll always have to check fornull
when you want to use the string. Say for instance you want to take the length of a string to make sure its at least 8 characters long. Then you'd have to doif (str == null || str.length() < 8) { ... }
. If you instead say, I will make sure mystr
is nevernull
and whenever I'm about to set it to null, I'll set it to""
instead, you'll never need to cover special cases by doingif (str == null)
. It just easier for you to write the code and for other to read the code if you maintain such invariant. -
Hannes almost 10 years@aioobe To determine the size of a LinkedList, you have to walk the whole list. To determine if it's empty, you only have to check the existence of an element. O(1) beats O(n).
-
john almost 10 yearsMakes sense now. Thanks a lot aioobe for the help. After I edited my actual code to use isEmpty like the way you suggested, performance of my application improved a lot from 4 ms to 0.6 ms as 95th percentile. I cannot imagine, such a small thing can make this big difference. :)
-
john almost 10 years@aioobe One last thing. I have added another update in my question. If I need to check Map for null and empty, should I keep both isEmpty method, one for Collection and other for Map? Or collection isEmpty one will be fine?
-
aioobe almost 10 yearsA Map is not a collection so that would require a method of its own.
-
David Ehrmann almost 10 years"I try to never ever equate null with an empty string or empty collection. I think it's a bad practice." This.
-
aioobe almost 10 yearsNo, the LinkedList (obviously) maintains a size property.
size()
is O(1): Link to java/util/LinkedList.java. -
pqsk over 9 years@aioobe that may be the case for LinkedList, but if you have the option to do a guaranteed O(1) vs O(n), where n could be millions why take the risk with a generic method for all types of collections? I hope you don't have code like that in production if you use a similar generic method.
-
aioobe over 9 yearsUsing isEmpty is obviously better than size. I've never claimed anything else.