使用迭代器合并列表

时间:2013-02-11 01:54:38

标签: java list iterator

我需要在java中合并两个字符串列表,我不太确定最好的方法。我必须使用迭代器和compareTo()方法。例如......

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

我可以假设输入列表已经排序,我不能使用contains()方法。我有一些初步检查但是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?
}

}

任何帮助都将不胜感激。

5 个答案:

答案 0 :(得分:4)

如果你只是使用变量来保存每个迭代器的当前值,这是相当简单的。此解决方案假设您的列表不包含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;
    }
}

答案 1 :(得分:2)

这是基本算法的一些伪代码:

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

我们知道列表是排序的,因此在每个阶段,我们只需从每个列表中获取最小元素并将其添加到合并列表中。如果最后一个迭代器耗尽而另一个迭代器耗尽,那么我们可以简单地遍历仍然具有元素的迭代器,将每个元素依次附加到合并列表。

正如您所见,使用Java中的迭代器执行此操作有点痛苦,因为next()删除了元素。解决此问题的一种方法是使用两个Queue s,每个迭代器一个,用于存储调用next()的值。然后,您需要比较每个队列的头部,将最小值添加到合并列表,然后将其从相应的Queue中删除。

答案 2 :(得分:2)

正如您所发现的,使用迭代器进行合并有点痛苦。让我们明确说明原因:

  • 对于合并的每个步骤,您希望检查 两个序列的第一个元素,但您只想提前通过<强>一个即可。
  • Iterator#next()将这些 inspect advance 操作捆绑到一个操作中,因此如果不同时检查这两个序列的头部,则无法进行检查。< / LI>

你需要的是一种在Iterator中第一个元素偷看而不推进它的方法。如果你有这种能力,那么合并将类似于:

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

事实证明,创建此PeekableIterator并不困难!你只需要跟踪你当前是否有一个偷看的元素,以及那个元素是什么。

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

修改

我没有注意到上面的评论引用了Google的Guava库中的PeekingIterator,这与此处的PeekableIterator大致相同。如果您可以访问第三方库,这肯定比滚动自己更好。

答案 3 :(得分:0)

不要尝试管理空列表与非空列表的合并作为特殊情况,只需循环直到至少有一个迭代器有效并直接在那里工作:

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

答案 4 :(得分:0)

公共类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;
}

}