交错迭代器方法

时间:2013-06-05 07:52:36

标签: java iterator

让我们说我们有一个迭代器:

Iterator i;

两种Java方法:

static X f(Iterator i)
static Y g(Iterator i)

它们都通过迭代器处理并产生结果,比如“sum”,“product”或其他东西。

请说出给定i的这些调用的结果是:

x == f(i)
y == g(i)

让我们说我们有另一个班级:

class Pair<X,Y>
{
  public X x;
  public Y y;
}

我想写一个方法:

static Pair<X,Y> h(Iterator i)

那样:

h(i) == Pair<X,Y>(x,y);

如何在不修改或重写hf的情况下撰写g?问题是我可能无法复制迭代器(例如它可能是输入流),所以我认为fg都必须在“并行”中使用迭代器”。我宁愿不把迭代器的全部内容放到列表中,也许不可能,好像我正在流式传输一个大型的源代码,我可能没有足够的内存。但是我不要求解决方案有多个线程,如果可能的话,我更愿意采用更简单的方法。

3 个答案:

答案 0 :(得分:2)

这与你拥有的迭代器无关。你需要一个非常详细的方案,类似于* nix的tee命令,仅适用于Java迭代器:一个包含你的初始迭代器的对象,并根据需要使用它,并且可以请求生成一个或多个“输出”迭代器。在里面,您可以使用队列来存储未消耗的元素。

如果你不想要多线程,那么从理论上讲很容易实现你必须缓存初始迭代器的整个产生。这是因为你的迭代器消耗方法都基于“拉”方法,并且在返回之前将坚持使用整个序列。

在Clojure中BTW项目 lamina 做了一些非常相似的事情,而不是Java迭代器,但是它的概念称之为“频道”。

答案 1 :(得分:1)

诀窍是让stacktrace知道这两个方法中哪一个调用了iterator.next方法。

只要两个呼叫者都没有呼叫next(),就会阻止前一个呼叫。

  • 这未经过优化。
  • 我试图使这段代码线程安全,但是当我快速编写它时,可能会出现一些同步问题。我让你回顾一下;)
  • 我让你删除很多System.out.println

originalIterator基于List,但使用您希望的数据结构。如您所见,stream.iterrator()只被调用一次。

public static class InterleavingIterator<E> implements Iterator<E> {

    private boolean hasNext;
    private E next;

    private final Iterator<E> originalIterator;

    private final Map<String, Integer> stepCaller = Collections.synchronizedMap(new HashMap<String, Integer>());
    private final AtomicInteger curStep = new AtomicInteger(0);

    public InterleavingIterator(Iterator<E> originalIterator, String caller1, String caller2) {
        this.originalIterator = originalIterator;
        stepCaller.put(caller1, 0);
        stepCaller.put(caller2, 0);

        hasNext = originalIterator.hasNext();
    }

    public boolean hasNext() {
        return hasNext;
    }

    public E next() {
        String caller = getCurrentCaller();
        int currentStep = curStep.get();
        System.out.println("caller is " + caller);
        if (stepCaller.get(caller) == currentStep) {
            System.out.println("Caller " + caller + " is on current step. We need to move on.");
            // we should go on next step. But first check that all caller are done with this step.
            while (otherCallerBehind(currentStep)) {
                System.out.println("Other caller are behind. Waiting for them...");
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("Ok, everybody is on current step. Going on.");

            synchronized (curStep) {
                if (currentStep == curStep.get()) {
                    // ok, go on.
                    curStep.incrementAndGet();
                    next = hasNext ? originalIterator.next() : null;
                    hasNext = originalIterator.hasNext();
                    System.out.println("hasNext=" + hasNext + ", next=" + next + ", currentStep=" + curStep.get());
                }
            }

        }
        E next2 = next;
        stepCaller.put(caller, stepCaller.get(caller) + 1);
        System.out.println("Caller " + caller + " is now at step " + stepCaller.get(caller) + ". Returning already fetch next : "
                + next);
        return next2;
    }

    private boolean otherCallerBehind(int currentStep) {
        for (Integer step : stepCaller.values()) {
            if (step < currentStep) {
                return true;
            }
        }
        return false;
    }

    public void remove() {
        throw new RuntimeException("Method remove not supported");
    }

    public String getCurrentCaller() {
        StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
        //            System.out.println(stackTraceElements[3].getMethodName());
        return stackTraceElements[3].getMethodName();
    }

}

public static void main(String[] args) {

    List<Integer> stream = Arrays.asList(1, 2, 3, 4, 5);

    final InterleavingIterator<Integer> interleavingIterator = new InterleavingIterator<Integer>(stream.iterator(), "f", "g");

    final AtomicInteger fres = new AtomicInteger(-1);
    final AtomicInteger gres = new AtomicInteger(-1);

    new Thread() {
        @Override
        public void run() {
            fres.set(f(interleavingIterator));
        };
    }.start();

    new Thread() {
        @Override
        public void run() {
            gres.set(g(interleavingIterator));
        };
    }.start();

    while (fres.get() == -1 && gres.get() == -1) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    System.out.println(fres);
    System.out.println(gres);
}

public static Integer f(Iterator<Integer> i) {
    Integer sum = 0;
    while (i.hasNext()) {
        sum += i.next();
    }
    return sum;
}

public static Integer g(Iterator<Integer> i) {
    Integer prod = 1;
    while (i.hasNext()) {
        prod *= i.next();
    }
    return prod;
}

答案 2 :(得分:0)

由于您可能知道要迭代的内容,因此可以获取值,将其放入新列表中,并将其迭代器传递给函数。

Pair<X,Y> getPair(Iterator i) {
  ValObj value = null;
  List<ValObj> artList = new LinkedList<ValObj>();

  while ((value=i.next())!=null) {
    artList.add(value);
  } 

  X x = f(artList.iterator());
  Y y = g(artList.iterator());

  return new Pair<X,Y>(x,y);
}

现在我注意到你不想将内容存储在列表中。如果是这种情况,没有关于迭代器的更多细节,我相信它是不可能的。