为了减少内存消耗,我正在重写一个SortedSet<Integer>
的类。在80%的情况下,此集合仅包含单个元素。所以我认为在这些情况下我可以使用SingeltonSet
,在其他情况下可以使用普通TreeSet
。现在我注意到Collections.singleton()
返回的SingletonSet
未实现SortedSet
。这个缺点有什么理由吗?我会说,单个元素总是可以被认为是排序的。我是否必须编写自己的SingletonSet
实现?
答案 0 :(得分:6)
这是一个有趣的观点,它似乎说明了集合API中的一个小漏洞。
事实是Collections.singleton()
被指定为返回Set
而不是SortedSet
,实际上实现不支持该接口。我不认为Collections.singleton()
更改其行为并返回SortedSet
的实例会有所帮助。这将鼓励实现执行instanceof
检查和向下转换。 (和相应的Map方法和接口类似。)
对于此用例,这是一个小小的安慰,但在Java SE 8中引入了新方法Collections.emptyNavigableMap
和emptyNavigableSet
。这对于需要空的可导航集合的用例很有用,但如果您真的想要使用单个元素或映射进行导航,那么您就不走运了。有一个增强请求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元素/类适用于我的用例。