为什么SingletonSet不实现SortedSet

时间:2017-06-06 20:14:51

标签: java collections

为了减少内存消耗,我正在重写一个SortedSet<Integer>的类。在80%的情况下,此集合仅包含单个元素。所以我认为在这些情况下我可以使用SingeltonSet,在其他情况下可以使用普通TreeSet。现在我注意到Collections.singleton()返回的SingletonSet未实现SortedSet。这个缺点有什么理由吗?我会说,单个元素总是可以被认为是排序的。我是否必须编写自己的SingletonSet实现?

4 个答案:

答案 0 :(得分:6)

这是一个有趣的观点,它似乎说明了集合API中的一个小漏洞。

事实是Collections.singleton()被指定为返回Set而不是SortedSet,实际上实现不支持该接口。我不认为Collections.singleton()更改其行为并返回SortedSet的实例会有所帮助。这将鼓励实现执行instanceof检查和向下转换。 (和相应的Map方法和接口类似。)

对于此用例,这是一个小小的安慰,但在Java SE 8中引入了新方法Collections.emptyNavigableMapemptyNavigableSet。这对于需要空的可导航集合的用例很有用,但如果您真的想要使用单个元素或映射进行导航,那么您就不走运了。有一个增强请求JDK-6201174,涵盖了类似的区域;我已经更新并重新围绕为单例导航集和地图提供API。

但是等等!正如您pointed out一样,还有一些额外的状态与排序/可导航的集合(即比较器)一起运行。 (或者在没有比较器的情况下,隐含地提供自然排序的比较器。)任何新的单例API也可以提供。这指出了我上面提到的空*方法没有谈论比较器这一事实。这似乎是另一个错误:JDK-8181754

不幸的是,除了扣下并实现一个单元素,可能是不可变的SortedSet或NavigableSet之外,我没有一个非常好的解决方法。你可以从Collections.UnmodifiableNavigableSet开始。这有点帮助,但并不多。事实上,空的可导航集是其中一个空的TreeSet!这是非常无益的,因为您要避免TreeSet个实例。

我可能从AbstractSet开始,然后从SortedSet添加最小的方法集。 NavigableSet中的方法相当少,所以如果你不需要所有的花里胡哨,那么坚持使用SortedSet是一个较小的任务。

答案 1 :(得分:4)

SortedSet接口定义了需要Comparator设置元素的方法。因此,由SortedSet维护的元素必须具有可比性。如果Collections.singleton()返回的单例实现SortedSet,则Collections.singleton()只能接受Comparables(这不是我们想要的)。

答案 2 :(得分:0)

我认为Stuart Marks的答案很好地解释了为什么没有单身人士可以进行排序。 但是,实现SingletonSortedSet并不困难。

那又怎么样:

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;

public class SingletonSortedSet<E>
        extends AbstractSet<E>
        implements SortedSet<E> {
    private final E element;
    private final Comparator<? super E> comparator;

    private SingletonSortedSet(E e, Comparator<? super E> comparator) {
        element = e;
        this.comparator = comparator;
    }

    public static <E> SortedSet<E> singletonSortedSet(E e, Comparator<? super E> comparator) {
        return new SingletonSortedSet<>(e, comparator);
    }

    public Iterator<E> iterator() {
        return singletonIterator(element);
    }

    public int size() {
        return 1;
    }

    public boolean contains(Object o) {
        return Objects.equals(o, element);
    }

    // Override default methods for Collection
    @Override
    public void forEach(Consumer<? super E> action) {
        action.accept(element);
    }

    @Override
    public Spliterator<E> spliterator() {
        return singletonSpliterator(element, comparator);
    }

    @Override
    public boolean removeIf(Predicate<? super E> filter) {
        throw new UnsupportedOperationException();
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(element);
    }

    @Override
    public Comparator<? super E> comparator() {
        return comparator;
    }

    @Override
    public SortedSet<E> subSet(E fromElement, E toElement) {
        if(contains(fromElement) || contains(toElement)) {
            return this;
        } else {
            return Collections.emptySortedSet();
        }
    }

    @Override
    public SortedSet<E> headSet(E toElement) {
        if(contains(toElement)) {
            return this;
        } else {
            return Collections.emptySortedSet();
        }
    }

    @Override
    public SortedSet<E> tailSet(E fromElement) {
        if(contains(fromElement)) {
            return this;
        } else {
            return Collections.emptySortedSet();
        }
    }

    @Override
    public E first() {
        return element;
    }

    @Override
    public E last() {
        return element;
    }

    private static <E> Iterator<E> singletonIterator(final E e) {
        return new Iterator<E>() {
            private boolean hasNext = true;
            public boolean hasNext() {
                return hasNext;
            }
            public E next() {
                if (hasNext) {
                    hasNext = false;
                    return e;
                }
                throw new NoSuchElementException();
            }
            public void remove() {
                throw new UnsupportedOperationException();
            }
            @Override
            public void forEachRemaining(Consumer<? super E> action) {
                Objects.requireNonNull(action);
                if (hasNext) {
                    hasNext = false;
                    action.accept(e);
                }
            }
        };
    }

    private static <T> Spliterator<T> singletonSpliterator(final T element, Comparator<? super T> comparator) {
        return new Spliterator<T>() {
            long est = 1;

            @Override
            public Spliterator<T> trySplit() {
                return null;
            }

            @Override
            public boolean tryAdvance(Consumer<? super T> consumer) {
                Objects.requireNonNull(consumer);
                if (est > 0) {
                    est--;
                    consumer.accept(element);
                    return true;
                }
                return false;
            }

            @Override
            public void forEachRemaining(Consumer<? super T> consumer) {
                tryAdvance(consumer);
            }

            @Override
            public long estimateSize() {
                return est;
            }

            @Override
            public int characteristics() {
                int value = (element != null) ? Spliterator.NONNULL : 0;

                return value | Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.IMMUTABLE |
                        Spliterator.DISTINCT | Spliterator.SORTED;
            }

            @Override
            public Comparator<? super T> getComparator() {
                return comparator;
            }
        };
    }
}

然后您可以使用SingletonSortedSet.singletonSortedSet(...)。

答案 3 :(得分:0)

不错的展示。嗯!我继续创建自己的文件并将其发布在https://github.com/hammerspaceinc/hs-java-utils。它要求SingletonSortedSet实现的Comparable元素/类适用于我的用例。