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

2022-08-31 09:12:13

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


答案 1

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

From the 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]


编辑:

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

}

答案 2

使用流的普通 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);
}