tags:

views:

775

answers:

5

I have the following fucntion.

func(ArrayList <String>[] name) { ........ }
The function fills the ArrayList[]. (I don't want to return the ArrayList[])

However, in the caller function the ArrayList[] obtained has all ArrayLists as null.
For eg.

1 name = new ArrayList[num];
2 func(name);
3 System.out.println(name[0]);

I get NullPointerException at line 3. Is this because of line 1, i.e. I am not parametrizing? If yes, is there another way this can be done? Because java does not allow creating a generic array of parametrized ArrayList.

+1  A: 

That is obviously not your real code, but you're creating an array of ArrayLists, which probably isn't what you want. You can probably just do:

ArrayList<String> name = new ArrayList(num);
func(name);
System.out.println(name.get(0));

Note that when you create the ArrayList, you're only specifying the initial capacity, not the size (number of initial items). It will have an initial size of 0. Your func can just call add to add items.

Matthew Flaschen
+1  A: 

Even better (no typing errors):

ArrayList<String> name = new ArrayList<String>();

I recommend not bothering with the initial capacity argument (num) - just leave it blank and it will work perfectly. But do bother with the generic type of String in the constructor, or the compiler will complain.

If you want to know how to use the ArrayList (for example, why to use the get() function), you should look at the documentation.

John
Why shouldn't he give an initial capacity? It will improve performance, particularly on large lists.
Matthew Flaschen
I agree that it could improve performance. But I assessed from the initial question that simplicity and correctness is much more likely to be important to the person asking the original question, than performance.
John
A: 

For arrays in Java when you create it all of the elements are either 0, false, or null depending in their type.

So:

final List<String>[] foo;

foo = new ArrayList<String>[10];
foo[0].add("hello"); // crash

that crashes because foo = new ArrayList>String>[10]; allocates enough room to hold 10 ArrayListss but it sets all of the values to null. So you need one additional step:

for(int i = 0; i < foo.length; i++)
{
    foo[i] = new ArrayList<String>();
}

I haven't compiled the code, but pretty sure it is all correct. You would do that between step 1 and 2 of your program.

I am guessing a bit because your code isn't quite accurate (it would not generate a null pointer as written as near as I can tell).

EDIT:

You would do the new in the method and the for loop with the assignments could be done inside of the method. I prefer to allocate and initialize in the same place (less confusing) but you can split it up if you needed to.

TofuBeer
foo[i] = new ArrayList<String>[...] is being done inside the function func. What I do not understand is that before the function func returns, I can access the elements. For eg. name[0] is not null. But when I access name[0] in the caller function it is null.
athena
can you post the actual code for that method?
TofuBeer
One thing to also try is to mark the parameter to the method as final (like void foo(final List<String>[] bar) just to make sure you are not accidentally reassigning it instead of just modifying the contents.
TofuBeer
No, that code is not correct. You can not create arrays of generic types, i.e. `new ArrayList<String>[10]` will not work. `new ArrayList<?>[10]` and `new ArrayList[10]` would, though.
Bombe
A: 

The problem you are encountering is due to the fact that in Java, parameters to methods are passed by value. What this means, is that every parameter is effectively "copied" into the method, meaning that any assignments you make to the parameters are only visible within the method, and cannot be seen by the caller.

Going by your example, you're passing in a null reference to an array of List<String>'s. This reference is then "copied" into the func() method, and when func then assigns something to this variable, it is only the local variable that is being updated, and not the reference held by your calling code.

Here's some compilable code (based on your example) that demonstrates the problem:

public class Main {

    public static void main(String[] args) {
        List<String>[] array = null;
        fill(array);
        System.out.println("In main(): " + array[0].get(0));
    }

    public static void fill(List<String>[] array) {
        array = (List<String>[])new List[10];
        array[0] = new ArrayList<String>();
        array[0].add("test");
        System.out.println("In fill(): " + array[0].get(0));
    }

}

The println in fill prints the correct value, because the array variable has been assigned to something within the fill method, however the println in the main method throws an NPE because only the "copy" of the array variable was changed by func, and not the "real" variable.

There are two ways to get around this: either instantiate the array within your calling code, or change the fill() method to return a reference to the array is has created.

Below is the first approach:

public class Main {

    public static void main(String[] args) {
        List<String>[] array = (List<String>[])new List[10];
        fill(array);
        System.out.println("In main(): " + array[0].get(0));
    }

    public static void fill(List<String>[] array) {
        array[0] = new ArrayList<String>();
        array[0].add("test");
        System.out.println("In fill(): " + array[0].get(0));
    }

}

You may be wondering why this works, because you're still assigning ArrayList's to the elements of the array, however these objects are visible outside of the calling method. The reason for this is that although the fill method is getting a "copy" of the reference to the array, the reference itself is still referencing the same array object. This means that you can modify the internal state of the array object, and any changes you make will be seen by the caller because it referencing that same object.

Below is the second approach:

public class Main {

    public static void main(String[] args) {
        List<String>[] array = fill();
        System.out.println("In main(): " + array[0].get(0));
    }

    public static List<String>[] fill() {
        List<String>[] array = (List<String>[])new List[10];
        array[0] = new ArrayList<String>();
        array[0].add("test");
        System.out.println("In fill(): " + array[0].get(0));
        return array;
    }

}

(As an aside, you should generally try to avoid creating arrays of generic collections, a better idea would be to use a list to store the lists themselves. E.g:

List<List<String>> list = new ArrayList<List<String>>();
list.add(new ArrayList<String>());
list.get(0).add("test");
Jared Russell
If it were not for most of this being perfect I'd mark it down -1 for "parameters to methods are passed by value.". Java does not pass by value, which isn't quite what you are saying, but it is easily interpreted that way. Java passes the reference by value. If it passed by value your code examples would not work. Only primitives are pass by value.
TofuBeer
A: 

new ArrayList[10] give me incompatable type. However, new ArrayList[10] works for me.

Lily Wei