简体   繁体   English

遍历多个SortedSet对象的迭代器

[英]Iterator over multiple SortedSet objects

In Java, I have several SortedSet instances. 在Java中,我有几个SortedSet实例。 I would like to iterate over the elements from all these sets. 我想迭代所有这些集合中的元素。 One simple option is to create a new SortedSet , such as TreeSet x , deep-copy the contents of all the individual sets y_1 , ..., y_n into it using x.addAll(y_i) , and then iterate over x . 一个简单的选择是创建一个新的SortedSet ,例如TreeSet x ,使用x.addAll(y_i)将所有单个集合y_1 ,..., y_n的内容深度复制到其中,然后迭代x

But is there a way to avoid deep copy? 但有没有办法避免深层复制? Couldn't I just create a view of type SortedSet which would somehow encapsulate the iterators of all the inner sets, but behave as a single set? 我不能只创建一个SortedSet类型的视图,它会以某种方式封装所有内部集合的迭代器,但表现为单个集合?

I'd prefer an existing, tested solution, rather than writing my own. 我更喜欢现有的,经过测试的解决方案,而不是自己写的。

I'm not aware of any existing solution to accomplish this task, so I took the time to write one for you. 我不知道有任何现有的解决方案来完成这项任务,所以我花时间为你写了一个。 I'm sure there's room for improvement on it, so take it as a guideline and nothing else. 我确信它还有改进的余地,所以把它作为指导而不是别的。

As Sandor points out in his answer , there are some limitations that must be imposed or assumed. 桑德尔在答案中指出,必须施加或假设一些限制。 One such limitation is that every SortedSet must be sorted relative to the same order, otherwise there's no point in comparing their elements without creating a new set (representing the union of every individual set). 一个这样的限制是每个SortedSet必须相对于相同的顺序进行排序,否则在没有创建新集合(表示每个单独集合的并集)的情况下比较它们的元素没有意义。

Here follows my code example which, as you'll notice, is relatively more complex than just creating a new set and adding all elements to it. 下面是我的代码示例,正如您将注意到的,它比创建新集合并向其添加所有元素要复杂得多。

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

The inner workings of this class are simple to grasp. 这门课程的内部工作很容易掌握。 The view keeps a list of sorted sets, the ones you want to iterate over. 视图保留已排序集的列表,即要迭代的集。 It also needs the comparator that will be used to compare elements ( null to use their natural ordering ). 它还需要用于比较元素的比较器( null以使用它们的自然顺序 )。 You can only add (distinct) sets to the view. 您只能向视图添加(不同的)集。

The rest of the magic happens in the Iterator of this view. 其余的魔法发生在这个观点的Iterator中。 This iterator keeps a PriorityQueue of the elements that will be returned from next() and a list of iterators from the individual sets. 此迭代器保留将从next()返回的元素的PriorityQueue以及来自各个集合的迭代器列表。
This queue will have, at all times, at most one element per set, and it discards repeating elements. 此队列在任何时候每组最多只有一个元素,并且它会丢弃重复元素。 The iterator also discards empty and used up iterators. 迭代器也会丢弃空的和用完的迭代器。 In short, it guarantees that you will traverse every element exactly once (as in a set). 简而言之,它保证您将遍历每个元素一次(如在集合中)。

Here's an example on how to use this class. 这是一个如何使用这个类的例子。

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

I do not think that is possible unless it is some special case, which would require custom implementation. 除非是特殊情况,否则我认为这是不可能的,这需要自定义实现。

For example take the following two comparators: 例如,使用以下两个比较器:

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

}

and the following code: 和以下代码:

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

This will result in: 这将导致:

5,3,1
2,4,6

and is useless for any Comparator operating on the head element of the given Iterators . 并且对于在给定Iterators的head元素上运行的任何Comparator都没用。 This makes it impossible to create such a general solution. 这使得无法创建这样的通用解决方案。 It is only possible if all sets are sorted using the same Comparator , however that cannot be guaranteed and ensured by any implementation which accept SortedSet objects, given multiple SortedSet instances (eg anything that would accept SortedSet<Long> instances, would accept both TreeSet objects). 只有当所有集合都使用相同的Comparator进行排序时才有可能,但是在任何接受SortedSet对象的实现中都无法保证和确保这一点,给定多个SortedSet实例(例如,任何接受SortedSet<Long>实例的集合都会接受两个TreeSet对象)。

A little bit more formal approach: 更正式的方法:

Given y_1,..,y_n are all sorted sets, if: 给定y_1,..,y_n是所有有序集合,如果:

  • the intersect of these sets are an empty set 这些集合的交集是一个空集
  • and there is an ordering of the sets where for every y_i, y_(i+1) set it is true that y_i[x] <= y_(i+1)[1] where x is the last element of the y_i sorted set, and <= means a comparative function 并且存在集合的排序,其中对于每个y_i, y_(i+1)设置y_i[x] <= y_(i+1)[1] ,其中x是y_i有序集的最后一个元素,和<=表示比较功能

then the sets y_1,..,y_n can be read after each other as a SortedSet. 那么集合y_1,..,y_n可以作为SortedSet在彼此之后读取。

Now if any of the following conditions are not met: 现在,如果不满足以下任何条件:

  • if the first condition is not met, then the definition of a Set is not fulfilled, so it can not be a Set until a deep copy merge is completed and the duplicated elements are removed (See Set javadoc, first paragraph : 如果不满足第一个条件,则不满足Set的定义,因此在完成深度复制合并并删除重复元素之前,它不能是Set (请参阅设置javadoc,第一段

sets contain no pair of elements e1 and e2 such that e1.equals(e2) 集合不包含元素对e1和e2,使得e1.equals(e2)

  • the second condition can only be ensured using exactly the same comparator <= function 只能使用完全相同的比较器<=功能来确保第二个条件

The first condition is the more important, because being a SortedSet implies being a Set , and if the definition of being a Set cannot be fulfilled, then the stronger conditions of a SortedSet definitely cannot be fulfilled. 第一个条件更重要,因为SortedSet意味着是一个Set ,如果无法满足Set的定义,那么SortedSet的更强条件肯定无法实现。

There is a possibility that an implementation can exists which mimics the working of a SortedSet , but it will definitely not be a SortedSet . 存在一种可能存在模仿 SortedSet工作的实现,但它肯定不是SortedSet

com.google.common.collect.Sets#union from Guava will do the trick. com.google.common.collect.Sets来自Guava的#union将会成功。 It returns an unmodifiable view of the union of two sets. 它返回两组联合的不可修改的视图。 You may iterate over it. 你可以迭代它。 Returned set will not be sorted. 返回的集合将不会被排序。 You may then create new sorted set from returned set ( new TreeSet() or com.google.common.collect.ImmutableSortedSet . I see no API to create view of given set as sorted set. 然后,您可以从返回的集合( new TreeSet()com.google.common.collect.ImmutableSortedSet创建新的有序集。我看不到API来创建给定集合的视图作为有序集合。

If your concern is a deep-copy on the objects passed to the TreeSet#addAll method, you shouldn't be. 如果您关注的是传递给TreeSet #addAll方法的对象的深层复制 ,那么您不应该这样做。 The javadoc does not indicate it's a deep-copy (and it certainly would say so if it was)...and the OpenJDK implementation doesn't show this either . javadoc并没有表明它是一个深层拷贝(如果是这样的话,肯定会这样说)......并且OpenJDK实现也没有显示出来 No copies - simply additional references to the existing object. 没有副本 - 只是对现有对象的附加引用。

Since the deep-copy isn't an issue, I think worrying about this, unless you've identified this as a specific performance problem, falls into the premature optimization category. 由于深层复制不是问题,我认为担心这一点,除非您已将此识别为特定性能问题,否则属于过早优化类别。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM