6

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();
}
9
  • 1
    What about array and map? Commented Jul 5, 2014 at 22:22
  • @BheshGurung Yes, that's a good suggestion. I haven't coded for array and map but I need those as well. Commented Jul 5, 2014 at 22:23
  • If this is performance critical, use strict typing. Casting the collection to Object and then back to collection will prevent many JVM optimizations. Commented Jul 5, 2014 at 22:23
  • @Banthar Can you provide an example on that, how would I do that? Commented Jul 5, 2014 at 22:24
  • 1
    @user2809564 basically separate method for each type. isEmpty(Collection<?>), isEmpty(String) and so on. Commented Jul 5, 2014 at 22:26

5 Answers 5

12

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();
Sign up to request clarification or add additional context in comments.

8 Comments

@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?
Yea. Your update is nicer than your original suggestion, but I'd still reconsider allowing null at all.
Thanks aioobe. I didn't understand this line of yours still reconsider allowing null at all. What do you mean by that?
If you let null and "" mean the same thing, you'll always have to check for null 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 do if (str == null || str.length() < 8) { ... }. If you instead say, I will make sure my str is never null 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 doing if (str == null). It just easier for you to write the code and for other to read the code if you maintain such invariant.
"I try to never ever equate null with an empty string or empty collection. I think it's a bad practice." This.
|
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().

7 Comments

There may be cases where you're right, but I'd be extremely surprised if size() was noticeably slower than isEmpty() for linked list.
Many (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?
Good to know. I still stand by my comment though.
@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).
No, the LinkedList (obviously) maintains a size property. size() is O(1): Link to java/util/LinkedList.java.
|
2

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

                    ...
                }
            }
        }
    }

Comments

1

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]

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.