Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 854
Stefan Dunn Avatar asked Apr 06 '12 12:04

Stefan Dunn


People also ask

Is it possible to declare a 2D array with different lengths?

You can't specify different sizes in the declaration.

Can you change the length of an ArrayList?

The size of an ArrayList cannot be changed after the ArrayList is initialized. Immediately looking at the question, I would think the answer would be false. If you initialize an ArrayList, you can continue adding unlimited elements to it and the ArrayList will automatically resize.

Can we create 2D array using ArrayList in Java?

The following article provides an outline for 2D ArrayList in Java. In java array list can be two dimensional, three dimensional etc. The basic format of the array list is being one dimensional. Apart from one dimensional all other formats are considered to be the multi-dimensional ways of declaring arrays in java.

Can you change the size of a 2D array Java?

Similar to the one-dimensional array, the length of the two-dimensional array is also fixed. You can not change the length of an array, I mean, the number of rows and columns will remain fixed.


2 Answers

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:

  1. 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 of ArrayList<String> elements. Or in even geekier speak: mainList.stream() returns a Stream<ArrayList<String>>, sorta.

  2. 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 into map. This map function will covert each element in our stream for us. It has a built in foreach statement. In order to accomplish this; the map 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]). Here u is an identifier you choose just like when using a foreach statement. The first half declares this like so: u ->. And like a foreach, the variable u 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 a String[].. because after all, this is a 2D array of String.. or rather from this point in the code, a 1D array of String[] (string arrays). Keep in mind that u is ultimately an ArrayList. Note, calling toArray from an ArrayList object will create a new array of the type passed into it. Here we pass in new String[0]. Therefore it creates a new array of type String[] and with length equal to the length of the ArrayList u. It then fills this new array of strings with the contents of the ArrayList and returns it. Which leaves the Lambda expression and back into map. Then, map collects these string arrays and creates a new stream with them, it has the associated type String[] and then returns it. Therefore, map returns a Stream<String[]>, in this case. (Well, actually it returns a Stream<Object[]>, which is confusing and needs conversion, see below)

  3. Therefore we just need to call toArray on that new stream of arrays of strings. But calling toArray on a Stream<Object[]> is a bit different than calling it on an ArrayList<String>, as we did before. Here, we have to use a function reference confusing thing. It grabs the type from this: String[][]::new. That new function has type String[][]. 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 to toArray() would be enough, seeing that its a stream and all. A stream thats specifically Stream<String[]>. Anyone know why map actually returns a Stream<Object[]> and not a stream of the type returned by the Lambda expression inside?

  4. Now that we got the toArray from our mainList 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

like image 113
Pimp Trizkit Avatar answered Oct 08 '22 23:10

Pimp Trizkit


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)

like image 33
michele b Avatar answered Oct 09 '22 00:10

michele b