如何将列表连接成一个列表

2022-09-03 13:51:02

我有一个值列表,其中一些可能是列表/集合或单个值。在JavaScript表示法中,它可能看起来像这样:

const input = [1,2,[3,4], [5,6], 7];

我想得到:

const concatenated = [1,2,3,4,5,6,7];

所以我有这个Java代码:

      ArrayList<T> concatenated = new ArrayList<>();

      for (T v : input) {
        try{
          concatenated.addAll((Collection) v);
        }
        catch (Exception e1){
          try{
            concatenated.addAll((List) v);
          }
          catch (Exception e2){
            concatenated.add(v);
          }
        }

     }

但是这个代码对我来说似乎很糟糕。首先,我不知道尝试投射到列表或收藏是否足够 - 是否有其他类型我应该尝试投射到?是否有任何我不应该忽略的错误?

如何正确地做到这一点?


答案 1

除非列表中有值,否则不需要这样处理代码。不过,在你的情况下,仅仅将基础转换为:ExceptionnullinstanceOf

// Edit: Since the type of the input `Collection` is not bound strictly
List<Object> flatten(Collection<?> input) {
    List<Object> concatenated = new ArrayList<>();
    for (Object v : input) {
        if (v instanceof Collection) {
            concatenated.addAll(flatten((Collection<?>) v));
        } else {
            concatenated.add(v);
        }
    }
    return concatenated;
} 

在jshell上进一步使用它给了我这个输出:

jshell> List<Object> list = List.of(1,2,List.of(3,4),List.of(5,6),7) 
list ==> [1, 2, [3, 4], [5, 6], 7]

jshell> flatten(list)
$3 ==> [1, 2, 3, 4, 5, 6, 7]

:


答案 2

正如其他人所提到的,对控制流使用异常并不理想。您可以改用运算符来测试元素是否为 .nullpointer的答案显示了一个很好的例子。如果你想要一个更通用的选项,你也可以做这样的事情:instanceofCollection

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;

public static <E> List<E> deepFlatten(final Iterable<?> iterable, final Class<E> type) {
    if (type.isPrimitive() || type.isArray() || Iterable.class.isAssignableFrom(type)) {
        throw new IllegalArgumentException(
                "type must not denote a primitive, array, or java.lang.Iterable type: " + type);
    }
    final List<E> result = new ArrayList<>();
    for (final Object element : iterable) {

        if (element instanceof Iterable<?>) {
            result.addAll(deepFlatten((Iterable<?>) element, type)); // recursion

        } else if (element != null && element.getClass().isArray()) {

            if (element instanceof Object[]) {
                result.addAll(deepFlatten(Arrays.asList((Object[]) element), type)); // recursion
            } else { // primitive array
                final Iterable<?> itrArray = IntStream.range(0, Array.getLength(element))
                        .mapToObj(index -> Array.get(element, index))::iterator; // method reference
                result.addAll(deepFlatten(itrArray, type)); // recursion
            }

        } else {
            /*
             * Will throw ClassCastException if any element is not an instance
             * of "type". You could also throw a NullPointerException here if
             * you don't want to allow null elements.
             */
            result.add(type.cast(element));
        }

    }
    return result;
}

这还通过递归处理“嵌入式”数组以及 s。请注意,由于歧义,它不处理s;我们应该扁平化键或值,还是两者兼而有之?IterableMap

调用上述内容:

Iterable<?> iterable = List.of(
        "A", "B", "C", "D",
        List.of("E", "F", List.of("G", "H"), "I", "J"),
        "K",
        new String[]{"L", "M", "N", "O", "P"},
        new String[][]{{"Q", "R"}, {"S", "T"}, {"U"}, {"V"}},
        new Object[]{"W", "X"},
        "Y", "Z"
);
List<String> flattened = deepFlatten(iterable, String.class);
System.out.println(flattened);

给了我:

[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z]

请注意,字母是有序的,因为 s 和数组具有有保证的迭代顺序。如果你包含一个结果,则每次可能的顺序可能不一样。ListIterableSetdeepFlatten