Search code examples
javacomparable

Cannot read field "value" because "anotherByte" is null


Here I have a Quick Sort algorithm. The base class has the function isLessThan()

abstract public class Sort<T extends Comparable<T>> {
    
    protected T[] array;

    public boolean isLessThan(T obj1, T obj2) {
        if (obj1.compareTo(obj2) < 0)
            return true;
        else
            return false;
    }
    
    abstract public void sort(T[] array);
}

The actual algorithm picks a partition, splits the array into high, low and then recursively partitions the high and low arrays. However I cannot get past the part where I add elements to high and low:

public class Quick<T extends Comparable<T>> extends Sort<T> {
    T[] low, high;
    
    public void addLow(T element, int index) {
        low[index] = element;
    }
    public void addHigh(T element, int index) {
        high[index] = element;
    }
    
    public T[] partition(T[] arr, T partition) {
        int lowCount = 0;
        int highCount = 0;
        
        low = (T[]) new Comparable[arr.length];
        high = (T[]) new Comparable[arr.length];
        // here I am adding each element to either high or low
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] != null) {
                if (isLessThan(arr[i], partition)) {
                    addLow(arr[i], i);
                    lowCount++;
                } else {
                    addHigh(arr[i], i);
                    highCount++;
                }
                
            }
        }
        // the rest of this code probably isn't relevant to my problem
        // sort low, then high
        if (lowCount > 1) {
            low = partition(low, low[0]);

        }
        if (highCount > 1) {
            high = partition(high, high[0]);
        }

        // merge
        T[] arr2 = (T[]) new Comparable[low.length + high.length];
        int count = 0;
        for(int i = 0; i < low.length; i++) { 
            arr2[i] = low[i];
             count++;
          } 
          for(int j = 0; j < high.length;j++) { 
              arr2[count++] = high[j];
          } 
        
        
        return arr2;
    }
    
    public void sort(T[] arr)
    {
        T partition = arr[0];
        
        arr = partition(arr, partition);
        
        
        
        //set super to array
        super.array = arr;
        
    }
}

For some reason, I get this error when calling isLessThan():

Exception in thread "main" java.lang.NullPointerException: Cannot read field "value" because "anotherByte" is null
    at java.base/java.lang.Byte.compareTo(Byte.java:490)
    at java.base/java.lang.Byte.compareTo(Byte.java:56)
    at Sort.isLessThan(Sort.java:6)
    at Quick.partition(Quick.java:20)
    at Quick.partition(Quick.java:33)
    at Quick.sort(Quick.java:59)
    at Main.main(Main.java:7)

What is causing this error?

My main looks like this:

public class Main {

    public static void main(String[] args) {
        Byte[] array = {121, 25, 44, 17, 30, 55, 29, 7, 81, 45, 79, 41, 108, 60, 83, 29, 77, 5, 17, 110};

        Quick s = new Quick();
        s.sort(array);
    }

}

Solution

  • Your assumption the rest of this code probably isn't relevant to my problem is wrong. Because it is low[0] that is null indeed.

        // the rest of this code probably isn't relevant to my problem
        // sort low, then high
        if (lowCount > 1) {
            System.out.println("LOW: " + low[0]);
            low = processPartition(low, low[0]);
        }
        
        if (highCount > 1) {
            System.out.println("HIGH: " + high[0]);
            high = processPartition(high, high[0]);
        }
    

    You instantiate both arrays and say they have the same dimension as the original array. This is okay.

        low = (T[]) new Comparable[arr.length];
        high = (T[]) new Comparable[arr.length];
    

    But your mistake is, that you use the wrong indexing approach! You take the index from the for-loop and pass it as index to the arrays.

    for (int i = 0; i < arr.length; i++) {
       if (arr[i] != null) {
          if (isLessThan(arr[i], partition)) {
              addLow(arr[i], i);
              lowCount++;
          } else {
               addHigh(arr[i], i);
               highCount++;
          }
     }
    

    But what happens?

    With the very first comparison you have 121 on 121. This is not less than but equal. In case of higher or equal you go into the else-part and call addHigh().

    So index 0 is gone for a High.

    Now the comparison goes on and on and all the others are LessThan() because 121 is the highest number in your list.

    But, as you hand in the index from the loop, the first LessThan()-value does not go into low[0] but low[1]. low[0] is null!

    And then you later call low = processPartition(low, low[0]); and get your NPE.

    So you have at least 1 thing to do:

    DON'T USE the index i out of your for-loop! That's wrong!

    DO USE your counter lowCount and highCount instead! They will always point to the correct field in the array!

    Please note, that this will only explain why you get the first NPE here and help you to solve it.

    When you run this code the same NPE-message will occur, but at another point in the process. This time, because you alway pass the arrays around and constantly change them. When the code somewhen comes back to the very first round and proceeds, it will find highCount > 1 and tries to process the high[]-array. But this has changed in the meantime and high[0] is null.

    You have to rethink you entire sorting approach here.

    I hope this helps at least to figure out what went wrong in the first place.