複数のコレクションを1つの論理コレクションに結合しますか?


110

クラスのメンバーとして、一定数のコレクション(たとえば、3つのArrayLists)があると仮定します。ここで、すべての要素を他のクラスに公開して、それらがすべての要素を単純に反復処理できるようにしたい(理想的には、読み取り専用)。私はグアバコレクションを使用していますが、グアバイテラブル/イテレータを使用して、一時的なコピー作成せずに内部コレクションの論理ビューを生成する方法を知りたいと思います。


回答:


113

Guavaを使用するとIterables.concat(Iterable<T> ...)、すべてのイテラブルのライブビューが作成され、1つに連結されます(イテラブルを変更すると、連結されたバージョンも変更されます)。次に、連結された反復可能オブジェクトをラップしますIterables.unmodifiableIterable(Iterable<T>)(以前に読み取り専用要件を見たことはありませんでした)。

Iterables.concat( .. )JavaDocs から:

複数のイテラブルを単一のイテラブルに結合します。返されるイテラブルには、入力内の各イテラブルの要素をトラバースするイテレータがあります。入力反復子は、必要になるまでポーリングされません。返されたイテラブルのイテレータはremove() 、対応する入力イテレータがサポートする場合にサポートします。

これはライブビューであることを明示的に示していませんが、最後の文はそれがライブビューであることを示しています(Iterator.remove()ライブビューを使用しない限り、バッキングイテレータがサポートしている場合にのみメソッドをサポートすることはできません)。

サンプルコード:

final List<Integer> first  = Lists.newArrayList(1, 2, 3);
final List<Integer> second = Lists.newArrayList(4, 5, 6);
final List<Integer> third  = Lists.newArrayList(7, 8, 9);
final Iterable<Integer> all =
    Iterables.unmodifiableIterable(
        Iterables.concat(first, second, third));
System.out.println(all);
third.add(9999999);
System.out.println(all);

出力:

[
1、2、3、4、5、6、7、8、9] [1、2、3、4、5、6、7、8、9、9999999]


編集:

ダミアンからのリクエストにより、ライブコレクションビューを返す同様のメソッドを以下に示します。

public final class CollectionsX {

    static class JoinedCollectionView<E> implements Collection<E> {

        private final Collection<? extends E>[] items;

        public JoinedCollectionView(final Collection<? extends E>[] items) {
            this.items = items;
        }

        @Override
        public boolean addAll(final Collection<? extends E> c) {
            throw new UnsupportedOperationException();
        }

        @Override
        public void clear() {
            for (final Collection<? extends E> coll : items) {
                coll.clear();
            }
        }

        @Override
        public boolean contains(final Object o) {
            throw new UnsupportedOperationException();
        }

        @Override
        public boolean containsAll(final Collection<?> c) {
            throw new UnsupportedOperationException();
        }

        @Override
        public boolean isEmpty() {
            return !iterator().hasNext();
        }

        @Override
        public Iterator<E> iterator() {
            return Iterables.concat(items).iterator();
        }

        @Override
        public boolean remove(final Object o) {
            throw new UnsupportedOperationException();
        }

        @Override
        public boolean removeAll(final Collection<?> c) {
            throw new UnsupportedOperationException();
        }

        @Override
        public boolean retainAll(final Collection<?> c) {
            throw new UnsupportedOperationException();
        }

        @Override
        public int size() {
            int ct = 0;
            for (final Collection<? extends E> coll : items) {
                ct += coll.size();
            }
            return ct;
        }

        @Override
        public Object[] toArray() {
            throw new UnsupportedOperationException();
        }

        @Override
        public <T> T[] toArray(T[] a) {
            throw new UnsupportedOperationException();
        }

        @Override
        public boolean add(E e) {
            throw new UnsupportedOperationException();
        }

    }

    /**
     * Returns a live aggregated collection view of the collections passed in.
     * <p>
     * All methods except {@link Collection#size()}, {@link Collection#clear()},
     * {@link Collection#isEmpty()} and {@link Iterable#iterator()}
     *  throw {@link UnsupportedOperationException} in the returned Collection.
     * <p>
     * None of the above methods is thread safe (nor would there be an easy way
     * of making them).
     */
    public static <T> Collection<T> combine(
        final Collection<? extends T>... items) {
        return new JoinedCollectionView<T>(items);
    }

    private CollectionsX() {
    }

}

ユーザーが要素を削除できないようにするにはどうすればよいですか?リストをunmodifiableListsにラップするよりも良い方法はありますか?
newgre


2
コレクションはどうですか?ではなくをIterables.concat生成します。ビューが必要になります。IterableCollectionCollection
Nowaker、2011

@Damianの唯一の便利な機能は、集約されたsize()メソッドを持つことです。Collectionインターフェースの他のすべてのメソッドは、未定義のセマンティクス(addなど)または悲惨なパフォーマンス(containsなど)を持っています。
Sean Patrick Floyd、

2
@ショーン、はい- size()私が必要なものです。add()例外をスローするのは良いことです-私はこのメソッドを気にしません。コレクションAPIが壊れており、誰もそれについて何もできません。Collection.add()Iterator.remove()、何とか。
Nowaker、2011

101

を使用したプレーンJava 8ソリューションStream

定数

と仮定しprivate Collection<T> c, c2, c3ます。

1つの解決策:

public Stream<T> stream() {
    return Stream.concat(Stream.concat(c.stream(), c2.stream()), c3.stream());
}

別の解決策:

public Stream<T> stream() {
    return Stream.of(c, c2, c3).flatMap(Collection::stream);
}

変数番号

仮定private Collection<Collection<T>> cs

public Stream<T> stream() {
    return cs.stream().flatMap(Collection::stream);
}

10

Java 8以上を使用している場合は、他の回答を参照しください。

すでにGoogle Guavaを使用している場合は、Sean Patrick Floydの回答をご覧ください。

Java 7で立ち往生していて、Google Guavaを含めたくない場合はIterables.concat()Iterableおよびを使用して独自の(読み取り専用)を作成できますIterator

定数

public static <E> Iterable<E> concat(final Iterable<? extends E> iterable1,
                                     final Iterable<? extends E> iterable2) {
    return new Iterable<E>() {
        @Override
        public Iterator<E> iterator() {
            return new Iterator<E>() {
                final Iterator<? extends E> iterator1 = iterable1.iterator();
                final Iterator<? extends E> iterator2 = iterable2.iterator();

                @Override
                public boolean hasNext() {
                    return iterator1.hasNext() || iterator2.hasNext();
                }

                @Override
                public E next() {
                    return iterator1.hasNext() ? iterator1.next() : iterator2.next();
                }
            };
        }
    };
}

変数番号

@SafeVarargs
public static <E> Iterable<E> concat(final Iterable<? extends E>... iterables) {
    return concat(Arrays.asList(iterables));
}

public static <E> Iterable<E> concat(final Iterable<Iterable<? extends E>> iterables) {
    return new Iterable<E>() {
        final Iterator<Iterable<? extends E>> iterablesIterator = iterables.iterator();

        @Override
        public Iterator<E> iterator() {
            return !iterablesIterator.hasNext() ? Collections.emptyIterator()
                                                : new Iterator<E>() {
                Iterator<? extends E> iterableIterator = nextIterator();

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

                @Override
                public E next() {
                    final E next = iterableIterator.next();
                    findNext();
                    return next;
                }

                Iterator<? extends E> nextIterator() {
                    return iterablesIterator.next().iterator();
                }

                Iterator<E> findNext() {
                    while (!iterableIterator.hasNext()) {
                        if (!iterablesIterator.hasNext()) {
                            break;
                        }
                        iterableIterator = nextIterator();
                    }
                    return this;
                }
            }.findNext();
        }
    };
}

1

新しいListaddAll()Listのを作成できます。次に、で変更不可能なリストを返しCollections.unmodifiableList()ます。


3
それは潜在的に非常に高価である新しい一時的なコレクション作成します
newgre

6
高価な方法ですが、リスト内の基になるオブジェクトはコピーされず、内部でArrayListスペースを割り当てて呼び出しますSystem.arraycopy()。それよりもはるかに効率的になることはできません。
Qwerky、2011

8
繰り返しごとにコレクション全体をコピーするのコストがかかりません。さらに、あなたはそれより良くなることができます、ショーンズの答えを見てください。
newgre

また、ネイティブ実装を使用してメモリをコピーします。配列を反復処理しません。
Qwerky

1
まあそれが配列をコピーしている場合、それは確かにスケーリングしない O(n)アルゴリズムであり、一度配列を反復するのと同じ複雑さを持っています。各リストに100万個の要素が含まれていると仮定すると、数百万個の要素をコピーして、それらを反復するだけで済みます。悪いアイデア。
newgre

0

これが私の解決策です:

編集-コードを少し変更

public static <E> Iterable<E> concat(final Iterable<? extends E> list1, Iterable<? extends E> list2)
{
    return new Iterable<E>()
    {
        public Iterator<E> iterator()
        {
            return new Iterator<E>()
            {
                protected Iterator<? extends E> listIterator = list1.iterator();
                protected Boolean checkedHasNext;
                protected E nextValue;
                private boolean startTheSecond;

                public void theNext()
                {
                    if (listIterator.hasNext())
                    {
                        checkedHasNext = true;
                        nextValue = listIterator.next();
                    }
                    else if (startTheSecond)
                        checkedHasNext = false;
                    else
                    {
                        startTheSecond = true;
                        listIterator = list2.iterator();
                        theNext();
                    }
                }

                public boolean hasNext()
                {
                    if (checkedHasNext == null)
                        theNext();
                    return checkedHasNext;
                }

                public E next()
                {
                    if (!hasNext())
                        throw new NoSuchElementException();
                    checkedHasNext = null;
                    return nextValue;

                }

                public void remove()
                {
                    listIterator.remove();
                }
            };
        }
    };
}

実装によりhasNext()、およびの役割が反転しますnext()。1つ目はイテレータの状態を変更しますが、2つ目は変更しません。それは逆でなければなりません。呼び出しnext()呼び出すことなく、hasNext()常に得られますnull。呼び出しhasNext()呼び出すことなくnext()要素を捨てます。あなたのnext()スローしませんNoSuchElementExceptionいずれか、代わりに戻りますnull
xehpuk 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.