Given a complex nested collection of objects such as:
Set<List<Map<String, List<Object>>>> complexNestedCollection;
Does a generic method exist to flatten this out and get a single List
of all Object
s contained within?
A few details:
- The list shouldn't include collection objects themselves or map keys - only the values at the lowest level.
- It should follow the same ordering where possible - so in the example, items in the list would be in order, whereas ordering of maps/sets would depend on the implementation.
- It could optionally exclude duplicates
- UPDATE: It should ideally detect/handle circular references at any level, e.g. a
List<List<Object>>
where the outer List contains itself as a member. (Credit to Adrian Jałoszewski for mentioning this in the comments below).
Note: The actual use case is to get all Strings from a List<List<String>>
, which can be done easily enough with two loops but it made me wonder about the general case.
I am wondering what the scenario could be and if it weren't better to define some specific data structure, such as a tree. But anyway:
I would avoid generics as java's typesystem is too simplistic to handle recursive types:
This is supported by two private functions that do the actual extraction filling up the provided
result
collection:My suggestion to solve the problem is to create a class which flattens the collections and maps recursively which memorizes the collections and maps already visited to handle circular dependencies. It's a straight forawart implementation of the DFS algorithm.
The
Visited
class has to offer a way to check whether the object we encountered is THE SAME (this is the reason I'm using the==
operator here instead ofequals
). This is the only way we can reduce circular dependencies without loosing information about collections which by coincidence contain the same elements.The only thing which is needed here is a
null
check, but it's not necessary to understand the logic behind this solution.Assuming that you use Java 8, you could do that with the
Stream API
thanks to the methodflatMap(Function<? super T,? extends Stream<? extends R>> mapper)
as next:For previous versions of
Java
, you can still useFluentIterable
from Google Guava to replace theStream
and usetransformAndConcat(Function<? super E,? extends Iterable<? extends T>> function)
instead offlatMap
to flatten your collection.The previous code snippet would then be rewritten as next:
I'm not sure if this exact implementation would work, as it's full of unchecked warnings and other dangerous stuff, but you should get the general idea.
You can also use
List<Object>
if you insist on List, but then you could get duplicate results, orLinkedHashSet<Object>
if you care about the order.Please instead of downvotes, give me suggestions for improvement. It's nicer.
You could use LambdaJ's flatten function.
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.
It outputs :
and shouldn't have any problem with a
It would also work with a
The initialisation code wouldn't be pretty though :D