Kết hợp nhiều Bộ sưu tập thành một Bộ sưu tập hợp lý?


110

Giả sử, tôi có một số lượng không đổi các bộ sưu tập (ví dụ 3 ArrayLists) là thành viên của một lớp. Bây giờ, tôi muốn hiển thị tất cả các phần tử cho các lớp khác để chúng có thể đơn giản lặp lại trên tất cả các phần tử (lý tưởng là chỉ đọc). Tôi đang sử dụng bộ sưu tập ổi và tôi tự hỏi làm thế nào tôi có thể sử dụng các trình lặp / vòng lặp ổi để tạo ra một chế độ xem hợp lý trên các bộ sưu tập bên trong mà không cần tạo các bản sao tạm thời.


^^ Liên kết bị hỏng. Tôi nghĩ anh ấy đã được trỏ đến phương pháp này trong ổi Javadoc
RustyTheBoyRobot

Câu trả lời:


113

Với Guava, bạn có thể sử dụng Iterables.concat(Iterable<T> ...), nó tạo ra một chế độ xem trực tiếp của tất cả các tệp lặp, được nối thành một (nếu bạn thay đổi các tệp lặp, phiên bản được nối cũng thay đổi). Sau đó, bọc có thể lặp được nối kết với Iterables.unmodifiableIterable(Iterable<T>)(tôi đã không thấy yêu cầu chỉ đọc trước đó).

Từ Iterables.concat( .. )JavaDocs:

Kết hợp nhiều tệp lặp lại thành một tệp có thể lặp lại. Có thể lặp lại được trả về có một trình vòng lặp đi qua các phần tử của từng có thể lặp trong các đầu vào. Các trình vòng lặp đầu vào không được thăm dò ý kiến ​​cho đến khi cần thiết. Trình vòng lặp của có thể lặp lại được trả về hỗ trợ remove() khi trình vòng lặp đầu vào tương ứng hỗ trợ nó.

Mặc dù điều này không nói rõ ràng rằng đây là một chế độ xem trực tiếp, nhưng câu cuối cùng ngụ ý rằng nó là như vậy (chỉ hỗ trợ Iterator.remove()phương thức nếu trình lặp hỗ trợ không thể thực hiện được trừ khi sử dụng chế độ xem trực tiếp)

Mã mẫu:

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

Đầu ra:

[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 9999999]


Biên tập:

Theo Yêu cầu từ Damian, đây là một phương pháp tương tự trả về Chế độ xem Bộ sưu tập trực tiếp

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

}

Làm cách nào để ngăn người dùng xóa các phần tử? Có cách nào đẹp hơn là gói các danh sách thành unmodifiableLists không?
newgre


2
Còn những bộ sưu tập thì sao? Iterables.concattạo ra một Iterable, không phải Collection. Tôi sẽ cần một Collectioncái nhìn.
Nowaker

@Damian, tính năng hữu ích duy nhất của điều đó là có một phương thức tổng hợp size (). Tất cả các phương thức khác trong giao diện Bộ sưu tập sẽ có ngữ nghĩa không xác định (thêm, v.v.) hoặc hiệu suất kém (chứa, v.v.).
Sean Patrick Floyd

2
@Sean, vâng - size()là thứ tôi cần. add()ném một ngoại lệ là tốt - tôi không quan tâm đến phương pháp này. API Bộ sưu tập bị hỏng và không ai có thể làm gì với nó. Collection.add(),, Iterator.remove()bla bla.
Nowaker

101

Các giải pháp Java 8 thuần túy sử dụng Stream .

Số không đổi

Giả định private Collection<T> c, c2, c3 .

Một cách giải quyết:

public Stream<T> stream() {
    return Stream.concat(Stream.concat(c.stream(), c2.stream()), c3.stream());
}

Giải pháp khác:

public Stream<T> stream() {
    return Stream.of(c, c2, c3).flatMap(Collection::stream);
}

Số biến

Giả sử private Collection<Collection<T>> cs:

public Stream<T> stream() {
    return cs.stream().flatMap(Collection::stream);
}

10

Nếu bạn đang sử dụng ít nhất Java 8, hãy xem câu trả lời khác của tôi .

Nếu bạn đã sử dụng Google Guava, hãy xem câu trả lời của Sean Patrick Floyd .

Nếu bạn gặp khó khăn ở Java 7 và không muốn bao gồm Google Guava, bạn có thể viết (chỉ đọc) của riêng mình Iterables.concat()bằng cách sử dụng không quá IterableIterator:

Số không đổi

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

Số biến

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

1

Bạn có thể tạo một cái mới ListaddAll()những cái khác của bạn Listcho nó. Sau đó, trả về một danh sách không thể sửa đổi với Collections.unmodifiableList().


3
Điều đó sẽ tạo ra một bộ sưu tập tạm thời mới đó là khả năng khá đắt
newgre

6
Đắt tiền như thế nào , các đối tượng cơ bản trong danh sách không được sao chép và ArrayListchỉ phân bổ không gian và gọi ẩn System.arraycopy(). Không thể hiệu quả hơn thế.
Qwerky

8
Làm thế nào để sao chép toàn bộ bộ sưu tập cho mỗi lần lặp lại không tốn kém? Hơn nữa, bạn có thể nhận được tốt hơn thế, hãy xem câu trả lời của Seans.
newgre

Nó cũng sử dụng một triển khai gốc để sao chép bộ nhớ, nó không lặp lại qua mảng.
Qwerky

1
Chà nếu nó đang sao chép mảng thì nó chắc chắn là một thuật toán O (n) không chia tỷ lệ và có cùng độ phức tạp khi lặp lại mảng một lần. Giả sử mỗi danh sách chứa một triệu phần tử, sau đó tôi cần sao chép một vài triệu phần tử, chỉ để lặp lại chúng. Ý kiến ​​tồi.
newgre

0

Đây là giải pháp của tôi cho điều đó:

EDIT - đã thay đổi mã một chút

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

Việc triển khai của bạn làm thay đổi vai trò của hasNext()next(). Đầu tiên thay đổi trạng thái của trình lặp của bạn trong khi thứ hai thì không. Nó phải là một cách khác xung quanh. Gọi next()mà không gọi hasNext()bao giờ cũng nhường null. Gọi hasNext()mà không gọi next()sẽ vứt bỏ các phần tử. Của bạn next()cũng không ném NoSuchElementException, nhưng thay vào đó trả lại null.
xehpuk
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.