将多个集合组合成一个逻辑集合?
Posted
技术标签:
【中文标题】将多个集合组合成一个逻辑集合?【英文标题】:Combine multiple Collections into a single logical Collection? 【发布时间】:2011-06-21 06:23:04 【问题描述】:假设,我有一个固定数量的集合(例如 3 个 ArrayLists)作为一个类的成员。现在,我想将所有元素公开给其他类,以便它们可以简单地迭代所有元素(理想情况下,只读)。 我正在使用番石榴集合,我想知道如何使用番石榴迭代器/迭代器来生成内部集合的逻辑视图而不制作临时副本。
【问题讨论】:
^^ 链接断开。我想他指的是this method in the Guava Javadoc 【参考方案1】:使用 Guava,您可以使用Iterables.concat(Iterable<T> ...)
,它会创建所有可迭代对象的实时视图,并连接为一个(如果您更改可迭代对象,则连接版本也会更改)。然后用Iterables.unmodifiableIterable(Iterable<T>)
包装连接的可迭代对象(我之前没有看到只读要求)。
来自Iterables.concat( .. )
JavaDocs:
将多个可迭代对象组合成一个 单个可迭代。返回的可迭代 有一个遍历 输入中每个可迭代的元素。 不轮询输入迭代器 直到必要。返回的 iterable 的迭代器支持
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 的请求,这里有一个类似的方法,它返回一个实时的 Collection View
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 更好的方法? @jn_ 只需将其包裹在Iterables.unmodifiableIterable(iterable)
收藏呢? Iterables.concat
产生 Iterable
,而不是 Collection
。我需要Collection
视图。
@Damian 唯一有用的功能是拥有一个聚合的 size() 方法。 Collection 接口中的所有其他方法要么具有未定义的语义(添加等),要么具有糟糕的性能(包含等)。
@Sean,是的 - size()
是我需要的。 add()
抛出异常很好——我不关心这个方法。 Collections API 已损坏,没有人可以对此做任何事情。 Collection.add()
,Iterator.remove()
,等等。【参考方案2】:
使用 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);
【讨论】:
【参考方案3】:如果您至少使用 Java 8,请参阅 my other answer。
如果您已经在使用 Google Guava,请参阅 Sean Patrick Floyd's answer。
如果您卡在 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();
;
【讨论】:
【参考方案4】:您可以为它创建一个新的List
和addAll()
以及其他List
s。然后用Collections.unmodifiableList()
返回一个不可修改的列表。
【讨论】:
这将创建一个可能相当昂贵的新临时集合 昂贵的如何,列表中的底层对象不会被复制,ArrayList
只是分配空间并在后台调用System.arraycopy()
。没有比这更有效的了。
如何为每次迭代复制整个集合不昂贵?此外,您可以做得更好,请参阅 Seans 的回答。
它也使用原生实现来复制内存,它不会遍历数组。
好吧,如果它正在复制数组,那肯定是一个 O(n) 算法,它不 缩放并且具有与遍历数组一次相同的复杂性。假设每个列表包含一百万个元素,那么我需要复制几百万个元素,只是为了迭代它们。坏主意。【参考方案5】:
这是我的解决方案:
编辑 - 稍微更改代码
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()
的角色。第一个会更改迭代器的状态,而第二个不会。应该反过来。调用next()
而不调用hasNext()
将始终产生null
。调用 hasNext()
而不调用 next()
将丢弃元素。您的next()
也不会抛出NoSuchElementException
,而是返回null
。
你重新发明了org.apache.commons.collections4.IterableUtils.chainedIterable()
以上是关于将多个集合组合成一个逻辑集合?的主要内容,如果未能解决你的问题,请参考以下文章