Birden çok Koleksiyonu tek bir mantıksal Koleksiyonda birleştirmek mi?


110

Bir sınıfın üyeleri olarak sabit sayıda koleksiyonum (örneğin 3 DiziListesi) olduğunu varsayalım. Şimdi, tüm öğeleri diğer sınıflara göstermek istiyorum, böylece tüm öğeler üzerinde basitçe yineleme yapabilirler (ideal olarak, salt okunur). Guava koleksiyonlarını kullanıyorum ve geçici kopyalar yapmadan dahili koleksiyonlar üzerinde mantıksal bir görünüm oluşturmak için guava yineleyicileri / yineleyicileri nasıl kullanabileceğimi merak ediyorum .


^^ Bozuk bağlantı. Guava Javadoc
RustyTheBoyRobot

Yanıtlar:


113

Guava ile kullanabilirsiniz Iterables.concat(Iterable<T> ...), tüm yinelenenlerin canlı bir görünümünü oluşturur, tek bir şekilde birleştirilir (yinelenenleri değiştirirseniz, birleştirilmiş sürüm de değişir). Ardından, birleştirilmiş yinelemeyi şununla sarın Iterables.unmodifiableIterable(Iterable<T>)(salt okunur gereksinimini daha önce görmemiştim).

Gönderen Iterables.concat( .. )javadocs:

Birden çok yinelemeyi tek bir yinelenebilir durumda birleştirir. Döndürülen yinelenebilir, girdilerdeki her yinelenebilir öğenin üzerinde gezinen bir yineleyiciye sahiptir. Girdi yineleyiciler gerekli olana kadar sorgulanmaz. Döndürülen yinelenebilir yineleyici remove() , karşılık gelen girdi yineleyicisi onu desteklediğinde destekler.

Bu, açıkça bunun canlı bir görüntü olduğunu söylemese de, son cümle bunun olduğunu ima eder ( Iterator.remove()yöntemi yalnızca destek yineleyicisi destekliyorsa desteklemek, canlı görüntü kullanılmadığı sürece mümkün değildir)

Basit kod:

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);

Çıktı:

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


Düzenle:

Damian'ın İsteği ile, canlı Koleksiyon Görünümü döndüren benzer bir yöntemi burada bulabilirsiniz

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() {
    }

}

Kullanıcının öğeleri kaldırmasını nasıl engellerim? Listeleri değiştirilemez listelere sarmaktan daha güzel bir yol var mı?
newgre


2
Ya koleksiyonlar? Iterables.concatüretir Iterable, değil Collection. Bir Collectionmanzaraya ihtiyacım var .
Nowaker

@Damian'ın bunun tek kullanışlı özelliği, birleştirilmiş size () yöntemine sahip olmasıdır. Koleksiyon arayüzündeki diğer tüm yöntemler ya tanımsız anlamlara (ekle vb.) Ya da kötü performansa (içerir vb.) Sahip olacaktır.
Sean Patrick Floyd

2
@Sean, evet - size()ihtiyacım olan şey. add()bir istisna atmak iyidir - bu yöntem umrumda değil. Koleksiyonlar API'si bozuk ve kimse bu konuda hiçbir şey yapamaz. Collection.add(),, Iterator.remove()falan.
Nowaker

101

Bir Stream.

Sabit sayı

Varsayalım private Collection<T> c, c2, c3.

Bir çözüm:

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

Başka bir çözüm:

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

Değişken sayı

Varsayalım private Collection<Collection<T>> cs:

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

10

En az Java 8 kullanıyorsanız, diğer cevabıma bakın .

Zaten Google Guava kullanıyorsanız Sean Patrick Floyd'un cevabına bakın .

Eğer Java 7 takılıp konum ve Google Guava dahil etmek istemiyorsanız, size kendi (salt okunur) yazabilirsiniz Iterables.concat()artık kullanmaktan daha Iterableve Iterator:

Sabit sayı

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();
                }
            };
        }
    };
}

Değişken sayı

@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

Yeni Listve addAll()diğerlerinden bir tane yaratabilirsin List. Ardından değiştirilemez bir liste ile dönün Collections.unmodifiableList().


3
Bu, potansiyel olarak oldukça pahalı olan yeni bir geçici koleksiyon oluşturacaktır
newgre

6
Pahalı nasıl , listelerde altındaki nesneleri kopyalanıp değildir ArrayListsadece uzay ve aramaları ayırır System.arraycopy()kaputun altında. Bundan daha verimli olamaz.
Qwerky

8
Her yineleme için bir koleksiyonun tamamını kopyalamak nasıl pahalı değildir ? Dahası, bundan daha iyisini elde edebilirsiniz, Seans yanıtına bakın.
newgre

Ayrıca belleği kopyalamak için yerel bir uygulama kullanır, dizi boyunca yineleme yapmaz.
Qwerky

1
İyi bu kesinlikle yok bir O (n) algoritmasıdır dizi kopyalama durumunda değil ölçek ve bir kez dizi yineleme aynı karmaşıklığı. Her listenin bir milyon öğe içerdiğini varsayalım, o zaman birkaç milyon öğeyi kopyalamam gerekiyor, yalnızca bunların üzerinde yinelemem gerekiyor. Kötü bir fikir.
newgre

0

İşte bunun için çözümüm:

EDIT - kodu biraz değiştirdi

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();
                }
            };
        }
    };
}

Sizin uygulama rollerini çevirir hasNext()ve next(). İlki yineleyicinizin durumunu değiştirirken, ikincisi değişmez. Öbür türlü olmalı. Aramadan next()arama hasNext()her zaman sonuç verecektir null. Aramadan hasNext()arama next(), öğeleri çöpe atacaktır. Sizin de next()atmaz NoSuchElementException, onun yerine geri döner null.
xehpuk
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.