Home » Java » How to write a generic isEmpty method which can check for null, empty?

How to write a generic isEmpty method which can check for null, empty?

Posted by: admin December 28, 2021 Leave a comment

Questions:

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();
}
Answers:

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();

###

For collections, you’ll want to use isEmpty() instead of size(). For some collection types (such as LinkedList), size() is more expensive than isEmpty().

###

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;
                    }

                    ...
                }
            }
        }
    }

###

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]

###

You can try by doing this. If you have a size as a private integer. You could do the following:

public boolean isEmpty()
{
    if(size == 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

This worked for me.