0

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?

dimo414
  • 47,227
  • 18
  • 148
  • 244
Eric Walton
  • 27
  • 1
  • 6
  • 4
    [Is Java pass-by-reference or pass-by-value?](http://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value) – Sotirios Delimanolis May 15 '14 at 00:13
  • 3
    Post the actual stack trace you are seeing. The code you've pasted shouldn't cause any sort of `NullPointerException` (though it also won't compile, since your `getData()` method isn't `static`). – dimo414 May 15 '14 at 00:19
  • @dimo414 Neither is `main` though :) – Sotirios Delimanolis May 15 '14 at 00:21
  • I suggest that you learn about classes. – Code-Apprentice May 15 '14 at 00:22
  • 2
    @SotiriosDelimanolis touche :) not a very helpful code snippet at all... – dimo414 May 15 '14 at 00:25
  • You need to understand how call/return works in Java. Parameters are passed by value, so updating them in the called method has no effect on what is see in the calling method. – Hot Licks May 15 '14 at 00:32
  • I should've asked "is this possible somehow in Java?". I do understand pass-by-value (that's a great discussion on it @Sotirios), just wanting to see the alternatives. And so shoot me, I forgot to type in 'static'. – Eric Walton May 15 '14 at 00:44
  • @EricWalton forgetting the `static`s is forgivable, but the code you've posted will not raise a `NullPointerException`. How can we hope to help explain (beyond guessing blindly) why you get a NPE if you provide neither the stack trace nor the code to replicate it? – dimo414 May 15 '14 at 02:06
  • @dimo414 Simply adding `System.out.println(array1[0])` gets the error. And as you can see, the stack trace is useless (line 32 was where i had the System.out). In any case, I found a decent solution, so thx. – Eric Walton May 15 '14 at 03:31
  • @EricWalton awesome, that does indeed demonstrate the problem you alluded to. I've updated your question to contain the NPE-generating call. When trying to debug a problem, providing [runnable examples](http://www.sscce.org/) helps everyone, no matter how trivial it may seem to you. – dimo414 May 15 '14 at 04:25

4 Answers4

2

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.

dimo414
  • 47,227
  • 18
  • 148
  • 244
  • Awesome description, esp. breaking it down so clearly. With tricky concepts like this one, it's helpful (I think) to hear and read it explained several different ways. When you attack it from a different angle, you have another shot at a "lightbulb" moment. Your explanation (particularly the `a = ...` at the top) got me to 100%. For some reason that approach clicked. Thx for stopping by. – Eric Walton May 16 '14 at 16:43
0
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

dimo414
  • 47,227
  • 18
  • 148
  • 244
dev2d
  • 4,245
  • 3
  • 31
  • 54
0

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.

-2

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
  • 2
    Please explain what this changes. – Sotirios Delimanolis May 15 '14 at 00:22
  • In the poster's defense the original question didn't explain how the arrays were being used, and broadly speaking using a non-null default value can help avoid NPEs. You still have to use the arrays correctly of course, but that's a broader question. – dimo414 May 15 '14 at 04:29