Предположим, что у меня есть постоянное количество коллекций (например, 3 ArrayLists) в качестве членов класса. Теперь я хочу выставить все элементы в другие классы, чтобы они могли просто перебирать все элементы (в идеале, только для чтения). Я использую коллекции guava, и мне интересно, как я мог использовать итераторы guava для создания логического представления внутренних коллекций без создания временных копий.
Объединить несколько коллекций в единую логическую коллекцию?
Ответ 1
С помощью Guava вы можете использовать Iterables.concat(Iterable<T> ...)
, он создает живое представление обо всех итерациях, объединенных в один (если вы измените итерации, конкатенированную версию также меняется). Затем заверните конкатенированный итерабель с Iterables.unmodifiableIterable(Iterable<T>)
(раньше я не видел требование только для чтения).
Из 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]
Edit:
По запросу от Damian, здесь аналогичный метод, который возвращает live 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() {
}
}
Ответ 2
Простые решения Java 8 с использованием Stream
.
Постоянное число
Предполагая 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, см. мой другой ответ.
Если вы уже используете Google Guava, см. ответ Шона Патрика Флойда.
Если вы застряли на 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
. Затем верните немодифицируемый список с помощью Collections.unmodifiableList()
.
Ответ 5
Вот мое решение для этого:
EDIT - немного изменился код
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();
}
};
}
};
}