【问题标题】:Java: Interleave two integer based arraylists -> good approach?Java:交错两个基于整数的数组列表->好方法?
【发布时间】:2013-01-11 04:18:45
【问题描述】:

作业:寻找更好的策略或方法而不是完整的代码。

在两种情况下,我有两个整数数组列表:

  1. 第一个列表比第二个大
  2. 第二个列表比第一个大

我的目标是在两种情况下将 list2 的元素交错到 list1 中。我已经创建了一个方法来做到这一点,但我觉得我可以做得更好。

这是条件 1 的预期结果。注意,list2 的元素用完后,我们将 list1 的元素留在原处:

list1: [10, 20, 30, 40, 50, 60, 70]
list2: [4, 5, 6, 7]
Combined: [10, 4, 20, 5, 30, 6, 40, 7, 50, 60, 70]

这是条件 2 的预期结果。由于 list2 有更多元素,我们在 list1 用完后将这些元素附加到 list1:

list1: [10, 20, 30, 40]
list2: [4, 5, 6, 7, 8, 9, 10, 11]
Combined: [10, 4, 20, 5, 30, 6, 40, 7, 8, 9, 10, 11]

我的代码使用 if-else 语句来处理这两个条件。然后我使用迭代器遍历 list2 的元素并将它们插入到 list1 中。

public static void main(String[] Args)
{
    ArrayList<Integer> numbers = new ArrayList<Integer>();
    numbers.add(10);
    numbers.add(20);
    numbers.add(30);
    numbers.add(40);
    //numbers.add(50);
    //numbers.add(60);
    //numbers.add(70);

    ArrayList<Integer> numbers2 = new ArrayList<Integer>();

    numbers2.add(4);
    numbers2.add(5);
    numbers2.add(6);
    numbers2.add(7);
    numbers2.add(8);
    numbers2.add(9);
    numbers2.add(10);
    numbers2.add(11);

    System.out.println("list1: " + numbers);
    System.out.println("list2: " + numbers2);

    interleave(numbers, numbers2);

    System.out.println();
    System.out.println("Combined: " + numbers);
}

public static void interleave(ArrayList<Integer> list1, ArrayList<Integer> list2)
{
    //obtain an iterator for the collection
    Iterator<Integer> itr2 = list2.iterator();

    //loop counter
    int count = 1;

    //handle based on initial size of lists
    if(list1.size() >= list2.size())
    {
       //loop through the first array and add elements from list 2 after each element
       while(itr2.hasNext())
       {
           //insert elements from list2
           list1.add(count, itr2.next());

           //make sure elements are getting added at 1, 3, 5, 7, 9, etc
           count = count + 2;
       }
    }
    else if(list1.size() < list2.size())
    {
       //loop through the first array and add elements from list 2 after each element
       while(itr2.hasNext())
       {
           if(count <= list1.size())
           {
               //insert elements from list2
               list1.add(count, itr2.next());

               //make sure elements are getting added at 1, 3, 5, 7, 9, etc
               count = count + 2;
           }
           else
           {
               //fill in the remainder of the elements from list2 to list1
               list1.add(itr2.next());
           }
       }
    }
}

【问题讨论】:

    标签: java arraylist iterator


    【解决方案1】:

    这是我的方法。

    public static void interleave(ArrayList<Integer> a1, ArrayList<Integer> a2) {
        int start = 0;
        int i;
        int size = a1.size();
        for (i = 1; i < a1.size(); i += 2) {
            if (start < a2.size()) {
                a1.add(i, a2.get(start++));
            }
        }
        while (a1.size() < (size + a2.size())) {
            a1.add(i++, a2.get(start++));
        }
    
    }
    

    【讨论】:

      【解决方案2】:

      我提出以下代码:

      private static List<Integer> joinTwoLists(List<Integer> a, List<Integer> b) {
          final boolean aIsBigger = a.size() > b.size();
          final List<Integer> joined = new ArrayList<>(aIsBigger ? a : b);
          final AtomicInteger index = new AtomicInteger(1);
          for (Integer value : aIsBigger ? b : a) {
              joined.add(index.getAndAdd(2), value);
          }
          return joined;
      }
      

      【讨论】:

        【解决方案3】:

        你喜欢这个解决方案吗?

        public static void main(final String[] args) {
            ArrayList<Integer> numbers = new ArrayList<Integer>();
            numbers.add(10); numbers.add(20); numbers.add(30); numbers.add(40);
            //numbers.add(50); numbers.add(60); numbers.add(70);
        
            ArrayList<Integer> numbers2 = new ArrayList<Integer>();
            numbers2.add(4); numbers2.add(5); numbers2.add(6); numbers2.add(7);
            numbers2.add(8); numbers2.add(9); numbers2.add(10); numbers2.add(11);
        
            System.out.println("list1: " + numbers);
            System.out.println("list2: " + numbers2);
            List<Integer> interleaved = interleave(numbers, numbers2);
        
            System.out.println("\nCombined: " + interleaved);
        }
        
        public static List<Integer> interleave(
            final List<Integer> list1,
            final List<Integer> list2
        ) {
            List<Integer> result
                = new ArrayList<Integer>(list1.size() + list2.size());
        
            Iterator<Integer> it1 = list1.iterator();
            Iterator<Integer> it2 = list2.iterator();
            while (it1.hasNext() || it2.hasNext()) {
                if (it1.hasNext()) {
                    result.add(it1.next());
                }
                if (it2.hasNext()) {
                    result.add(it2.next());
                }
            }
            return result;
        }
        

        【讨论】:

        • @assylias 我理解为“代码示例可选(但必须可读)”而不是“代码示例严格禁止”。
        • 您将始终需要遍历这两个列表。没有办法避免这种情况。你只能尽量让代码尽可能容易理解。
        • 这是其中之一“我不敢相信它真的这么简单。”
        【解决方案4】:

        这是考虑这个问题的另一种方式,并且可以很容易地扩展到 3 个列表。

        首先,要认识到这里最难解决的部分是迭代,而不是创建新列表。从可迭代对象创建新列表很简单。

        所以想象我们有一个这样的方法:

        public <T> Iterable<T> interleave(Iterable<? extends T>... lists) {
            return new Iterable<T>() {
               @Override
               public Iterator<T> iterator() {
                   return new InterleavingIterator<T>(lists);
               }
            };
        }
        

        我们需要做的是一个Iterator&lt;T&gt;,它一次循环遍历每个迭代器。这是队列 (fifo) 的完美工作!您的迭代器可能如下所示:

        class InterleavingIterator<T> implements Iterator<T> {
        
            private final Queue<Iterator<? extends T>> iterators = new LinkedList<>();
        
            public InterleavingIterator(Iterable<? extends T>> iteratables) {
                for ( Iterable<T> iterable : iterables ) {
                   Iterator<T> iterator = iterable.iterator();
                   if ( iterator.hasNext() ) {
                      this.iterators.add(iterator);
                   }
                }
            }
        
            public boolean hasNext() {
                return !iterators.isEmpty();
            }
        
            public T next() {
                Iterator<T> nextIterator = iterators.poll();
                T result = nextIterator.next();
                if ( nextIterator.hasNext() ) {
                   iterators.add(nextIterator);
                }
                return result;
            }
        }
        

        简而言之,每次请求下一个元素时,都会弹出队列顶部的迭代器,返回next()的结果,如果迭代器还有元素(hasNext())则放入排在最后。

        这对于任意数量的列表都完全相同,并且不需要任何 icky 条件检查。

        要使用它创建一个新列表,您可以这样做:

        List<Integer> combined = new ArrayList<Integer>(interleave(list1, list2));
        

        【讨论】:

          【解决方案5】:

          几个cmets:

          • 我可能会返回一个新列表,而不是任意修改其中一个
          • 新算法更简单:
            • 创建一个新的空列表
            • 在计数器上循环从 0 到两个列表大小的最小值并交错
            • 完成后,检查其中一个列表是否有更多项目并添加它们(例如 addAllsubList 的组合)
            • 返回列表
          • 如果您不需要改变两个原始列表,您可以用更短的方式声明它们:List&lt;Integer&gt; numbers = Arrays.asList(10, 20, 30, 40); - 请注意,它会创建一个固定大小的列表,因此您无法添加或删除
          • 使用您当前的代码,您可以使用以下代码而不是重复内容:List listSmall, listBig; if (list1.size() &lt; list2.size()) { listSmall = list1; listBig = list2; } else { /* the opposite */} - 然后您知道listSmall 是小代码,您只需要一个循环。

          【讨论】:

          • 问题基本上说要修改原来的,但是是的,我会继续并返回一个列表。优秀的分数。
          猜你喜欢
          • 2020-03-22
          • 1970-01-01
          • 2020-07-09
          • 2013-04-16
          • 1970-01-01
          • 1970-01-01
          • 2012-10-14
          • 2021-06-11
          • 2016-03-01
          相关资源
          最近更新 更多