11

Как объединить два списка в Java?

11

Вопрос: Как объединить два списка в Java без изменения оригинальных списков?

Я пытаюсь объединить два списка listOne и listTwo в новый список newList. Я использую следующий код:

List<String> newList = new ArrayList<String>();
newList.addAll(listOne);
newList.addAll(listTwo);

Однако мне интересно, есть ли более простой способ выполнить эту задачу?

Условия:

  1. Оригинальные списки не должны изменяться.
  2. Могу использовать только JDK.
  3. Никакие внешние библиотеки недопустимы.

Дополнительные баллы за решение в виде однострочной записи или версию для JDK 1.3.

5 ответ(ов)

11

В Java 8 вы можете объединить два списка строк следующим образом:

List<String> newList = Stream.concat(listOne.stream(), listTwo.stream())
                             .collect(Collectors.toList());

В Java 16 и выше вы можете сделать то же самое более лаконично:

List<String> newList = Stream.concat(listOne.stream(), listTwo.stream()).toList();

Во втором примере метод toList() возвращает неизменяемый список, что может быть полезно, если вам не нужно изменять объединённый список после его создания.

6

Сразу на ум приходит способ сократить этот код до одной строки:

List<String> newList = new ArrayList<String>(listOne); newList.addAll(listTwo);

Тем не менее, вы можете сделать это ещё короче, используя конструктор с объёмом:

List<String> newList = new ArrayList<String>() {{
    addAll(listOne);
    addAll(listTwo);
}};

Это создаст новый список, в который сразу же добавятся все элементы из listOne и listTwo.

2

Вот ещё один однострочный пример кода на Java 8:

List<String> newList = Stream.of(listOne, listTwo)
                            .flatMap(Collection::stream)
                            .collect(Collectors.toList());

Этот код объединяет два списка listOne и listTwo в один новый список newList. Использование flatMap(Collection::stream) позволяет "развернуть" каждый из списков в поток элементов, а затем collect(Collectors.toList()) собирает все элементы в новый список.

В качестве бонуса, поскольку Stream.of() поддерживает переменное количество аргументов, вы можете объединять сколько угодно списков. Например:

List<String> newList = Stream.of(listOne, listTwo, listThree)
                            .flatMap(Collection::stream)
                            .collect(Collectors.toList());

В этом случае newList будет содержать элементы из listOne, listTwo и listThree.

1

Если одной из ваших задач является сохранение оригинальных списков, то создание нового списка с использованием метода addAll() фактически удваивает количество ссылок на объекты в ваших списках. Это может привести к проблемам с памятью, если ваши списки содержат большое количество элементов.

Если вам не нужно изменять конкатенированный результат, вы можете избежать этой проблемы, используя собственную реализацию списка. Хотя реализация класса занимает больше одной строки, его использование простое и удобное.

Вот пример класса CompositeUnmodifiableList.java:

public class CompositeUnmodifiableList<E> extends AbstractList<E> {

    private final List<? extends E> list1;
    private final List<? extends E> list2;

    public CompositeUnmodifiableList(List<? extends E> list1, List<? extends E> list2) {
        this.list1 = list1;
        this.list2 = list2;
    }
    
    @Override
    public E get(int index) {
        if (index < list1.size()) {
            return list1.get(index);
        }
        return list2.get(index - list1.size());
    }

    @Override
    public int size() {
        return list1.size() + list2.size();
    }
}

Пример использования:

List<String> newList = new CompositeUnmodifiableList<String>(listOne, listTwo);

Таким образом, вы сможете создать новый список, не дублируя ссылки на объекты и избегая потенциальных проблем с памятью.

0

Вероятно, это не проще, но весьма интересно и неэстетично:

List<String> newList = new ArrayList<String>() { { addAll(listOne); addAll(listTwo); } };

Не используйте это в рабочем коде... 😉

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь