Convert ArrayList into 2D array containing varying lengths of arrays
So I have:
ArrayList<ArrayList<String>>
Which contains an x number of ArrayLists which contain another y number of Strings.. To demonstrate:
Index 0:
String 1
String 2
String 3
Index 1:
String 4
Index 2:
Index 3:
String 5
String 6
Where index refers to the array index containing a string.
How can I transform this into a 2D array which looks like:
{{String1, String2, String3},{String4}, {}, {String5, String6}}
Thank you so much.
Solution 1:
Welcome to a world with Java 8!
It only took me all night with no sleep to learn what was needed to write this one freaking line of code. I'm sure it is already out there somewhere but I couldn't find it. So I'm sharing my hours and hours of research, enjoy. Woot!
Assuming:
ArrayList<ArrayList<String>> mainList = new ArrayList<ArrayList<String>>();
// populate this list here
(Or, rather, in Java 8:
ArrayList<ArrayList<String>> mainList = new ArrayList();
//Populate
)
Then all you need is:
String[][] stringArray = mainList.stream().map(u -> u.toArray(new String[0])).toArray(String[][]::new);
Bam! One line.
I'm not sure how fast it is compared to the other options. But this is how it works:
-
Take a stream of the
mainList
2D ArrayList. This stream is a bit like a Vector hooked up with a LinkedList and they had a kid. And that kid, later in life, dosed up on some NZT-48. I digress;mainList.stream()
is returning a stream ofArrayList<String>
elements. Or in even geekier speak:mainList.stream()
returns aStream<ArrayList<String>>
, sorta. -
Call the
.map
function on that stream which will return a new stream with contents that match a new type specified by the parameters passed intomap
. Thismap
function will covert each element in our stream for us. It has a built inforeach
statement. In order to accomplish this; themap
function takes a lambda expression as its parameter. A Lambda expression is like a simple inline one-line function. Which has two data types gettin' Jiggy wit it. First is the type of data in the stream upon which it was called (mainList.stream()
). The next type is the type of data it will map it out to, which is in the right half of the lambda expression:u -> u.toArray(new String[0])
. Hereu
is an identifier you choose just like when using aforeach
statement. The first half declares this like so:u ->
. And like aforeach
, the variableu
will now be each element in the stream as it iterates through the stream. Thus,u
is of the data type that the elements of the original stream are because it is them. The right half of the Lambda expression shows what to do with each element:u.toArray(new String[0])
. With the results being stored in their rightful place in a new stream. In this case we convert it to aString[]
.. because after all, this is a 2D array ofString
.. or rather from this point in the code, a 1D array ofString[]
(string arrays). Keep in mind thatu
is ultimately anArrayList
. Note, callingtoArray
from anArrayList
object will create a new array of the type passed into it. Here we pass innew String[0]
. Therefore it creates a new array of typeString[]
and with length equal to the length of the ArrayListu
. It then fills this new array of strings with the contents of theArrayList
and returns it. Which leaves the Lambda expression and back intomap
. Then,map
collects these string arrays and creates a new stream with them, it has the associated typeString[]
and then returns it. Therefore,map
returns aStream<String[]>
, in this case. (Well, actually it returns aStream<Object[]>
, which is confusing and needs conversion, see below) -
Therefore we just need to call
toArray
on that new stream of arrays of strings. But callingtoArray
on aStream<Object[]>
is a bit different than calling it on anArrayList<String>
, as we did before. Here, we have to use a function reference confusing thing. It grabs the type from this:String[][]::new
. Thatnew
function has typeString[][]
. Basically, since the function is called toArray it will always be an[]
of some sort. In our case since the data inside was yet another array we just add on another[]
. I'm not sure why the NZT-48 wasn't working on this one. I would have expected a default call totoArray()
would be enough, seeing that its a stream and all. A stream thats specificallyStream<String[]>
. Anyone know whymap
actually returns aStream<Object[]>
and not a stream of the type returned by the Lambda expression inside? -
Now that we got the
toArray
from ourmainList
stream acting properly. We can just dump it into a local variable easy enough:String[][] stringArray = mainList.stream...
Convert 2D ArrayList of Integers to 2D array of primitive ints
Now, I know some of you are out there going. "This doesn't work for ints!" As was my case. It does however work for "Ents", see above. But, if you want a 2D primitive int
array from a 2D ArrayList
of Integer
(ie. ArrayList<ArrayList<Integer>>
). You gotta change around that middle[earth] mapping. Keep in mind that ArrayLists can't have primitive types. Therefore you can't call toArray
on the ArrayList<Integer>
and expect to get a int[]
. You will need to map it out... again.
int[][] intArray = mainList.stream().map( u -> u.stream().mapToInt(i->i).toArray() ).toArray(int[][]::new);
I tried to space it out for readability. But you can see here that we have to go through the same whole mapping process again. This time we can't just simply call toArray
on the ArrayList u
; as with the above example. Here we are calling toArray
on a Stream
not an ArrayList
. So for some reason we don't have to pass it a "type", I think its taking brain steroids. Therefore, we can take the default option; where it takes a hit of that NZT-48 and figures out the obvious for us this [run]time. I'm not sure why it couldn't just do that on the above example. Oh, thats right.... ArrayLists don't take NZT-48 like Streams do. Wait... what am I even talking about here?
Annnyhoow, because streams are sooo smart. Like Sheldon, we need a whole new protocol to deal with them. Apparently advanced intelligence doesn't always mean easy to deal with. Therefore, this new mapToInt
is needed to make a new Stream
which we can use its smarter toArray
. And the i->i
Lambda expression in mapToInt
is a simple unboxing of the Integer
to int
, using the implicit auto-unboxing that allows int = Integer
. Which, now, seems like a dumb trivial thing to do, as if intelligence has its limits. During my adventure learning all this: I actually tried to use mapToInt(null)
because I expected a default behavior. Not an argument!! (cough Sheldon cough) Then I say in my best Husker valley girl accent, "Afterall, it is called mapToInt
, I would guess that, like, 84% of the time (42x2) it will be, like, passed i->i
by, like, everyone, so like, omgawd!" Needless to say, I feel a bit... like.. this guy. I don't know, why it doesn't work that way.
Well, I'm red-eye and half delirious and half asleep. I probably made some mistakes; please troll them out so I can fix them and let me know if there is an even better way!
PT
Solution 2:
String[][] array = new String[arrayList.size()][];
for (int i = 0; i < arrayList.size(); i++) {
ArrayList<String> row = arrayList.get(i);
array[i] = row.toArray(new String[row.size()]);
}
where arrayList
is your ArrayList<ArrayList<String>>
(or any List<List<String>>
, change the first line inside the for
loop accordingly)
Solution 3:
You can use toArray()
method to convert an ArrayList to an array. Since you have ArrayList within ArrayList, you will need to iterate over each element and apply this method.
Something like this:-
ArrayList<ArrayList<String>> mainList = new ArrayList<ArrayList<String>>();
// populate this list here
//more code
// convert to an array of ArrayLists
ArrayList<String[]> tempList = new ArrayList<String[]>();
for (ArrayList<String> stringList : mainList){
tempList.add((String[])stringList.toArray());
}
//Convert to array of arrays - 2D array
String [][]list = (String[][])tempList.toArray();
You can find more about toArray()
here.
Solution 4:
For example:
public String[][] toArray(List<List<String>> list) {
String[][] r = new String[list.size()][];
int i = 0;
for (List<String> next : list) {
r[i++] = next.toArray(new String[next.size()]);
}
return r;
}
Solution 5:
ArrayList<ArrayList<String>> list= new ArrayList<ArrayList<String>>();
ArrayList<String> a = new ArrayList<String>();
ArrayList<String> b = new ArrayList<String>();
a.add("Hello");
b.add("Hell");
list.add(a);
list.add(b);
Object[][] array = new Object[list.size()][];
Iterator<ArrayList<String>> iterator = list.iterator();
int i = 0;
while(iterator.hasNext()){
array[i] = iterator.next().toArray();
}
You can use the below snippet to convert Object[] to String[]
String[] stringArray = Arrays.copyOf(objectArray, objectArray.length, String[].class);