簡體   English   中英

遍歷多個SortedSet對象的迭代器

[英]Iterator over multiple SortedSet objects

在Java中,我有幾個SortedSet實例。 我想迭代所有這些集合中的元素。 一個簡單的選擇是創建一個新的SortedSet ,例如TreeSet x ,使用x.addAll(y_i)將所有單個集合y_1 ,..., y_n的內容深度復制到其中,然后迭代x

但有沒有辦法避免深層復制? 我不能只創建一個SortedSet類型的視圖,它會以某種方式封裝所有內部集合的迭代器,但表現為單個集合?

我更喜歡現有的,經過測試的解決方案,而不是自己寫的。

我不知道有任何現有的解決方案來完成這項任務,所以我花時間為你寫了一個。 我確信它還有改進的余地,所以把它作為指導而不是別的。

桑德爾在答案中指出,必須施加或假設一些限制。 一個這樣的限制是每個SortedSet必須相對於相同的順序進行排序,否則在沒有創建新集合(表示每個單獨集合的並集)的情況下比較它們的元素沒有意義。

下面是我的代碼示例,正如您將注意到的,它比創建新集合並向其添加所有元素要復雜得多。

import java.util.*;

final class MultiSortedSetView<E> implements Iterable<E> {

    private final List<SortedSet<E>> sets = new ArrayList<>();
    private final Comparator<? super E> comparator;

    MultiSortedSetView() {
        comparator = null;
    }

    MultiSortedSetView(final Comparator<? super E> comp) {
        comparator = comp;
    }


    @Override
    public Iterator<E> iterator() {
        return new MultiSortedSetIterator<E>(sets, comparator);
    }


    MultiSortedSetView<E> add(final SortedSet<E> set) {
        // You may remove this `if` if you already know
        // every set uses the same comparator.
        if (comparator != set.comparator()) {
            throw new IllegalArgumentException("Different Comparator!");
        }
        sets.add(set);
        return this;
    }


    @Override
    public boolean equals(final Object o) {
        if (this == o) { return true; }
        if (!(o instanceof MultiSortedSetView)) { return false; }
        final MultiSortedSetView<?> n = (MultiSortedSetView<?>) o;
        return sets.equals(n.sets) &&
                (comparator == n.comparator ||
                (comparator != null ? comparator.equals(n.comparator) :
                    n.comparator.equals(comparator)));
    }

    @Override
    public int hashCode() {
        int hash = comparator == null ? 0 : comparator.hashCode();
        return 37 * hash + sets.hashCode();
    }

    @Override
    public String toString() {
        return sets.toString();
    }



    private final static class MultiSortedSetIterator<E>
            implements Iterator<E> {

        private final List<Iterator<E>> iterators;
        private final PriorityQueue<Element<E>> queue;

        private MultiSortedSetIterator(final List<SortedSet<E>> sets,
                final Comparator<? super E> comparator) {
            final int n = sets.size();
            queue = new PriorityQueue<Element<E>>(n,
                    new ElementComparator<E>(comparator));
            iterators = new ArrayList<Iterator<E>>(n);
            for (final SortedSet<E> s: sets) {
                iterators.add(s.iterator());
            }
            prepareQueue();
        }


        @Override
        public E next() {
            final Element<E> e = queue.poll();
            if (e == null) {
                throw new NoSuchElementException();
            }
            if (!insertFromIterator(e.iterator)) {
                iterators.remove(e.iterator);
            }
            return e.element;
        }

        @Override
        public boolean hasNext() {
            return !queue.isEmpty();
        }


        private void prepareQueue() {
            final Iterator<Iterator<E>> iterator = iterators.iterator();
            while (iterator.hasNext()) {
                if (!insertFromIterator(iterator.next())) {
                    iterator.remove();
                }
            }
        }

        private boolean insertFromIterator(final Iterator<E> i) {
            while (i.hasNext()) {
                final Element<E> e = new Element<>(i.next(), i);
                if (!queue.contains(e)) {
                    queue.add(e);
                    return true;
                }
            }
            return false;
        }



        private static final class Element<E> {
            final E element;
            final Iterator<E> iterator;

            Element(final E e, final Iterator<E> i) {
                element = e;
                iterator = i;
            }

            @Override
            public boolean equals(final Object o) {
                if (o == this) { return true; }
                if (!(o instanceof Element)) { return false; }
                final Element<?> e = (Element<?>) o;
                return element.equals(e.element);
            }
        }


        private static final class ElementComparator<E>
                implements Comparator<Element<E>> {
            final Comparator<? super E> comparator;

            ElementComparator(final Comparator<? super E> comp) {
                comparator = comp;
            }

            @Override
            @SuppressWarnings("unchecked")
            public int compare(final Element<E> e1, final Element<E> e2) {
                if (comparator != null) {
                    return comparator.compare(e1.element, e2.element);
                }
                return ((Comparable<? super E>) e1.element)
                        .compareTo(e2.element);
            }
        }
    }
}

這門課程的內部工作很容易掌握。 視圖保留已排序集的列表,即要迭代的集。 它還需要用於比較元素的比較器( null以使用它們的自然順序 )。 您只能向視圖添加(不同的)集。

其余的魔法發生在這個觀點的Iterator中。 此迭代器保留將從next()返回的元素的PriorityQueue以及來自各個集合的迭代器列表。
此隊列在任何時候每組最多只有一個元素,並且它會丟棄重復元素。 迭代器也會丟棄空的和用完的迭代器。 簡而言之,它保證您將遍歷每個元素一次(如在集合中)。

這是一個如何使用這個類的例子。

SortedSet<Integer> s1 = new TreeSet<>();
SortedSet<Integer> s2 = new TreeSet<>();
SortedSet<Integer> s3 = new TreeSet<>();
SortedSet<Integer> s4 = new TreeSet<>();

// ...

MultiSortedSetView<Integer> v =
        new MultiSortedSetView<Integer>()
            .add(s1)
            .add(s2)
            .add(s3)
            .add(s4);

for (final Integer i: v) {
    System.out.println(i);
}

除非是特殊情況,否則我認為這是不可能的,這需要自定義實現。

例如,使用以下兩個比較器:

public class Comparator1 implements Comparator<Long> {

  @Override
  public int compare(Long o1, Long o2) {
    return o1.compareTo(o2);
  }
}

public class Comparator2 implements Comparator<Long> {

  @Override
  public int compare(Long o1, Long o2) { 
    return -o1.compareTo(o2);
  }

}

和以下代碼:

TreeSet<Long> set1 = new TreeSet<Long>(new Comparator1());
TreeSet<Long> set2 = new TreeSet<Long>(new Comparator2());
set1.addAll(Arrays.asList(new Long[] {1L, 3L, 5L}));
set2.addAll(Arrays.asList(new Long[] {2L, 4L, 6L}));
System.out.println(Joiner.on(",").join(set1.descendingIterator())); 
System.out.println(Joiner.on(",").join(set2.descendingIterator())); 

這將導致:

5,3,1
2,4,6

並且對於在給定Iterators的head元素上運行的任何Comparator都沒用。 這使得無法創建這樣的通用解決方案。 只有當所有集合都使用相同的Comparator進行排序時才有可能,但是在任何接受SortedSet對象的實現中都無法保證和確保這一點,給定多個SortedSet實例(例如,任何接受SortedSet<Long>實例的集合都會接受兩個TreeSet對象)。

更正式的方法:

給定y_1,..,y_n是所有有序集合,如果:

  • 這些集合的交集是一個空集
  • 並且存在集合的排序,其中對於每個y_i, y_(i+1)設置y_i[x] <= y_(i+1)[1] ,其中x是y_i有序集的最后一個元素,和<=表示比較功能

那么集合y_1,..,y_n可以作為SortedSet在彼此之后讀取。

現在,如果不滿足以下任何條件:

  • 如果不滿足第一個條件,則不滿足Set的定義,因此在完成深度復制合並並刪除重復元素之前,它不能是Set (請參閱設置javadoc,第一段

集合不包含元素對e1和e2,使得e1.equals(e2)

  • 只能使用完全相同的比較器<=功能來確保第二個條件

第一個條件更重要,因為SortedSet意味着是一個Set ,如果無法滿足Set的定義,那么SortedSet的更強條件肯定無法實現。

存在一種可能存在模仿 SortedSet工作的實現,但它肯定不是SortedSet

com.google.common.collect.Sets來自Guava的#union將會成功。 它返回兩組聯合的不可修改的視圖。 你可以迭代它。 返回的集合將不會被排序。 然后,您可以從返回的集合( new TreeSet()com.google.common.collect.ImmutableSortedSet創建新的有序集。我看不到API來創建給定集合的視圖作為有序集合。

如果您關注的是傳遞給TreeSet #addAll方法的對象的深層復制 ,那么您不應該這樣做。 javadoc並沒有表明它是一個深層拷貝(如果是這樣的話,肯定會這樣說)......並且OpenJDK實現也沒有顯示出來 沒有副本 - 只是對現有對象的附加引用。

由於深層復制不是問題,我認為擔心這一點,除非您已將此識別為特定性能問題,否則屬於過早優化類別。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM