Home > Net >  java.lang.ArrayStoreException when extending a list with new elements
java.lang.ArrayStoreException when extending a list with new elements

Time:04-29

I am calling methods with parameters like this: methodName(Object... param). These methods I can't change. To keep my code a bit more dynamic, I have lists which I pass to these methods. On its own, this is no issue, as I can call:

methodName(list.toArray(new Object[]{}));

and it works just fine. However, I sometimes need to pass additional elements to these methods, but can't add these items to the list. For this I tried the following:

methodName(Arrays.asList(list, oobj1, obj2).toArray(new Object[]{}));

which compiles just fine. But when calling this, it results in an java.lang.ArrayStoreException.

Below is an example code which replicates exactly this behavior. I have attempted multiple variations, but none work as expected.

List<Integer> nums = new ArrayList<>();
nums.add(1);
nums.add(2);
nums.add(3);

System.out.println(Arrays.asList(nums, 8, 9).toArray(new Integer[] {}));
System.out.println(Arrays.asList(nums.toArray(new Integer[] {}), 8, 9).toArray(new Integer[] {}));

Arrays.asList(nums, 8, 9) alone results in [[1, 2, 3], 8, 9] and Arrays.asList(nums.toArray(new Integer[] {}), 8, 9) results in [[Ljava.lang.Integer;@15db9742, 8, 9].

The desired output is [1, 2, 3, 8, 9] without too much more code. How can I achieve this while keeping the initial list intact?

CodePudding user response:

Three ways to do it (added correct flatMap approach by SgtOmer):

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Stream;
import java.util.stream.IntStream;

public class ArrayStore {
    public static void main(String[] args) {
        List<Integer> nums = new ArrayList<>();
        nums.add(1);
        nums.add(2);
        nums.add(3);

        System.out.println(Arrays.toString(
            Stream.concat(nums.stream(), IntStream.of(8, 9).boxed()).toArray(Integer[]::new)
        ));
        System.out.println(Arrays.toString(
            Stream.concat(nums.stream(), Stream.of(new Integer[]{8, 9})).toArray(Integer[]::new)
        ));
        System.out.println(Arrays.toString(
            Arrays.asList(nums, Arrays.asList(new Integer[]{8, 9})).stream().flatMap(Collection::stream).toArray(Integer[]::new)
        ));
    }
}

produces

$ java ArrayStore.java
[1, 2, 3, 8, 9]
[1, 2, 3, 8, 9]
[1, 2, 3, 8, 9]

CodePudding user response:

Hope this is what you were looking for:

Arrays.asList(mums, [8, 9]).stream()
    .flatMap(Collection::stream)
    .collect(<the collector you want>)

CodePudding user response:

As Sweeper suggested, I implemented an utility function.

The code is now

public static void main(String[] args) {
    List<Integer> nums = new ArrayList<>();
    nums.add(1);
    nums.add(2);
    nums.add(3);
    System.out.println(concat(7, nums, 4, 5, 6));
}
private <T> List<T> concat(T... nums) {
    List<T> result = new ArrayList<>();
    for (T t : nums) {
        if (t instanceof Collection)
            result.addAll((Collection<? extends T>) t);
        else
            result.add(t);
    }
    return result;
}

I am not going for recursion in my code base as I know that I won't be nesting another list within nums. However, recursion can be easily implemented as well, and the call wouldn't even change:

static <T> List<T> concat(T... nums) {
    return concat(new ArrayList<>(), nums);
}

static <T> List<T> concat(List<T> result, T... nums) {
    for (T t : nums) {
        if (t instanceof Collection)
            concat(result, ((Collection<T>) t).toArray((T[]) new Object[] {}));
        else
            result.add(t);
    }
    return result;
}

Overall the advantage of this concat method, is the fact that you can prepend and append at the same time. You can even concatenate multiple lists and also add elements between the lists.

  • Related