回答:
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() {
}
}
Iterables.unmodifiableIterable(iterable)
Iterables.concat
生成します。ビューが必要になります。Iterable
Collection
Collection
size()
私が必要なものです。add()
例外をスローするのは良いことです-私はこのメソッドを気にしません。コレクションAPIが壊れており、誰もそれについて何もできません。Collection.add()
、Iterator.remove()
、何とか。
を使用したプレーン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);
}
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();
}
};
}
新しいList
とaddAll()
他List
のを作成できます。次に、で変更不可能なリストを返しCollections.unmodifiableList()
ます。
ArrayList
スペースを割り当てて呼び出しますSystem.arraycopy()
。それよりもはるかに効率的になることはできません。
これが私の解決策です:
編集-コードを少し変更
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
。