简体   繁体   English

使用迭代器合并列表

[英]Merging Lists using iterators

I need to merge two lists of strings in java and I'm not too sure on the best way to do it. 我需要在java中合并两个字符串列表,我不太确定最好的方法。 I have to use iterators and the compareTo() method. 我必须使用迭代器和compareTo()方法。 For example... 例如...

Example: L1: A,B,C,D L2: B,D,F,G result: A,B,B,C,D,D,F,G 示例:L1:A,B,C,D L2:B,D,F,G结果:A,B,B,C,D,D,F,G

I can assume the input lists are already sorted and i cant use the contains() method. 我可以假设输入列表已经排序,我不能使用contains()方法。 I have some initial checks but the while loop is what im stuck on. 我有一些初步检查但是while循环是我坚持的。

public static ListADT<String> merge(ListADT<String> L1,ListADT<String> L2) throws BadListException {
ListADT<String> L3 = new ArrayList<String>;
if(L1 == null || L2 == null) {
    throw new BadListException();
}
Iterator<String> itr1 = new L1.iterator();
Iterator<String> itr2 = new L2.iterator();  
if(L1.size() == 0 && L2.size() == 0) {
    return L3;
}
if(L1.size() == 0 && L2.size() != 0) {
    for(int i = 0; i < L2.size(); i++) {
        return L3.add(L2.get(i));
    }
}
if(L2.size() == 0 && L1.size() != 0) {
    for(int i = 0; i < L1.size(); i++) {
        return L3.add(L1.get(i));
    }
}
while(itr1.hasNext() || irt2.hasNext()) {
    //merge the lists here?
}

} }

Any help would be appreciated. 任何帮助,将不胜感激。

It's fairly straightforward if you just use variables to hold the current value from each iterator. 如果你只是使用变量来保存每个迭代器的当前值,这是相当简单的。 This solution assumes your lists do not contain null , but it would not be difficult to add null-handling since the lists are sorted. 此解决方案假定您的列表不包含null ,但由于列表已排序,因此添加空值处理并不困难。

package com.example;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class IteratorMerge {

    /**
     * @param args
     */
    public static void main(String[] args) {
        List<String> list1 = Arrays.asList(new String[]{"A", "B", "C", "D"});
        List<String> list2 = Arrays.asList(new String[]{"B", "D", "F", "G"});

        System.out.println(merge(list1, list2));
    }

    public static List<String> merge(List<String> L1,List<String> L2) {
        List<String> L3 = new ArrayList<String>();

        Iterator<String> it1 = L1.iterator();
        Iterator<String> it2 = L2.iterator();

        String s1 = it1.hasNext() ? it1.next() : null;
        String s2 = it2.hasNext() ? it2.next() : null;
        while (s1 != null && s2 != null) {
            if (s1.compareTo(s2) < 0) { // s1 comes before s2
                L3.add(s1);
                s1 = it1.hasNext() ? it1.next() : null;
            }
            else { // s1 and s2 are equal, or s2 comes before s1
                L3.add(s2);
                s2 = it2.hasNext() ? it2.next() : null;
            }
        }

        // There is still at least one element from one of the lists which has not been added
        if (s1 != null) {
            L3.add(s1);
            while (it1.hasNext()) {
                L3.add(it1.next());
            }
        }
        else if (s2 != null) {
            L3.add(s2);
            while (it2.hasNext()) {
                L3.add(it2.next());
            }
        }

        return L3;
    }
}

Here's some pseudocode for the basic algorithm: 这是基本算法的一些伪代码:

while(itr1 && itr2)
{
     if(itr1 value < it2 value)
         add itr1 to list
         increment itr1

     else
         add itr2 to list
         increment itr2
}

check if itr1 or itr2 still have more elements
while itr1 or itr2 has more elements, add those elements to the list

We know that the lists are sorted, so at each stage, we simply grab the smallest element from each list and add it to the merged list. 我们知道列表是排序的,因此在每个阶段,我们只需从每个列表中获取最小元素并将其添加到合并列表中。 If, at the end, one of the iterators is exhausted and the other is not, then we can simply iterate through the one which still has elements, appending each element in turn to the merged list. 如果,最后,其中一个迭代器耗尽而另一个迭代器没有,那么我们可以简单地遍历仍然具有元素的那个,将每个元素依次附加到合并列表。

As you've seen, doing this with Iterators in Java is a bit of a pain as next() removes the element. 正如您所见,使用Java中的迭代器执行此操作有点痛苦,因为next()删除了元素。 One way of getting around this is to utilize two Queue s, one for each Iterator, that store the values from the call to next() . 解决这个问题的一种方法是利用两个Queue ,每个迭代器一个,用于存储调用next() You then need to compare the head of each queue, adding the minimum to the merged list and then removing it from its respective Queue . 然后,您需要比较每个队列的头部,将最小值添加到合并列表,然后将其从相应的Queue删除。

As you've found, it is sort of a pain to merge using iterators. 正如您所发现的,使用迭代器进行合并是一种痛苦。 Let's explicitly state why: 让我们明确说明原因:

  • For each step of the merge, you want to inspect the first element of both sequences, but you only want to advance through one . 对于合并的每个步骤,您需要检查 两个序列的第一个元素,但是您只想前进 一个
  • Iterator#next() bundles these inspect and advance operations into one operation, so it is impossible to inspect the head of both sequences without also advancing through both. Iterator#next()这些检查推进操作捆绑到一个操作中,因此不可能在没有前进的情况下检查两个序列的头部。

What you need is a way to peek at the first element in an Iterator without advancing it. 你需要的是一种在不推进Iterator情况下窥视 Iterator中的第一个元素的方法。 If you had this ability, then the merge would look something like: 如果你有这种能力,那么合并将类似于:

public <T extends Comparable<T>> List<T> merge(Iterator<T> it1, Iterator<T> it2) {
    PeekableIterator<T> seq1 = new PeekableIterator<T>(it1);
    PeekableIterator<T> seq2 = new PeekableIterator<T>(it2);
    List<T> merged = new ArrayList<T>();
    while (seq1.hasNext() && seq2.hasNext()) {
        if (seq1.peekNext().compareTo(seq2.peekNext()) < 0) {
            merged.add(seq1.next());
        } else {
            merged.add(seq2.next());
        }
    }
    while (seq1.hasNext()) {
        merged.add(seq1.next());
    }
    while (seq2.hasNext()) {
        merged.add(seq2.next());
    }
    return merged;
}

And it turns out that it is not too difficult to create this PeekableIterator ! 事实证明,创建这个PeekableIterator并不困难! You just need to keep track of whether or not you currently have a peeked element, and what that element is. 您只需要跟踪您当前是否有一个偷看元素,以及该元素是什么。

public class PeekableIterator<T> implements Iterator<T> {
    private final Iterator<T> backing;
    private boolean havePeek = false;
    private T peek;

    public PeekableIterator(Iterator<T> backing) {
        this.backing = backing;
    }

    @Override
    public boolean hasNext() {
        return havePeek || backing.hasNext();
    }

    @Override
    public T next() {
        if (havePeek) {
            havePeek = false;
            return peek;
        } else {
            return backing.next();
        }
    }

    public T peekNext() {
        if (!havePeek) {
            peek = backing.next();
            havePeek = true;
        }
        return peek;
    }

    @Override
    public void remove() {
        throw new UnsupportedOperationException();
    }
}

EDIT 编辑

I didn't notice the comment above referring to the PeekingIterator in Google's Guava library, which is more or less the same as the PeekableIterator here. 我没有注意到上面的评论引用了Google的Guava库中的PeekingIterator ,它与PeekableIterator相同。 If you have access to third party libraries, this would certainly be preferable to rolling your own. 如果您可以访问第三方库,这肯定比滚动自己的库更可取。

Don't try to manage merging of an empty list with a non empty one as a special case, just loop until at least one of the iterators is valid and do your work directly there: 不要尝试管理空列表与非空列表的合并作为特殊情况,只需循环直到至少有一个迭代器有效并直接在那里工作:

public static ListADT<String> merge(ListADT<String> L1,ListADT<String> L2) throws BadListException {
  ListADT<String> L3 = new ArrayList<String>;

  Iterator<String> itr1 = new L1.iterator(), itr2 = new L2.iterator();

  while (itr1.hasNext() || itr2.hasNext()) {
    if (!itr1.hasNext())
      L3.add(itr2.next());
    else if (!itr2.hasNext())
      L3.add(itr1.next());
    else {
      String s1 = peek from itr1
      String s2 = peek from itr2;

      if (s1.compareTo(s2) < 0) {
        L3.add(itr1.next());
        L3.add(itr2.next());
      }
      else {
        L3.add(itr2.next());
        L3.add(itr1.next())
      }
    }
  }
}

public class MergeIterator { 公共类MergeIterator {

public static void main(String[] args) {
       List<String> s1 = new ArrayList<String>();
    s1.add("a");
    s1.add("z");
    s1.add("b");
    s1.add("k");
    s1.add("c");
    Collections.sort(s1);
    List<String> s2 = new ArrayList<String>();
    s2.add("p");
    s2.add("a");
    s2.add("d");
    s2.add("n");
    s2.add("m");
    Collections.sort(s2);
    Iterator<String> it1 = s1.iterator();
    // sortIterator(it1);
    Iterator<String> it2 = s2.iterator();
    System.out.println();
    combine(it1, it2);
}

private static Iterator<String> combine(Iterator<String> it1,
        Iterator<String> it2) {
    Iterator<String> it3 = null;
    List<String> l1 = new ArrayList<>();
    String s1 = null, s2 = null;
    while (it1.hasNext() || it2.hasNext()) { //line 1

    s1 = (s1 == null ? (it1.hasNext() ? it1.next() : null) : s1); //line 2 
    s2 = (s2 == null ? (it2.hasNext() ? it2.next() : null) : s2); // line 3
        if (s1 != null && s1.compareTo(s2) < 0) { // line 4
            l1.add(s1);
            s1 = null;
        } else {
            l1.add(s2);
            s2 = null;
        }

    }
    it3 = l1.iterator();
    return it3;
}

} }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM