Frage

Trying to get a function to fill multiple arrays with values. Would love to be able to do this:

public class Demo {
  public static void getData(int[] array1, int[] array2)  {
    array1 = new int[5];
    array2 = new int[5];
  }

  public static void main(String[] args)  {
    int[] array1 = null;
    int[] array2 = null;
    getData(array1, array2); 
    System.out.println(array1[0]); // line 11
  }
}
Exception in thread "main" java.lang.NullPointerException
  at Demo.main(Demo.java:11)

This gives a NullPointerException, but I'm not exactly sure why. I can't allocate the arrays in main() because there's no way to know in advance how large they'll be. Could use a List, or a 2D array, but is there a way to do this without either?

War es hilfreich?

Lösung

As several other commentators have mentioned, the problem here is misunderstanding the notion of pass-by-reference. Without getting into the nitty-gritty, as a rule of thumb any time you say a = ... where a is an Object (which includes arrays) you are disconnecting the name a from a previous value, and pointing it instead at your new value. There is no connection between the two, and no way to reference past values of a. So in your case, by setting array1 and array2 to new values you are no longer referencing the objects passed into the function. In this case the function arguments are null, not actual objects, but that doesn't change anything here.

The more important question however is what to do about this? Obviously the current design is unworkable, but how exactly can we do this? It's a common problem, so unsurprisingly there are many viable options.

Use static variables

The easiest thing you can do here is simply ensure both main() and getData() are working with the same variable, by using static class variables both methods can reference. While easy for simple projects, this breaks down quickly and starts introducing more problems than it solves as your project's size and scope gets bigger. Use with caution.

public class Demo {
  public static void getData(int[] array1, int[] array2)  {
    array1 = new int[5];
    array2 = new int[5];
  }

  public static void main(String[] args)  {
    int[] array1 = null;
    int[] array2 = null;
    getData(array1, array2); 
    System.out.println(array1[0]); // line 11
  }
}

Use a dedicated object, and a pass around references to that object

Alternatively, we can avoid static variables by constructing a single holder object and passing around references to it. We can then freely change the instance variables inside our holder.

public class Holder {
  // Should really be private, with constructors/getters/setters
  // but for brevity we'll access them directly here.  Don't
  // take this shortcut in production code
  public int[] array1;
  public int[] array2;
}

public class Demo {
  public static void getData(Holder holder) {
    holder.array1 = new int[5];
    holder.array2 = new int[5];
  }

  public static void main(String[] args)  {
    Holder holder = new Holder();
    getData(holder);
    System.out.println(holder.array1.length+" "+holder.array2.length);
  }
}

Use a resizable data structure

You mention your concern is you don't know ahead of time the size of the loaded data, which makes this a perfect candidate for a List or other data structure. You also mention you don't want to do this, which is fine, but be really sure you actually need arrays - they provide very few benefits over proper collection types, and many more hassles. Using a resizable data structure lets us construct and populate the same object separately.

import java.util.*;

public class Demo {
  public static void getData(List<Integer> ls1, List<Integer> ls2) {
    // bad formatting for brevity, don't do this either
    ls1.add(1); ls1.add(2); ls1.add(3); ls1.add(4); ls1.add(5); 
    ls2.add(1); ls2.add(2); ls2.add(3); ls2.add(4); ls2.add(5);
  }

  public static void main(String[] args)  {
    List<Integer> ls1 = new ArrayList<>();
    List<Integer> ls2 = new ArrayList<>();
    getData(ls1, ls2);
    System.out.println(ls1.size()+" "+ls2.size());
  }
}

Compartmentalize this behavior entirely into a proper object

All of the above are reasonable options given specific use cases, but they all pale in comparison to the robustness, power, and code-safety of creating a proper object and compartmentalizng the behavior entirely. What do I mean by this? Instead of having a getData() method which applies some business logic to some existing variables, group the variables and the logic together into an object - this is the core tenant of Object Oriented Programming.

public class Data {
  // Again, getters/setters, but at least these are final, that's an improvement
  public final int[] array1;
  public final int[] array2;

  public Data() {
    array1 = new int[5];
    array2 = new int[5];
  }
}

public class Demo {
  public static void main(String[] args)  {
    Data data = new Data();
    System.out.println(data.array1.length+" "+data.array2.length);
  }
}

By using a dedicated class, we hide the logic of building this data from the caller. Instead, the main() method simply constructs a Data() object, and can trust simply by constructing the object that it now has all the data it needs. No need to worry about references, array sizes, or anything else; all those decisions are dealt with correctly internally and hidden away from the caller.

Andere Tipps

Java will always pass the reference to your array declared in main so not initializing it will always get an NPE. I think you should just use List for this since you need your method to be able to define the size. Just convert to array if you need the final output to be an array.

array1 = new int[5];
array2 = new int[5];//this will create variable in function body scope

that is the reason you are getting null pointer

try:

public static void main(String[] args)  {
    int[] array1 = new int[5];
    int[] array2 = new int[5];
    getData(array1, array2); 
}

public static void getData(int[] array1, int[] array2)  {
    // fill array1 and array2 with data
}

the way you are trying to achieve it, is not a possible way in Java

You could make an array with length 0:

public void getData(int[] array1, int[] array2)  {
   array1 = new int[5];
   array2 = new int[5];
}
public void main(String[] args)  {
   int[] array1 = new int[0];
   int[] array2 = new int[0];
getData(array1, array2); 
}
// fill array1 and array2 with data
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top