You can iterate through the combinations one at a time, for example, by the hour, using an array to record the size of each internal array and an array of counters that keeps track of which member to use from each internal array. Something like this method:
/** * Produce a List<String> which contains every combination which can be * made by taking one String from each inner String array within the * provided two-dimensional String array. * @param twoDimStringArray a two-dimensional String array which contains * String arrays of variable length. * @return a List which contains every String which can be formed by taking * one String from each String array within the specified two-dimensional * array. */ public static List<String> combinations(String[][] twoDimStringArray) { // keep track of the size of each inner String array int sizeArray[] = new int[twoDimStringArray.length]; // keep track of the index of each inner String array which will be used // to make the next combination int counterArray[] = new int[twoDimStringArray.length]; // Discover the size of each inner array and populate sizeArray. // Also calculate the total number of combinations possible using the // inner String array sizes. int totalCombinationCount = 1; for(int i = 0; i < twoDimStringArray.length; ++i) { sizeArray[i] = twoDimStringArray[i].length; totalCombinationCount *= twoDimStringArray[i].length; } // Store the combinations in a List of String objects List<String> combinationList = new ArrayList<String>(totalCombinationCount); StringBuilder sb; // more efficient than String for concatenation for (int countdown = totalCombinationCount; countdown > 0; --countdown) { // Run through the inner arrays, grabbing the member from the index // specified by the counterArray for each inner array, and build a // combination string. sb = new StringBuilder(); for(int i = 0; i < twoDimStringArray.length; ++i) { sb.append(twoDimStringArray[i][counterArray[i]]); } combinationList.add(sb.toString()); // add new combination to list // Now we need to increment the counterArray so that the next // combination is taken on the next iteration of this loop. for(int incIndex = twoDimStringArray.length - 1; incIndex >= 0; --incIndex) { if(counterArray[incIndex] + 1 < sizeArray[incIndex]) { ++counterArray[incIndex]; // None of the indices of higher significance need to be // incremented, so jump out of this for loop at this point. break; } // The index at this position is at its max value, so zero it // and continue this loop to increment the index which is more // significant than this one. counterArray[incIndex] = 0; } } return combinationList; }
How the method works
If you imagine that an array of counters is like reading a digital clock, then the first combination of String sees an array of counters at all zeros, so the first line is created using the zero element (first element) of each internal array.
To get the next combination, the array of counters is incremented by one. Thus, the index of least significant counters increases by one. If this leads to the fact that its value becomes equal to the length of the internal array that it represents, then the index is zeroed, and the next index of greater significance increases. A separate dimensional array stores the length of each internal array, so the loop of the array counter knows when the index has reached its maximum.
For example, if an array of size:
[3][3][2][1]
and an array of counters:
[0][2][1][0]
then the increment will make the least significant (rightmost) index equal to 1, which is its maximum value. Thus, the index is zeroed, and the next index of greater significance (the second to the right of it) increases to 2. But this is also the maximum of this index, so it is zeroed and we move on to the next index of a larger value. This increases to three, which is its maximum value, so it is reset to zero and we move on to the most significant (left) index. This increases to 1, which is less than its maximum value, therefore the counter increases:
[1][0][0][0]
This means that the next String combination is performed using the second member of the first internal array and the first member of the next three internal arrays.
Warnings and style notes
I wrote it now in about forty minutes, and thatβs half one in the morning, which means that even if he seems to be doing exactly what is needed, there are very likely errors or bits of code that could be optimized. Therefore, make sure that the unit test is complete if its performance is critical.
Note that it returns a List array, not a String array, because I think Java collections are much preferable to using arrays in most cases. In addition, if you need a result set without duplicates, you can simply change the list to a set that automatically removes duplicates and leaves you with a unique set.
If you really need the result as a String array, don't forget that you can use the List<String>.toArray(String[]) method to simply convert the returned list to what you need.