【问题标题】:How do I join two lists in Java?如何在 Java 中加入两个列表?
【发布时间】:2023-03-10 19:35:01
【问题描述】:

条件:不修改原列表;只有 JDK,没有外部库。单行或 JDK 1.3 版本的奖励积分。

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

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

【问题讨论】:

标签: java list jdk1.5


【解决方案1】:

稍微简单一点:

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

【讨论】:

  • 这会导致重复的字符串吗?这意味着两个列表中都存在的字符串将在结果列表中存在两次?
  • @Zainodis 是的,可能有重复。 List 结构没有唯一性约束。您可以通过对集合执行相同的操作来删除受骗者。 Set&lt;String&gt; newSet = new HashSet&lt;&gt;(setOne); newSet.addAll(setTwo);
【解决方案2】:

在我的头顶上,我可以把它缩短一行:

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

【讨论】:

  • 虽然您在技术上是正确的,但您已将其缩短了一行,这种不对称性让我感到困扰。足以让我更乐意“花费”额外的线路。
  • 这里没有问题,newList 的内部数组将被初始化为 listOne 的大小,然后在添加 listTwo 中的所有项目时必须潜在地扩展?获取每个列表的大小并使用它来调整新数组的大小会更好吗?
  • 这是最适合我的解决方案。我对不同解决方案的性能进行了比较,最终获胜,同时创建了空列表和addAll()。我尝试了所有建议不要复制列表的方法,结果导致我们这次不需要大量开销。
  • 在ArrayList 上使用LinkedList 以实现高效添加。 stackoverflow.com/a/322742/311420
【解决方案3】:

稍微短一点:

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

【讨论】:

    【解决方案4】:

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

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

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

    【讨论】:

    • 丑陋和邪恶,就像几乎所有使用双括号初始化一样。不过它更短;)
    • @MarnixKlooster: Eclipse 知道你不应该使用它并让它使用起来不愉快;-)
    • 虽然物理上是一行,但我不认为这是“单行”。
    • 为什么人们讨厌匿名块初始化器
    • @NimChimpsky 我认为这主要是因为它不仅仅是一个匿名块初始化程序,而是您实际上是在创建 ArrayList 的匿名子类。话虽这么说,如果你相信这个Double Brace Initilization question 的结果,看起来讨厌 DBI 主要是风格品味和微优化的问题。据我所知,这样做不会受到重大处罚。偷偷摸摸的缺点是如果你曾经尝试比较它的类,因为它不会是 ArrayList。
    【解决方案5】:

    如果目标列表是预先声明的,你可以做一个单行。

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

    【讨论】:

      【解决方案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);
      

      【讨论】:

      • 静态导入或辅助类有什么关系?
      【解决方案7】:

      如果不引入您自己的实用程序方法,我无法在一般情况下改进两行,但如果您确实有字符串列表并且您愿意假设这些字符串不包含逗号,您可以拉这条长单线:

      List<String> newList = new ArrayList<String>(Arrays.asList((listOne.toString().subString(1, listOne.length() - 1) + ", " + listTwo.toString().subString(1, listTwo.length() - 1)).split(", ")));
      

      如果你放弃泛型,这应该是 JDK 1.4 兼容的(虽然我没有测试过)。也不推荐用于生产代码;-)

      【讨论】:

      • 那是一行恶心的代码。可能比所有其他解决方案(组合)的性能密集度更高,做出大量您真的不应该做出的假设,而不是真正的单行(您可以将整个程序放在一行技术上)。
      • @luk2302 不包含分号(末尾除外),因此可以认为是单行。
      • 天哪,如果列表中的项目中有逗号怎么办?
      【解决方案8】:

      我并不是说这很简单,但你提到了单行的奖金;-)

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

      【讨论】:

      • 为什么永远不要使用这些?
      • @David 因为它旨在在 JDK 内部使用。如果您在代码中使用它,您的代码很可能不会在非 Sun(或现在非 Oracle)的 JDK/JRE 上运行。
      • @AdrianShum 除了 Oracle,还有其他 JDK/JRE 吗?那会让我感到惊讶。即使仅限于最常见的 API 功能,重建整个东西也可能需要很长时间......
      • JVM 相当多。企业界最常见的应该是 IBM 的,它是 iirc,与 websphere 捆绑在一起
      • 每当您看到Vector 的用法时,请休息5 分钟。
      【解决方案9】:

      使用 Helper 类。

      我建议:

      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)));
      }
      

      【讨论】:

        【解决方案10】:

        您可以使用Apache commons-collections 库:

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

        【讨论】:

        • 不错,但需要 apache commons。他确实指定了“没有外部库”
        • @Quantum7,对其他人仍然有用;)另外,apache commons 甚至是一个外部库吗?没有它我什么都不会开始!
        • @Platinum 不,根据文档 ListUtils.union 完全等同于 OP 的代码。但在列表上下文中使用 SET 操作(“Union”)可能会产生误导。我可以看到您可能希望这如何删除重复项或其他内容,但该方法似乎没有这样做。
        • 避免使用 Apache Commons 集合。它不是类型安全的,没有泛型。如果您使用 Java 1.4,那就太好了,但对于 Java 5 及更高版本,我更喜欢 Google Guava。
        • @MichaelPiefel 最新的 Apache Commons Collections 4 是类型安全的。使用 Java 8 方法引用,这种静态实用程序变得非常重要。
        【解决方案11】:

        发现这个问题希望连接任意数量的列表,而不是在意外部库。所以,也许它会帮助别人:

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

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

        【讨论】:

        • 例如:Lists.newArrayList(Iterables.concat(list1,list2));
        • 你应该打电话给com.google.common.collect.Iterators#concat(java.util.Iterator&lt;? extends java.util.Iterator&lt;? extends T&gt;&gt;)而不是Iterables#concat();因为后面仍然将元素复制到临时链接中!
        【解决方案12】:

        没有办法接近单线,但我认为这是最简单的:

        List<String> newList = new ArrayList<String>(l1);
        newList.addAll(l2);
        
        for(String w:newList)
                System.out.printf("%s ", w);
        

        【讨论】:

          【解决方案13】:

          我认为最聪明的人:

          /**
           * @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;
          }
          

          【讨论】:

          • 别忘了@SafeVarargs!
          【解决方案14】:

          并不简单,但没有调整大小的开销:

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

          【讨论】:

            【解决方案15】:

            您的要求之一是保留原始列表。如果您创建一个新列表并使用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);
            

            【讨论】:

            • 这是一个可行的解决方案,但请注意,如果基础列表对象更改(list1,list2),此列表的内容也会更改。您可能无法修改 CompositeUnmodifiableList itself 的实例,但如果您可以获得对原始列表的引用,那么您可以。同样对于那些不熟悉的人:final修饰符只会影响对列表对象的引用,它本身不能改变,但列表的内容仍然可以改变!
            • @jwj 都很好,谢谢。类名可能值得一些解释。我认为这个类所做的事情与Collections.unmodifiableList() 方法非常相似,它包装了一个列表以使其不可修改。 CompositeUnmodifiableList 做同样的事情,只是它包装了两个列表并提供了一个连接视图。您对CompositeUnmodifiableList 的所有观点也适用于Collections.unmodifiableList()。
            • 构造函数可以带List&lt;? extends E&gt;
            【解决方案16】:
            public class TestApp {
            
            /**
             * @param args
             */
            public static void main(String[] args) {
                System.out.println("Hi");
                Set<List<String>> bcOwnersList = new HashSet<List<String>>();
                List<String> bclist = new ArrayList<String>();
                List<String> bclist1 = new ArrayList<String>();
                List<String> object = new ArrayList<String>();
                object.add("BC11");
                object.add("C2");
                bclist.add("BC1");
                bclist.add("BC2");
                bclist.add("BC3");
                bclist.add("BC4");
                bclist.add("BC5");
                bcOwnersList.add(bclist);
                bcOwnersList.add(object);
            
                bclist1.add("BC11");
                bclist1.add("BC21");
                bclist1.add("BC31");
                bclist1.add("BC4");
                bclist1.add("BC5");
            
                List<String> listList= new ArrayList<String>();
                for(List<String> ll : bcOwnersList){
                    listList = (List<String>) CollectionUtils.union(listList,CollectionUtils.intersection(ll, bclist1));
                }
                /*for(List<String> lists : listList){
                    test = (List<String>) CollectionUtils.union(test, listList);
                }*/
                for(Object l : listList){
                    System.out.println(l.toString());
                }
                System.out.println(bclist.contains("BC"));
            
            }
            
            }
            

            【讨论】:

            • 我什至不明白什么部分实际上是答案......
            • 几句话的解释会有很大帮助。请记住这一点,以备将来的 cmets 使用
            【解决方案17】:

            在 Java 8 中:

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

            【讨论】:

            • 天哪,这是 Java 8 中的东西?从技术上讲,我猜你赢了,但那是一条长线:-)
            • 对于普通读者,这里是一个使用 Java _ St​​reams 的更短的解决方案:stackoverflow.com/a/34090554/363573
            • 它很丑,但至少它很流畅,并且可以在没有多行 lambda 的情况下使用。我真的希望有一个流利的 addAll 返回连接列表。
            • 我想值得注意的是,从中得到一个不同的列表也很容易,就像这样:List&lt;String&gt; newList = Stream.concat(listOne.stream(), listTwo.stream()).distinct().collect(Collectors.toList());
            • concat 的替代方案:流Stream.of(listOne, listTwo).flatMap(Collection::stream).collect(Collectors.toList())
            【解决方案18】:

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

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

            【讨论】:

            • 不修改原始列表是标准之一,但在这不是限制的情况下,此处作为示例很有用。
            • 谢谢,或者更简单的 listOne.addAll(listTwo)
            【解决方案19】:

            另一个 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());
            

            【讨论】:

            • x -&gt; x.stream() 可以替换为 Collection::stream。
            • ...甚至List::stream。
            • 不错。我喜欢这种方法。
            【解决方案20】:
            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;
            }
            

            【讨论】:

              【解决方案21】:

              这是一个使用两行代码的 java 8 解决方案:

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

              请注意,如果

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

              出于副作用考虑。

              上述两种情况都不适用于上述加入两个列表的情况,所以这样是安全的。

              基于this answer另一个问题。

              【讨论】:

              • 如果我没记错的话,实际上不建议这样做 - docs.oracle.com/javase/8/docs/api/java/util/stream/... 请参阅副作用部分。 > 通常不鼓励对流操作的行为参数产生副作用,因为它们通常会导致无意中违反无状态要求以及其他线程安全隐患。所以在这种情况下最好使用 Collectors.toList()
              • @AntonBalaniuc 问题是这是否真的是副作用。此时newList 不能被任何其他线程观察到。但是你是对的,如果不知道 newList 的值来自哪里(例如,如果 newList 作为参数传递,则可能不应该这样做。
              • 我很好奇;为什么是.forEach(newList::addAll); 而不是.collect(Collectors.toList());?
              • @11684 因为收集器会收集到List&lt;List&lt;Object&gt;&gt;。你可能想到的是这样的:stackoverflow.com/questions/189559/…
              • @SpaceTrucker 哎呀,我忽略了这一点。感谢您消除我的困惑。是的,我应该想到flatMap。
              【解决方案22】:

              Java 8(Stream.of 和 Stream.concat)

              建议的解决方案适用于三个列表,尽管它也可以应用于两个列表。在 Java 8 中,我们可以使用 Stream.of 或 Stream.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);
              

              【讨论】:

                【解决方案23】:

                如果您的列表具有不同的类型,并且您希望将它们组合成另一种类型的列表,那么这是一种使用流和 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());
                }
                

                【讨论】:

                  【解决方案24】:

                  另一个使用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]
                  

                  【讨论】:

                  • 我要补充一点,这个解决方案可能比使用 flatMap 的解决方案性能更高,因为列表在收集时只迭代一次
                  • 我不认为flatMap 会导致额外的迭代,你能澄清一下吗?
                  【解决方案25】:

                  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());
                  }
                  

                  【讨论】:

                    【解决方案26】:

                    在 Java 8 中(另一种方式):

                    List<?> newList = 
                    Stream.of(list1, list2).flatMap(List::stream).collect(Collectors.toList());
                    

                    【讨论】:

                    【解决方案27】:

                    如果您想静态地执行此操作,您可以执行以下操作。

                    示例使用 2 个自然顺序 (==Enum-order) A, B 的 EnumSet,然后加入 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);
                                      }}
                                  );
                    

                    【讨论】:

                    • 这将创建一个新的匿名类。不推荐的方法!
                    【解决方案28】:
                    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;
                      }
                    

                    【讨论】:

                      【解决方案29】:

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

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

                      【讨论】:

                        【解决方案30】:
                        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;
                            }
                        
                        }
                        

                        【讨论】:

                          猜你喜欢
                          • 2012-03-12
                          • 2016-11-25
                          • 1970-01-01
                          • 2020-03-05
                          • 2016-02-25
                          • 1970-01-01
                          • 1970-01-01
                          • 2014-11-22
                          • 2017-04-21
                          相关资源
                          最近更新 更多