如何将嵌套Java集合中的所有项目展平为单个列表?

Ste*_*ers 14 java collections recursion loops list

给定一个复杂的嵌套对象集合,例如:

Set<List<Map<String, List<Object>>>> complexNestedCollection;
Run Code Online (Sandbox Code Playgroud)

是否存在一种通用方法来将其展平并获得包含在其中List的所有Object内容?

一些细节:

  1. 该列表不应包含集合对象本身或映射键 - 仅包含最低级别的值.
  2. 它应尽可能遵循相同的顺序 - 因此在示例中,列表中的项目将按顺序排列,而映射/集合的排序将取决于实现.
  3. 它可以选择性地排除重复
  4. 更新:理想情况下,它应该检测/处理任何级别的循环引用,例如List<List<Object>>外部List将自身包含为成员的位置.(感谢AdrianJałoszewski在下面的评论中提到这一点).

注意:实际的用例是从a获取所有字符串List<List<String>>,这可以通过两个循环轻松完成,但它让我对一般情况感到疑惑.

Mig*_*ork 11

我不确定这个确切的实现是否会起作用,因为它充满了未经检查的警告和其他危险的东西,但你应该得到一般的想法.

public static Set<Object> recursiveExtract(Object stuff) {

    Set<Object> set = new HashSet<Object>();

    if(stuff instanceof Iterable) {
        for(Object o : (Iterable<?>)stuff) {
            set.addAll(recursiveExtract(o));
        }
    } else if(stuff instanceof Map) {
        for(Object o : ((Map<?, ? extends Object>) stuff).values()) {
            set.addAll(recursiveExtract(o));
        }
    } else {
        set.add(stuff);
    }

    return set;
}
Run Code Online (Sandbox Code Playgroud)

List<Object>如果您坚持使用List,也可以使用,但是您可以获得重复的结果,或者LinkedHashSet<Object>如果您关心订单.


请给我改进建议,而不是downvotes.它更好.


Nic*_*tto 10

假设你使用的是Java 8,你可以Stream API通过下面的方法来做到这一点flatMap(Function<? super T,? extends Stream<? extends R>> mapper):

// 1. Convert the Set as a Stream of List<Map<String, List<Object>>>
// 2. Extract the elements of the lists to get a Stream of Map<String, List<Object>>
// 3. Extract values of the maps to get a Stream of List<Object>
// 4. Extract the elements of the lists to get a Stream of Object
// 5. Get rid of duplicates
// 6. Collect the result as a List of Object
List<Object> result = complexNestedCollection.stream()
    .flatMap(List::stream)
    .flatMap(m -> m.values().stream())
    .flatMap(List::stream)
    .distinct()
    .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)

返回一个流,该流包含将此流的每个元素替换为通过将提供的映射函数应用于每个元素而生成的映射流的内容的结果.每个映射的流在其内容放入此流后关闭.(如果映射的流为空,则使用空流,而不是.)


对于以前的版本Java,你仍然可以使用FluentIterable谷歌番石榴更换Stream和使用transformAndConcat(Function<? super E,? extends Iterable<? extends T>> function),而不是flatMap扁平化您的收藏.

之前的代码片段将被重写为下一个:

List<Object> result =
    new ArrayList<>(
        new LinkedHashSet<>(
            FluentIterable.from(complexNestedCollection)
                .transformAndConcat(
                    new Function<List<Map<String, List<Object>>>, Iterable<Map<String, List<Object>>>> () {
                        public Iterable<Map<String, List<Object>>> apply(final List<Map<String, List<Object>>> input) {
                            return input;
                        }
                    }
                ).transformAndConcat(
                    new Function<Map<String, List<Object>>, Iterable<List<Object>>> () {
                        public Iterable<List<Object>> apply(final Map<String, List<Object>> input) {
                            return input.values();
                        }
                    }
                ).transformAndConcat(
                    new Function<List<Object>, Iterable<Object>> () {
                        public Iterable<Object> apply(final List<Object> input) {
                            return input;
                        }
                    }
                ).toList()
        )
    );
Run Code Online (Sandbox Code Playgroud)


Eri*_*nil 9

这是FlattenEverythingButTheKitchenSink类,是之前答案的略微修改版本.它使用Java 7和Java 8进行了测试.

它适用于列表,集合,地图,队列,甚至任意深度的数组.它在没有警告的情况下编译和运行,我找不到任何反例.因此班级名称:)

如果您想要一个可能重复的对象列表,请使用flatten,如果您想要一个Set,请使用uniqFlatten.

编辑:重构以避免代码重复.

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

它输出:

[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]
Run Code Online (Sandbox Code Playgroud)

并且应该没有任何问题

Set<List<Map<String, List<Object>>>> complexNestedCollection;
Run Code Online (Sandbox Code Playgroud)

它也适用于

Set<List<Map<String, List<int[][][][]>>>>
Run Code Online (Sandbox Code Playgroud)

但初始化代码并不漂亮:D