Java でリストを逆順にイテレートする

f:id:Naotsugu:20191027210540p:plain


はじめに

以下のようなリストを逆順で末尾からイテレートしたいケースがあります。

List<String> list = Arrays.asList("A", "B", "C", "D");

でもこれ、意外とスマートにできません。


もちろん以下のようにすることで逆順にすることはできます。

Collections.reverse(list);
list.forEach(System.out::println);

しかしこの場合は、元のリスト自体の並びが変わってしまいます。


別のリストを作成して並び替えれば良いですが、リストの要素数が大きい場合は効率が良くありません。

List<String> copy = new ArrayList<>(list);
Collections.reverse(copy);
copy.forEach(System.out::println);


ListIterator による逆順イテレート

Iterator はイテレートで前進することしかできませんが、ListIterator は後退することができます。

list.listIterator(list.size()) で末尾に位置する ListIterator を取得し、previous() でリストを逆順にイテレートすることができます。

for (ListIterator<String> i = list.listIterator(list.size()); i.hasPrevious();) {
    System.out.println(i.previous());
}


ListIterator ユーティリティを利用した逆順イテレート

以下のようなユーティリティを作成します。

public static <T> Iterator<T> descendingIteratorOf(List<T> list) {
    return new Iterator<T>() {

        private final ListIterator<T> irt = list.listIterator(list.size());

        @Override
        public boolean hasNext() {
            return irt.hasPrevious();
        }

        @Override
        public T next() {
            return irt.previous();
        }
    };
}


このユーティリティを使えば以下のように

List<String> list = Arrays.asList("A", "B", "C", "D");
descendingIteratorOf(list).forEachRemaining(System.out::println); // D C B A


Stream として扱いたい場合は以下のようにできます。

Iterable<String> iterable = () -> descendingIteratorOf(list);
StreamSupport.stream(iterable.spliterator(), false)
        .map(String::toLowerCase)
        .forEach(System.out::println); // d c b a

Iterable を経由させ、StreamSupport で処理します。


Stream の取得については以下を参照してください。

blog1.mammb.com


DescendingIterator による逆順イテレート

元にするリストが LinkedListArrayDeque のように Deque インターフェースを実装している場合は、descendingIterator() を使うことができます。

Deque<String> deque = new LinkedList<>(list);
for (Iterator<String> i = deque.descendingIterator(); i.hasNext();) {
    System.out.println(i.next());
}


以下でも同様です。

deque.descendingIterator().forEachRemaining(System.out::println);


元のリストが ArrayList などであり、要素数が大きな場合は、LinkedList インスタンスを新たに作成しなければならないため ListIterator を利用した方が効率が良いです。



Effective Java 第3版

Effective Java 第3版

  • 作者:Joshua Bloch
  • 出版社/メーカー: 丸善出版
  • 発売日: 2018/10/30
  • メディア: 単行本(ソフトカバー)

プログラミング言語Java

プログラミング言語Java

Head First Java: A Brain-Friendly Guide

Head First Java: A Brain-Friendly Guide

  • 作者:Kathy Sierra,Bert Bates
  • 出版社/メーカー: O'Reilly Media
  • 発売日: 2005/02/22
  • メディア: ペーパーバック