Here is a FlattenEverythingButTheKitchenSink class, a slightly modified version of a previous answer. It was tested with Java 7 and Java 8.
It works with Lists, Sets, Maps, Queues, and even Arrays of arbitrary depth.
It compiles and runs without warning, and I couldn't find any counterexample. Hence the class name :)
If you want a List of objects with possible duplicates, use flatten, if you want a Set, use uniqFlatten.
EDITED: Refactoring to avoid code repetition.
package stackOverflow;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
// Answer for
// https://stackoverflow.com/questions/20144826/how-to-flatten-all-items-from-a-nested-collection-into-a-single-list
public class FlattenEverythingButTheKitchenSink
{
public static void main(String[] args) {
int[][][] int3dArray = { { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } },
{ { 10, 11, 12 }, { 13, 14, 15 }, { 16, 17, 18 } },
{ { 19, 20, 21 }, { 22, 23, 24 }, { 25, 26, 27 }, { 28 }, { 29, 30 } } };
String[][] string2dArray = { { "He, llo" }, { "Wo", "rld" } };
String[] stringArray = { "Hello", "World" };
Set<Integer> integersSet = new HashSet<Integer>();
integersSet.add(1);
integersSet.add(2);
integersSet.add(3);
Map<String, String> stringMap = new HashMap<>();
stringMap.put("key1", "value1");
stringMap.put("key2", "value2");
stringMap.put("key3", "value3");
Queue<String> qe = new LinkedList<String>();
qe.add("x");
qe.add("y");
qe.add("z");
Object[] objectArray = { "Hell", 0, "W", 0, "orld", integersSet, stringMap, qe };
List<Object> mixList = new ArrayList<Object>();
mixList.add("String");
mixList.add(3);
mixList.add(string2dArray);
System.out.println(flatten(int3dArray));
System.out.println(flatten(flatten(int3dArray)));
System.out.println(flatten(3));
System.out.println(flatten(stringArray));
System.out.println(flatten(string2dArray));
System.out.println(flatten(objectArray));
System.out.println(flatten(mixList));
mixList.add(int3dArray);
System.out.println(uniqFlatten(mixList));
}
public static List<Object> flatten(Object object) {
return (List<Object>) recursiveFlatten(object, true);
}
public static Set<Object> uniqFlatten(Object object) {
return (Set<Object>) recursiveFlatten(object, false);
}
private static Collection<Object> recursiveFlatten(Object object, Boolean allowDuplicates) {
Collection<Object> setOrList;
if (allowDuplicates) {
setOrList = new ArrayList<Object>();
} else {
setOrList = new LinkedHashSet<Object>();
}
if (object.getClass().isArray()) {
for (int i = 0; i < Array.getLength(object); i++) {
setOrList.addAll(recursiveFlatten(Array.get(object, i), allowDuplicates));
}
} else if (object instanceof Map) {
for (Object element : ((Map<?, ?>) object).values()) {
setOrList.addAll(recursiveFlatten(element, allowDuplicates));
}
} else if (object instanceof Iterable) {
for (Object element : (Iterable<?>) object) {
setOrList.addAll(recursiveFlatten(element, allowDuplicates));
}
} else {
setOrList.add(object);
}
return setOrList;
}
}
It outputs :
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
[3]
[Hello, World]
[He, llo, Wo, rld]
[Hell, 0, W, 0, orld, 1, 2, 3, value1, value2, value3, x, y, z]
[String, 3, He, llo, Wo, rld]
[String, 3, He, llo, Wo, rld, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
and shouldn't have any problem with a
Set<List<Map<String, List<Object>>>> complexNestedCollection;
It would also work with a
Set<List<Map<String, List<int[][][][]>>>>
The initialisation code wouldn't be pretty though :D
list` which contains itself.
– Adrian Jałoszewski Nov 07 '16 at 16:34>` and in one of the lists you have a `List` too as object that you should get as a List once flatterned because you can know at runtime the generic type of your list due to type erasure so you cannot know that we don't necessary want to flattern a given list in a middle of objects
– Nicolas Filotto Nov 07 '16 at 17:33> list = Arrays.asList(Arrays.asList(1), Arrays.asList(Arrays.asList(1)))` here we would expect to get `1` and `Arrays.asList(1))` in our resulting list while with a generic method you will get `1` and `1` so if you remove duplicates only `1` which is totally wrong
– Nicolas Filotto Nov 07 '16 at 17:51