รวมหลายคอลเลกชันเป็นคอลเลกชันเชิงตรรกะเดียว?


110

สมมติว่าฉันมีคอลเล็กชันจำนวนคงที่ (เช่น 3 ArrayLists) ในฐานะสมาชิกของคลาส ตอนนี้ฉันต้องการเปิดเผยองค์ประกอบทั้งหมดไปยังคลาสอื่น ๆ เพื่อให้พวกเขาสามารถทำซ้ำองค์ประกอบทั้งหมดได้อย่างง่ายดาย (ควรอ่านอย่างเดียว) ฉันใช้คอลเลกชัน guava และฉันสงสัยว่าฉันจะใช้ guava iterables / iterators เพื่อสร้างมุมมองเชิงตรรกะบนคอลเลกชันภายในได้อย่างไรโดยไม่ต้องทำสำเนาชั่วคราว


^^ ลิงค์เสีย ฉันคิดว่าเขากำลังชี้ไปที่วิธีนี้ใน Guava Javadoc
RustyTheBoyRobot

คำตอบ:


113

ด้วย Guava คุณสามารถใช้Iterables.concat(Iterable<T> ...)มันสร้างมุมมองสดของ iterables ทั้งหมดที่เชื่อมต่อกันเป็นหนึ่งเดียว (ถ้าคุณเปลี่ยน iterables เวอร์ชันที่ต่อกันจะเปลี่ยนไปด้วย) จากนั้นห่อส่วนที่สามารถทำซ้ำได้ด้วย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]


แก้ไข:

ตามคำขอจาก 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() {
    }

}

ฉันจะป้องกันไม่ให้ผู้ใช้ลบองค์ประกอบได้อย่างไร มีวิธีที่ดีกว่าการรวมรายการไว้ใน unmodifiableLists หรือไม่?
newgre


2
สิ่งที่เกี่ยวกับคอลเลกชัน? Iterables.concatสร้างIterableไม่ใช่Collection. ฉันต้องการCollectionมุมมอง
Nowaker

@Damian คุณลักษณะที่มีประโยชน์เพียงอย่างเดียวคือมีวิธีการขนาดรวม () วิธีการอื่น ๆ ทั้งหมดในอินเทอร์เฟซการรวบรวมอาจมีความหมายที่ไม่ได้กำหนดไว้ (เพิ่ม ฯลฯ ) หรือประสิทธิภาพที่น่าสังเวช (มี ฯลฯ )
Sean Patrick Floyd

2
@ ฌอนใช่ - size()คือสิ่งที่ฉันต้องการ add()การโยนข้อยกเว้นเป็นสิ่งที่ดี - ฉันไม่สนใจวิธีนี้ Collections API เสียและไม่มีใครทำอะไรกับมันได้ Collection.add(),, บIterator.remove()ลา ๆ .
Nowaker

101

โซลูชัน 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);
}

10

หากคุณกำลังใช้อย่างน้อย Java 8 ดูคำตอบอื่น ๆ ของฉัน

หากคุณกำลังใช้ Google ฝรั่งดูคำตอบของฌอนแพทริคฟลอยด์

หากคุณติดอยู่ที่ 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();
        }
    };
}

1

คุณสามารถสร้างใหม่ListและaddAll()อื่น ๆ ของคุณListได้ จากนั้นกลับรายการ unmodifiable Collections.unmodifiableList()กับ


3
นั่นจะเป็นการสร้างคอลเลคชันชั่วคราวใหม่ซึ่งอาจมีราคาค่อนข้างแพง
ใหม่

6
ราคาแพงอย่างไรวัตถุที่อยู่ภายใต้รายการจะไม่ถูกคัดลอกและArrayListเพียงแค่จัดสรรพื้นที่และการโทรSystem.arraycopy()ภายใต้ประทุน ไม่สามารถมีประสิทธิภาพมากไปกว่านั้น
Qwerky

8
การคัดลอกคอลเลกชันทั้งหมดสำหรับการทำซ้ำทุกครั้งนั้นไม่แพงอย่างไร ยิ่งไปกว่านั้นคุณจะได้รับสิ่งที่ดีกว่านั้นดู Seans ตอบ
ใหม่

นอกจากนี้ยังใช้การใช้งานแบบเนทีฟเพื่อคัดลอกหน่วยความจำโดยจะไม่วนซ้ำผ่านอาร์เรย์
Qwerky

1
ถ้าเป็นการคัดลอกอาร์เรย์แน่นอนว่าเป็นอัลกอริทึม O (n) ซึ่งไม่ได้ปรับขนาดและมีความซับซ้อนเหมือนกับการทำซ้ำบนอาร์เรย์เพียงครั้งเดียว สมมติว่าแต่ละรายการมีองค์ประกอบเป็นล้านองค์ประกอบจากนั้นฉันต้องคัดลอกองค์ประกอบไม่กี่ล้านรายการเพื่อที่จะวนซ้ำ ความคิดที่ไม่ดี
newgre

0

นี่คือทางออกของฉันสำหรับสิ่งนั้น:

แก้ไข - เปลี่ยนรหัสเล็กน้อย

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แต่ผลตอบแทน
xehpuk
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.