Как объединить списки в один список
У меня есть список значений, некоторые из которых могут быть списками/коллекциями или отдельными значениями. В нотации 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
Код не нуждается в обработке Exception
как таковой, если в списках нет null
значений. Впрочем, в вашем случае достаточно просто привести основание instanceOf
как:
// 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
Как уже упоминалось, использование исключений для потока управления не является идеальным. Вместо этого вы можете использовать оператор instanceof
чтобы проверить, является ли элемент Collection
. Ответ от nullpointer показывает хороший пример этого. Если вы хотите более общий вариант, вы можете сделать что-то вроде:
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;
}
Это также обрабатывает "встроенные" массивы, как и Iterable
s, посредством рекурсии. Обратите внимание, что он не обрабатывает Map
из-за неоднозначности; мы должны сгладить ключи или значения - или оба?
Вызов выше с:
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]
Обратите внимание, что буквы расположены по порядку, потому что List
и массивы имеют гарантированные порядки итераций. Если ваш Iterable
содержал Set
результат deepFlatten
может не каждый раз находиться в одном и том же порядке.
Ответ 3
Использование Exceptions
для управления потоком приложений/бизнес-логикой является анти-паттерном. Вы можете прочитать больше об этом здесь, здесь и здесь.
Относительно хранения различных типов элементов в коллекциях может быть сложно отлаживать и поддерживать. Вы можете написать свою собственную обертку и инкапсулировать обработку из использования. Вы можете сослаться на это для вдохновения.