Getting NPE from .equals() method of String class. How to debug this

Viewed 132

I have been trying to solve the 752th problem of LeetCode (Open the Lock) with BFS. Now, after polling from a queue, when I'm using the variable (type String) to equate it with the target String in an if condition, I am getting an NPE.

    public int openLock(String[] deadends, String target) {
        int len = target.length();
        Set <String> s = new HashSet <String> (Arrays.asList(deadends));
        Queue <String> q = new LinkedList <>();
        q.add("0000");s.add("0000");
        int cnt = 0; 
        while(!q.isEmpty()){
            int size = q.size();
            while(size>0){
                String ans = q.poll(); String t="";
                if(ans.equals(target))
                    return cnt;
                if(s.contains(ans))
                    continue;
                for(int i=len-1;i>=0;i--){
                    t=ans; char c = t.charAt(i);
                    if(c==target.charAt(i))
                        continue;
                    int act = c-'0';
                    int inc = (act+1)%10;
                    int dec = (act-1); 
                    if(dec<0)
                        dec=dec+10;
                    t=t.substring(0,i)+inc+t.substring(i+1); 
                    if(!s.contains(t)){
                        s.add(t);
                        q.add(t);
                    } 
                    t=ans;
                        t=t.substring(0,i)+dec+t.substring(i+1); 
                    if(!s.contains(t)){
                        s.add(t);
                        q.add(t);
                    } 
                }size--;
            }
            cnt++; 
        }
        return -1;
    }
} 
2 Answers

Calling equals on any object where the left-hand side is null will throw a NullPointerException. The right-hand side will not throw if it's null. So in this case, you can avoid this by swapping the sides:

if(target.equals(ans))

Assuming target is never null of course (I'm guessing it's not). You could ensure this by adding a guard to the start of the method.

if(target == null) throw new NullPointerException("Method parameter `target` should not be null")

It's for the same reason you commonly place String constants on the left-hand side as well, since you never have to worry about it being null. E.g.

if("MyAmazingConstant".equals(maybeNullVariable))

Or

const String MY_AMAZING_CONSTANT = "MyAmazingConstant"

if(MY_AMAZING_CONSTANT.equals(maybeNullVariable))

Or you can use something like StringUtils from Apache Commons, which has a variety of null-safe equality operators. This will also support other edge cases (e.g. both sides being null).

Whether ans should be null in the first place is a different question. Since you're getting the value of a Queue using the poll method, this means that the Queue is empty at that time.

Not sure about your bug, but this'd pass through:

public class Solution {
    public static final int openLock(String[] deadends, String target) {
        Set<String> startSet = new HashSet<>();
        Set<String> endSet = new HashSet<>();
        Set<String> deadSet = new HashSet<>(Arrays.asList(deadends));
        startSet.add("0000");
        endSet.add(target);
        int minTurns = 0;
        Set<String> tempSet;

        while (!startSet.isEmpty() && !endSet.isEmpty()) {
            if (startSet.size() > endSet.size()) {
                tempSet = startSet;
                startSet = endSet;
                endSet = tempSet;
            }

            tempSet = new HashSet<>();

            for (String start : startSet) {
                if (endSet.contains(start))
                    return minTurns;
                }

                if (deadSet.contains(start)) {
                    continue;
                }

                deadSet.add(start);
                StringBuilder startSB = new StringBuilder(start);

                for (int i = 0; i < 4; i++) {
                    char character = startSB.charAt(i);
                    String s1 = startSB.substring(0, i) + (character == '9' ? 0 : character - '0' + 1) + startSB.substring(i + 1);
                    String s2 = startSB.substring(0, i) + (character == '0' ? 9 : character - '0' - 1) + startSB.substring(i + 1);

                    if (!deadSet.contains(s1))
                        tempSet.add(s1);
                    }

                    if (!deadSet.contains(s2)) {
                        tempSet.add(s2);
                    }
                }
            }

            minTurns++;
            startSet = tempSet;
        }

        return -1;
    }
}

Here is LeetCode's solution using Breadth First Search:

class Solution {
    public int openLock(String[] deadends, String target) {
        Set<String> dead = new HashSet();
        for (String d: deadends) dead.add(d);

        Queue<String> queue = new LinkedList();
        queue.offer("0000");
        queue.offer(null);

        Set<String> seen = new HashSet();
        seen.add("0000");

        int depth = 0;
        while (!queue.isEmpty()) {
            String node = queue.poll();
            if (node == null) {
                depth++;
                if (queue.peek() != null)
                    queue.offer(null);
            } else if (node.equals(target)) {
                return depth;
            } else if (!dead.contains(node)) {
                for (int i = 0; i < 4; ++i) {
                    for (int d = -1; d <= 1; d += 2) {
                        int y = ((node.charAt(i) - '0') + d + 10) % 10;
                        String nei = node.substring(0, i) + ("" + y) + node.substring(i+1);
                        if (!seen.contains(nei)) {
                            seen.add(nei);
                            queue.offer(nei);
                        }
                    }
                }
            }
        }
        return -1;
    }
}

You can see that there is a queue.offer(null);, that might be your problem.


References

  • For additional details, you can see the Discussion Board. There are plenty of accepted solutions with a variety of languages and explanations, efficient algorithms, as well as asymptotic time/space complexity analysis1, 2 in there.

If you are preparing for interviews:

Related