簡體   English   中英

簡化在Java中編寫自定義迭代器的過程

[英]Simplify writing custom iterators in Java

在Java中為自定義集合編寫迭代器非常復雜,因為您不必編寫提供一個元素的直接代碼,而是必須編寫狀態機:

public class CustomCollection<T> implements Iterable<T>
{
    private T[] data;
    private int size;

    @Override
    public Iterator<T> iterator()
    {
        return new Iterator<T>()
        {
            private int cursor = 0;

            @Override
            public boolean hasNext()
            {
                return cursor < size;
            }

            @Override
            public T next()
            {
                return data[cursor++];
            }

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

對於比數組列表或鏈表更復雜的集合,正確獲取這些狀態機是一項艱巨的任務。 事實上,C#設計團隊認為編寫自定義迭代器足夠復雜,可以引入特殊的語言支持( yield return ),讓編譯器構建狀態機。

在下一版本的Java中會出現類似於yield return嗎? 或者是否有任何庫解決方案使我在Java中編寫自己的迭代器時生活更輕松?

不,Java沒有像yield這樣的東西。 就庫而言, Guava有許多有用的類可以使某些類型的迭代器易於編寫:

  • AbstractIterator只需要您實現T computeNext()方法。
  • AbstractLinkedIterator要求您實現T computeNext(T previous)

AbstractIterator可用於此,如下所示:

return new AbstractIterator<T>() {
  private int index = 0;

  protected T computeNext() {
    return index == size ? endOfData() : data[index++];
  }
};

您也可以像Amir建議的那樣使用Arrays.asList ,甚至可以這樣做:

private final List<T> listView = new AbstractList<T>() {
  public int size() {
    return data.length;
  }

  public T get(int index) {
    return data[index];
  }
};

public Iterator<T> iterator() {
  return listView.iterator();
}

也許我只是不理解你的問題。 你能不能return Arrays.asList(data).iterator()

Java總是提供一種機制來維護狀態並在以后的某個時間繼續執行:線程。 我的庫解決方案的基本思想是讓ConcurrentIterable在一個線程中生成元素,讓ConcurrentIterator在另一個線程中使用它們,通過有界隊列進行通信。 (這通常被稱為生產者/消費者模式。)

首先,這是簡化用法的演示:

public class CustomCollection<T> extends ConcurrentIterable<T>
{
    private T[] data;
    private int size;

    @Override
    protected void provideElements()
    {
        for (int i = 0; i < size; ++i)
        {
            provideElement(data[i]);
        }
    }
    // ...
}

請注意完全沒有狀態機。 您所要做的就是從ConcurrentIterable派生並實現方法provideElements 在此方法中,您可以編寫直接代碼,該代碼為集合中的每個元素調用provideElement

有時,客戶端不會遍歷整個集合,例如在線性搜索中。 一旦檢測到墮胎,您可以通過檢查iterationAborted()來停止提供元素:

    @Override
    protected void provideElements()
    {
        for (int i = 0; i < size && !iterationAborted(); ++i)
        {
            provideElement(data[i]);
        }
    }

只要您不關心生成的其他元素,檢查iterationAborted()就完全沒問題。 對於無限序列,檢查iterationAborted()是必需的。

生產者如何檢測消費者已停止迭代? 這是通過對消費者中的令牌的強引用以及對生產者中的相同令牌的弱引用來實現的。 當消費者停止迭代時,該令牌變得有資格進行垃圾收集,並且它最終將對生產者不可見。 從那時起,所有新元素都將被丟棄。

(如果沒有這種預防措施,在某些情況下,有界隊列最終可能會填滿,生產者將進入無限循環,並且所包含的元素永遠不會被垃圾收集。)

現在為實施細節:

ConcurrentIterable.java

import java.util.Iterator;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

public abstract class ConcurrentIterable<T> implements Iterable<T>
{
    private static final int CAP = 1000;
    private final ThreadLocal<CommunicationChannel<T>> channels
    = new ThreadLocal<CommunicationChannel<T>>();

    @Override
    public Iterator<T> iterator()
    {
        BlockingQueue<Option<T>> queue = new ArrayBlockingQueue<Option<T>>(CAP);
        Object token = new Object();
        final CommunicationChannel<T> channel
        = new CommunicationChannel<T>(queue, token);
        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                channels.set(channel);
                provideElements();
                enqueueSentinel();
            }
        }).start();
        return new ConcurrentIterator<T>(queue, token);
    }

    protected abstract void provideElements();

    protected final boolean iterationAborted()
    {
        return channels.get().iterationAborted();
    }

    protected final void provideElement(T element)
    {
        enqueue(Option.some(element));
    }

    private void enqueueSentinel()
    {
        enqueue(Option.<T> none());
    }

    private void enqueue(Option<T> element)
    {
        try
        {
            while (!offer(element))
            {
                System.gc();
            }
        }
        catch (InterruptedException ignore)
        {
            ignore.printStackTrace();
        }
    }

    private boolean offer(Option<T> element) throws InterruptedException
    {
        CommunicationChannel<T> channel = channels.get();
        return channel.iterationAborted()
            || channel.queue.offer(element, 1, TimeUnit.SECONDS);
    }
}

CommunicationChannel.java

import java.lang.ref.WeakReference;
import java.util.concurrent.BlockingQueue;

public class CommunicationChannel<T>
{
    public final BlockingQueue<Option<T>> queue;
    private final WeakReference<Object> token;

    public CommunicationChannel(BlockingQueue<Option<T>> queue, Object token)
    {
        this.queue = queue;
        this.token = new WeakReference<Object>(token);
    }

    public boolean iterationAborted()
    {
        return token.get() == null;
    }
}

ConcurrentIterator.java

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.BlockingQueue;

public class ConcurrentIterator<T> implements Iterator<T>
{
    private final BlockingQueue<Option<T>> queue;
    @SuppressWarnings("unused")
    private final Object token;
    private Option<T> next;

    public ConcurrentIterator(BlockingQueue<Option<T>> queue, Object token)
    {
        this.queue = queue;
        this.token = token;
    }

    @Override
    public boolean hasNext()
    {
        if (next == null)
        {
            try
            {
                next = queue.take();
            }
            catch (InterruptedException ignore)
            {
                ignore.printStackTrace();
            }
        }
        return next.present;
    }

    @Override
    public T next()
    {
        if (!hasNext()) throw new NoSuchElementException();
        T result = next.value;
        next = null;
        return result;
    }

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

Option.java

public class Option<T>
{
    public final T value;
    public final boolean present;

    private Option(T value, boolean present)
    {
        this.value = value;
        this.present = present;
    }

    public static <T> Option<T> some(T value)
    {
        return new Option<T>(value, true);
    }

    @SuppressWarnings("unchecked")
    public static <T> Option<T> none()
    {
        return none;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private static final Option none = new Option(null, false);
}

讓我知道你的想法!

如果您不介意啟動新線程,可以使用SynchronousQueue

public class InternalIterator<T> implements Iterator<T>{

    private SynchronousQueue<T> queue = new SynchronousQueue<T>();
    private volatile boolean empty = false;
    private T current =null;
    private Object lock = new Object();

    private Runner implements Runnable{//run in deamon

        public void run(){
            //iterate and call 
            synchronized()(lock){
                try{
                    queue.offer(t);
                    lock.wait();
                }catch(InteruptedException e){
                    empty=true;
                    throw new RuntimeException(e);
                }
            } 
            //for each element to insert this will be the yield return 

            emtpy=true;
        }

    }

    public boolean hasNext(){
        if(current!=null)return true;

        while(!empty){
            if( (current=queue.poll(100,TimeUnit.MILLISECONDS))!=null){//avoid deadlock when last element is already returned but empty wasn't written yet 
                return true;
            }
        }

        return false;
    }

    public boolean next(){
        if(!hasNext())throw new NoSuchElementException();
        T tmp = current;
        current=null;
        return tmp;
    }

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


}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM