遍历多个SortedSet对象的迭代器

时间:2014-10-22 12:23:03

标签: java iterator set sortedset

在Java中,我有几个SortedSet个实例。我想迭代所有这些集合中的元素。一个简单的选项是创建新的SortedSet,例如TreeSet x,将所有单个集y_1,...,y_n的内容深层复制到其中x.addAll(y_i),然后迭代x

但有没有办法避免深层复制?我不能只创建一个SortedSet类型的视图,它会以某种方式封装所有内部集合的迭代器,但表现为单个集合吗?

4 个答案:

答案 0 :(得分:2)

  

我更喜欢现有的经过测试的解决方案,而不是自己编写。

我不知道有任何现有的解决方案来完成这项任务,所以我花时间为你写了一个。我确信它还有改进的余地,所以把它作为指导而不是别的。

桑德尔在his answer中指出,必须施加或假设一些限制。一个这样的限制是每个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中。此迭代器保留将从PriorityQueue返回的next()个元素以及各个集合中的迭代器列表。
此队列在任何时候每组最多只有一个元素,并且它会丢弃重复元素。迭代器也会丢弃空的和用完的迭代器。简而言之,它保证您将遍历每个元素一次(如在集合中)。

以下是如何使用此类的示例。

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

答案 1 :(得分:1)

我不认为这是可能的,除非是某些特殊情况,这需要自定义实现。

例如,请使用以下两个比较器:

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

对于对给定Comparator的head元素进行操作的任何Iterators都没用。 这使得无法创建这样的通用解决方案。只有当所有集合都使用相同的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有序集的最后一个元素,并且{{1} }意味着比较功能

然后集合<=可以作为SortedSet在彼此之后读取。

现在,如果不满足以下任何条件:

  • 如果未满足第一个条件,则不会满足y_1,..,y_n的定义,因此在完成深层复制合并并删除重复元素之前,它不能是Set(见Set javadoc, first paragraph
  

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

  • 只能使用完全相同的比较器Set功能
  • 来确保第二个条件

第一个条件更重要,因为<=意味着是SortedSet,如果无法满足Set的定义,那么a的条件就越强Set肯定无法实现。

有可能存在模仿 SortedSet工作的实现,但绝对不会是SortedSet

答案 2 :(得分:0)

com.google.common.collect.Sets#union from Guava会做到这一点。它返回两组联合的不可修改的视图。你可以迭代它。返回的集合将不会被排序。然后,您可以从返回的集合(new TreeSet()com.google.common.collect.ImmutableSortedSet创建新的有序集。我看不到API来创建给定集合的视图作为有序集。

答案 3 :(得分:0)

如果你关心的是传递给TreeSet #addAll方法的对象的深层复制,你就不应该这样做。 javadoc并没有表明它是一个深层拷贝(如果是的话,肯定会这么说)......和OpenJDK implementation doesn't show this either。没有副本 - 只是对现有对象的附加引用。

由于深层复制不是问题,我认为担心这一点,除非您已将此识别为特定性能问题,否则属于过早优化类别。