将多个集合合并为一个逻辑集合?

时间:2011-02-04 09:57:03

标签: java collections guava

假设我有一个恒定数量的集合(例如3个ArrayLists)作为类的成员。现在,我想将所有元素公开给其他类,以便它们可以简单地遍历所有元素(理想情况下,只读)。 我正在使用guava集合,我想知道如何使用guava iterables / iterators在内部集合上生成逻辑视图,而不用制作临时副本。

5 个答案:

答案 0 :(得分:111)

使用Guava,您可以使用Iterables.concat(Iterable<T> ...),它创建所有迭代的实时视图,连接成一个(如果您更改迭代,连接版本也会更改)。然后用Iterables.unmodifiableIterable(Iterable<T>)包装连接的iterable(我之前没有看过只读的要求)。

来自Iterables.concat( .. ) JavaDocs:

  

将多个迭代组合成一个   单一可迭代。返回的可迭代   有一个遍历的迭代器   输入中每个可迭代的元素。   不轮询输入迭代器   直到必要。归来了   iterable的迭代器支持remove()   当相应的输入迭代器时   支持它。

虽然这没有明确说明这是一个实时视图,但最后一句意味着它是(仅当支持迭代器支持它时才支持Iterator.remove()方法,除非使用实时视图)< / p>

示例代码:

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]


编辑:

根据Damian的请求,这是一个返回实时集合视图的类似方法

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

}

答案 1 :(得分:97)

使用Stream的普通Java 8解决方案。

常数

假设private Collection<T> c, c2, c3

一个解决方案:

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

答案 2 :(得分:10)

如果您至少使用Java 8,请参阅my other answer

如果您已使用Google Guava,请参阅Sean Patrick Floyd's answer

如果您遇到Java 7并且不想包含Google Guava,您可以使用不超过Iterables.concat()和{{1}编写自己的(只读)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();
                }
            };
        }
    };
}

答案 3 :(得分:0)

您可以为其他List创建一个新的addAll()List。然后使用Collections.unmodifiableList()返回一个不可修改的列表。

答案 4 :(得分: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();
                }
            };
        }
    };
}