如何在Java中加入两个列表?


749

条件:请勿修改原始清单;仅JDK,无外部库。一线或JDK 1.3版本的加分点。

有没有比以下更简单的方法:

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

5
如果你这样做只是为了迭代的目的看另外一个问题-有谷歌番石榴和Java 8解决方案stackoverflow.com/questions/4896662/...
鲍里斯Treukhov

具有实用程序方法的Java 8解决方案:stackoverflow.com/a/37386846/1216775
akhil_mittal '17

阅读了一些答案后,很抱歉我问了。
Anthony Rutledge

Answers:


590

在Java 8中:

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

81
Gawd,这是Java 8中的事情吗?从技术上讲,我猜你赢了,但这真是太长了:-)
罗伯特·阿特金斯

4
对于不熟悉的读者,这是同时使用Java _ St​​reams的较短解决方案:stackoverflow.com/a/34090554/363573
Stephan

7
它很丑陋,但至少流利,可以在没有多行lambda的情况下使用。我真的希望有一个流畅的addAll可以返回简明清单。
Usman Ismail

6
我想值得一提的是,从中也可以很容易地得到一份明确的清单,例如:List<String> newList = Stream.concat(listOne.stream(), listTwo.stream()).distinct().collect(Collectors.toList());
罗杰(Roger)

1
替代concat的方法:溪流Stream.of(listOne, listTwo).flatMap(Collection::stream).collect(Collectors.toList())
Peter Walser

569

我可以把它缩短一排:

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

156
从技术上来说,您已将其缩短了一行,但这种不对称性困扰着我。我更愿意“花”多余的钱。
罗伯特·阿特金斯

13
这里没有问题,其中newList的内部数组将被初始化为listOne的大小,然后在添加listTwo中的所有项目时必须潜在地扩展吗?取得每个列表的大小并使用它来调整新数组的大小会更好吗?
埃里克

2
这是最适合我的解决方案。我比较了这次获奖的各种解决方案的性能,并创建了空白列表,然后比较addAll()了两者。我尝试了所有建议不要复制列表的方法,它们导致我们这次不需要很多开销。
manuelvigarcia

很好,但是您甚至可以缩短它:List list = new ArrayList <>(list1).addAll(list2);
速度

1
@velocity:不,那是行不通的。addAll(Collection)返回boolean
Stijn Van Bael

306

您可以使用Apache commons-collections库:

List<String> newList = ListUtils.union(list1, list2);

52
不错,但需要使用Apache Commons。他确实指定了“无外部库”
2011年

101
@ Quantum7,对其他人仍然有用;)另外,Apache Commons甚至是外部库吗?没有它,我什么也不会开始!
tster 2012年

28
@Platinum不,根据docs ListUtils.union所述,它完全等同于OP的代码。但是在列表上下文中使用SET操作(“联合”)可能会产生误导。我可以看到您如何期望这会删除重复项或某些内容,但是看来该方法无法做到这一点。
Quantum7年

24
避免使用Apache Commons Collections。它不是类型安全的,没有泛型。如果您使用Java 1.4,那就太好了,但是对于Java 5及更高版本,我更喜欢Google Guava。
Michael Piefel 2013年

11
@MichaelPiefel最新的Apache Commons Collections 4是类型安全的。使用Java 8方法参考,这种静态实用程序变得非常重要。
mingfai 2014年

93

您的要求之一就是保留原始列表。如果创建一个新列表并使用addAll(),那么实际上将使对列表中对象的引用数量增加一倍。如果您的列表很大,可能会导致内存问题。

如果不需要修改串联结果,则可以使用自定义列表实现来避免这种情况。定制实现类不只一行,显然……但是使用它又短又甜。

CompositeUnmodifiableList.java:

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

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

    public CompositeUnmodifiableList(List<E> list1, List<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);

15
这是对这个问题的真正答案。
Wouter Lievens

9
这是一个可行的解决方案,但请注意,如果基础列表对象发生更改(list1,list2),则此列表的内容也会更改。您可能无法修改CompositeUnmodifiableList 本身的实例,但是如果可以获取对原始列表的引用,则可以。对于那些陌生的对象:final修饰符仅影响对列表对象本身的引用,不能更改,但列表内容仍然有可能更改!
jwj

3
@jwj都很好,谢谢。类名可能值得解释。我认为此类与Collections.unmodifiableList()方法非常相似,该方法包装了一个列表使其无法修改。 CompositeUnmodifiableList除了包装两个列表并提供串联视图外,它的功能相同。您提出的所有观点也CompositeUnmodifiableList都是正确的Collections.unmodifiableList()
凯文K

2
建设者可以采取List<? extends E>
Patrick Parker

84

可能不简单,但有趣且丑陋:

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

不要在生产代码中使用它;;)


44
丑陋与邪恶,几乎就像使用双括号初始化一样。但是,它更短;)
乔恩(Jorn

4
@MarnixKlooster:Eclipse 知道您不应该使用它,并且使其使用起来不愉快;-)
Joachim Sauer

20
尽管实际上是一条直线,但我不认为这是“单线”。
splungebob 2012年

11
人们为什么讨厌匿名块初始化程序
NimChimpsky 2012年

18
@NimChimpsky我认为主要是因为它不仅是一个匿名块初始化程序,而且实际上是在创建ArrayList的匿名子类。话虽这么说,如果您相信这个Double Brace Initiisation问题的结果,那么似乎讨厌DBI基本上是风格和微优化的问题。据我所知,并没有重大的处罚。偷偷摸摸的缺点是,如果您试图比较它的类,因为它不会是ArrayList。
Patrick

75

另一个Java 8单行代码:

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

另外,由于Stream.of()可变参数,您可以连接任意数量的列表。

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

35
x -> x.stream()可以用代替Collection::stream
马丁

10
...甚至用List::stream
MC Emperor

73

不简单,但不调整开销:

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

55

发现了这个问题,希望连接任意数量的列表,而不在乎外部库。因此,也许会帮助其他人:

com.google.common.collect.Iterables#concat()

如果要在一个for()中将相同的逻辑应用于多个不同的集合,则很有用。


9
例如:Lists.newArrayList(Iterables.concat(list1,list2));
meilechh 2014年

你应该打电话com.google.common.collect.Iterators#concat(java.util.Iterator<? extends java.util.Iterator<? extends T>>)代替Iterables#concat(); 因为以后仍然将元素复制到临时链接中!
鲍勃

45

Java 8(Stream.ofStream.concat

提议的解决方案适用于三个列表,尽管它也可以应用于两个列表。在Java 8中,我们可以将Stream.ofStream.concat用作:

List<String> result1 = Stream.concat(Stream.concat(list1.stream(),list2.stream()),list3.stream()).collect(Collectors.toList());
List<String> result2 = Stream.of(list1,list2,list3).flatMap(Collection::stream).collect(Collectors.toList());

Stream.concat将两个流作为输入,并创建一个延迟串联的流,其元素是第一个流的所有元素,然后是第二个流的所有元素。由于我们有三个列表,因此我们Stream.concat两次使用了此方法()。

我们还可以编写一个实用程序类,该类具有一个接受任意数量列表的方法(使用varargs),并返回一个串联列表:

public static <T> List<T> concatenateLists(List<T>... collections) {
        return Arrays.stream(collections).flatMap(Collection::stream).collect(Collectors.toList()); 
}

然后,我们可以将这种方法用作:

List<String> result3 = Utils.concatenateLists(list1,list2,list3);

您可能会说List <String> result1 = Stream.concat(Stream.concat(list1.stream(),list2.stream()),list3.stream())。collect(Collectors.toList()); 在您的第一个运算符中。请修复它。
WebComer

44

这是使用两行的Java 8解决方案:

List<Object> newList = new ArrayList<>();
Stream.of(list1, list2).forEach(newList::addAll);

请注意,如果出现以下情况,则不应使用此方法

  • 的来源newList未知,可能已与其他线程共享
  • 修改的流newList是并行流,并且访问 newList不同步或线程安全

由于副作用的考虑。

上述两个条件不适用于将两个列表连接在一起的上述情况,因此这是安全的。

基于这个答案,另一个问题。


12
如果我没有记错,那么实际上不建议这样做-docs.oracle.com/javase/8/docs/api/java/util/stream / ...请参阅“副作用”部分。>通常,不鼓励在流参数的行为参数中产生副作用,因为它们经常会导致无意识地违反无状态要求以及其他线程安全性隐患。因此,在这种情况下,最好使用Collectors.toList()
Anton Balaniuc

@AntonBalaniuc问题是这是否真的有副作用。在这一点newList上,任何其他线程都无法观察到。但是您是正确的,如果不知道值的newList来源(例如,如果newList作为参数传递),则可能不应该这样做(例如,作为参数传递。)
SpaceTrucker

2
我很好奇; 为什么要.forEach(newList::addAll);代替.collect(Collectors.toList());
7684年

4
@ 11684,因为收集器将收集一个List<List<Object>>。您可能想到的是这样的:stackoverflow.com/questions/189559/…–
SpaceTrucker

@SpaceTrucker糟糕,我忽略了这一点。感谢您清理我的困惑。是的,我应该一直在想flatMap
7684年

34

这很简单,只有一行,但是会将listTwo的内容添加到listOne。您是否真的需要将内容放在第三个列表中?

Collections.addAll(listOne, listTwo.toArray());

11
不修改原始列表是标准之一,但这对于此处不受限制的情况作为示例很有用。
罗伯特·阿特金斯

1
谢谢,或更简单的listOne.addAll(listTwo)
Jay

27

稍微简单一点:

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

这会导致字符串重复吗?这意味着两个列表中都存在一个字符串会在结果列表中存在两次吗?
AgentKnopf 2012年

4
@Zainodis是的,可能有重复。该List结构没有任何唯一性约束。您可以通过对集合执行相同的操作来删除重复项。Set<String> newSet = new HashSet<>(setOne); newSet.addAll(setTwo);
Patrick

20

简短一点是:

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

17

您可以创建通用的Java 8实用程序方法来连接任意数量的列表

@SafeVarargs
public static <T> List<T> concat(List<T>... lists) {
    return Stream.of(lists).flatMap(List::stream).collect(Collectors.toList());
}

13

如果预先声明了目标列表,则可以执行oneliner。

(newList = new ArrayList<String>(list1)).addAll(list2);


9

另一个使用Java8流的班轮解决方案,因为flatMap解决方案已经过帐,所以这里的解决方案没有flatMap

List<E> li = lol.stream().collect(ArrayList::new, List::addAll, List::addAll);

要么

List<E> ints = Stream.of(list1, list2).collect(ArrayList::new, List::addAll, List::addAll);

    List<List<Integer>> lol = Arrays.asList(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6));
    List<Integer> li = lol.stream().collect(ArrayList::new, List::addAll, List::addAll);
    System.out.println(lol);
    System.out.println(li);

输出

[[1, 2, 3], [4, 5, 6]]
[1, 2, 3, 4, 5, 6]

1
我要补充一点,此解决方案可能比使用的解决方案性能更高flatMap,因为列表在收集时仅被迭代一次
Stefan Haberl

7

我认为最聪明的是:

/**
 * @param smallLists
 * @return one big list containing all elements of the small ones, in the same order.
 */
public static <E> List<E> concatenate (final List<E> ... smallLists)
{
    final ArrayList<E> bigList = new ArrayList<E>();
    for (final List<E> list: smallLists)
    {
        bigList.addAll(list);
    }
    return bigList;
}

3
别忘了@SafeVarargs
Radon Rosborough 2014年

6

您可以使用静态导入和帮助程序类来完成此操作

nb此类的通用化可能会得到改善

public class Lists {

   private Lists() { } // can't be instantiated

   public static List<T> join(List<T>... lists) {
      List<T> result = new ArrayList<T>();
      for(List<T> list : lists) {
         result.addAll(list);
      }
      return results;
   }

}

然后你可以做类似的事情

import static Lists.join;
List<T> result = join(list1, list2, list3, list4);

静态导入或助手类有什么关系?
shmosel

6

Java 8版本,支持按对象键联接:

public List<SomeClass> mergeLists(final List<SomeClass> left, final List<SomeClass> right, String primaryKey) {
    final Map<Object, SomeClass> mergedList = new LinkedHashMap<>();

    Stream.concat(left.stream(), right.stream())
        .map(someObject -> new Pair<Object, SomeClass>(someObject.getSomeKey(), someObject))
        .forEach(pair-> mergedList.put(pair.getKey(), pair.getValue()));

    return new ArrayList<>(mergedList.values());
}

4
public static <T> List<T> merge(List<T>... args) {
    final List<T> result = new ArrayList<>();

    for (List<T> list : args) {
        result.addAll(list);
    }

    return result;
}

4

使用助手类。

我建议:

public static <E> Collection<E> addAll(Collection<E> dest, Collection<? extends E>... src) {
    for(Collection<? extends E> c : src) {
        dest.addAll(c);
    }

    return dest;
}

public static void main(String[] args) {
    System.out.println(addAll(new ArrayList<Object>(), Arrays.asList(1,2,3), Arrays.asList("a", "b", "c")));

    // does not compile
    // System.out.println(addAll(new ArrayList<Integer>(), Arrays.asList(1,2,3), Arrays.asList("a", "b", "c")));

    System.out.println(addAll(new ArrayList<Integer>(), Arrays.asList(1,2,3), Arrays.asList(4, 5, 6)));
}

3
public static <T> List<T> merge(@Nonnull final List<T>... list) {
    // calculate length first
    int mergedLength = 0;
    for (List<T> ts : list) {
      mergedLength += ts.size();
    }

    final List<T> mergedList = new ArrayList<>(mergedLength);

    for (List<T> ts : list) {
      mergedList.addAll(ts);
    }

    return mergedList;
  }

2

我们可以使用java8和2种方法加入2个列表。

    List<String> list1 = Arrays.asList("S", "T");
    List<String> list2 = Arrays.asList("U", "V");

1)使用concat:

    List<String> collect2 = Stream.concat(list1.stream(), list2.stream()).collect(toList());
    System.out.println("collect2 = " + collect2); // collect2 = [S, T, U, V]

2)使用flatMap:

    List<String> collect3 = Stream.of(list1, list2).flatMap(Collection::stream).collect(toList());
    System.out.println("collect3 = " + collect3); // collect3 = [S, T, U, V]

1
在回答一个已有11年历史的问题以及其他30个答案时,请务必指出您的答案所解决的问题有哪些新方面,并指出在提出问题时这些技术是否会起作用,或者它们是否取决于所使用的功能。多年来介绍。
Jason Aller

2

几乎所有的答案都建议使用ArrayList。

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

最好使用LinkedList进行有效的添加操作。

ArrayList add被O(1)摊销,但是O(n)最坏的情况是因为必须调整数组大小并复制它。而LinkedList add始终为常数O(1)。

更多信息https://stackoverflow.com/a/322742/311420


0

我并不是说这很简单,但是您提到了单线奖励;-)

Collection mergedList = Collections.list(new sun.misc.CompoundEnumeration(new Enumeration[] {
    new Vector(list1).elements(),
    new Vector(list2).elements(),
    ...
}))

为什么不应该使用那些?
戴维(David

5
@David,因为它旨在在JDK内部使用。如果在代码中使用了该代码,则代码很可能不会在非Sun(或现在非Oracle)的JDK / JRE上运行。
Adrian Shum

@AdrianShum除了Oracle还有其他JDK / JRE吗?那会让我感到惊讶。即使仅限于最常见的API功能,重建整个内容也可能需要很
长时间

1
JVM有很多。最常见的一个在企业世界应该是IBM一个是,IIRC,与WebSphere捆绑
阿德里安岑

0

不可能接近单线,但是我认为这是最简单的:

List<String> newList = new ArrayList<String>(l1);
newList.addAll(l2);

for(String w:newList)
        System.out.printf("%s ", w);

0

如果您的列表具有不同的类型,并且您想将它们组合到另一种类型的列表中,那么这是一种使用流和Java 8的方法。

public static void main(String[] args) {
    List<String> list2 = new ArrayList<>();
    List<Pair<Integer, String>> list1 = new ArrayList<>();

    list2.add("asd");
    list2.add("asdaf");
    list1.add(new Pair<>(1, "werwe"));
    list1.add(new Pair<>(2, "tyutyu"));

    Stream stream = Stream.concat(list1.stream(), list2.stream());

    List<Pair<Integer, String>> res = (List<Pair<Integer, String>>) stream
            .map(item -> {
                if (item instanceof String) {
                    return new Pair<>(0, item);
                }
                else {
                    return new Pair<>(((Pair<Integer, String>)item).getKey(), ((Pair<Integer, String>)item).getValue());
                }
            })
            .collect(Collectors.toList());
}

0

如果要静态执行此操作,则可以执行以下操作。

这些示例以自然顺序(== Enum-order)使用2个EnumSet,A, B然后在ALL列表中联接。

public static final EnumSet<MyType> CATEGORY_A = EnumSet.of(A_1, A_2);
public static final EnumSet<MyType> CATEGORY_B = EnumSet.of(B_1, B_2, B_3);

public static final List<MyType> ALL = 
              Collections.unmodifiableList(
                  new ArrayList<MyType>(CATEGORY_A.size() + CATEGORY_B.size())
                  {{
                      addAll(CATEGORY_A);
                      addAll(CATEGORY_B);
                  }}
              );

这将创建一个新的匿名类。不推荐的方法!
克拉韦米尔

-3
import java.util.AbstractList;
import java.util.List;


/**
 * The {@code ConcatList} is a lightweight view of two {@code List}s.
 * <p>
 * This implementation is <em>not</em> thread-safe even though the underlying lists can be.
 * 
 * @param <E>
 *            the type of elements in this list
 */
public class ConcatList<E> extends AbstractList<E> {

    /** The first underlying list. */
    private final List<E> list1;
    /** The second underlying list. */
    private final List<E> list2;

    /**
     * Constructs a new {@code ConcatList} from the given two lists.
     * 
     * @param list1
     *            the first list
     * @param list2
     *            the second list
     */
    public ConcatList(final List<E> list1, final List<E> list2) {
        this.list1 = list1;
        this.list2 = list2;
    }

    @Override
    public E get(final int index) {
        return getList(index).get(getListIndex(index));
    }

    @Override
    public E set(final int index, final E element) {
        return getList(index).set(getListIndex(index), element);
    }

    @Override
    public void add(final int index, final E element) {
        getList(index).add(getListIndex(index), element);
    }

    @Override
    public E remove(final int index) {
        return getList(index).remove(getListIndex(index));
    }

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

    @Override
    public boolean contains(final Object o) {
        return list1.contains(o) || list2.contains(o);
    }

    @Override
    public void clear() {
        list1.clear();
        list2.clear();
    }

    /**
     * Returns the index within the corresponding list related to the given index.
     * 
     * @param index
     *            the index in this list
     * 
     * @return the index of the underlying list
     */
    private int getListIndex(final int index) {
        final int size1 = list1.size();
        return index >= size1 ? index - size1 : index;
    }

    /**
     * Returns the list that corresponds to the given index.
     * 
     * @param index
     *            the index in this list
     * 
     * @return the underlying list that corresponds to that index
     */
    private List<E> getList(final int index) {
        return index >= list1.size() ? list2 : list1;
    }

}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.