【问题标题】:Combine multiple Collections into a single logical Collection?将多个集合组合成一个逻辑集合?
【发布时间】:2011-06-21 06:23:04
【问题描述】:

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

【问题讨论】:

标签: java collections guava


【解决方案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&lt;T&gt; 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&lt;Collection&lt;T&gt;&gt; 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(),使用不超过 IterableIterator

    常数

    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】:

      您可以为它创建一个新的ListaddAll() 以及其他Lists。然后用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()
      猜你喜欢
      • 2017-04-20
      • 2012-01-01
      • 2019-02-05
      • 1970-01-01
      • 2015-02-26
      • 1970-01-01
      • 1970-01-01
      • 2011-10-28
      相关资源
      最近更新 更多