编写包含()的通用集合
我正在用java编写一个skiplist类作为练习。 我编写了一个名为SkipListInternal
的类,其中包含实际的skiplist。 我还创建了一个名为SkipListSet
的包装类,它实现了SortedSet
接口,并包含一个SkipListInternal
的实例。
除此之外, SkipListInternal
包含一个方法E find(E e)
,如果它存在则返回等于e
的元素,否则返回null。
当写boolean contains(Object o)
(inheritance自Collection
via SortedSet
)方法时,我注意到它的参数是一个Object而不是E.我打算做这样的事情,但由于类型擦除:
public class SkipListSet implements SortedSet{ private SkipListInternal skiplist; ... public boolean contains(Object o){ if (!(o instanceof E)) return false; return skiplist.find((E) o) != null; } ... }
既然不能这样做,我该怎么做呢?
严格来说,这样的实施是错误的。
这样做的原因是即使一个对象不是E
,它仍然可以在equals()
调用时返回true
。
假设你有一个这样的课:
public class FakeString { private final String value; public FakeString(String value) { if (value == null) { throw new IllegalArgumentException(); } this.value = value; } public int hashCode() { return value.hashCode(); } public boolean equals(Object o) { return value.equals(o); } }
然后这段代码将打印为true
:
List strings = Arrays.asList("foo", "bar", "baz"); System.out.println(strings.contains(new FakeString("bar")));
并且只是为了澄清:这种行为是有意的,并且是contains()
采用Object
而不是E
。 顺便说一下, remove()
也是如此。
由于contains()
来自java.util.Collection
,我们应该遵循Collection.contains()
契约 。 因为抛出ClassCastException
是一种可选行为,所以在转换失败时在代码中返回false是正确的。 所以我认为你的实施符合合同。
/** * Returns true if this collection contains the specified element. * More formally, returns true if and only if this collection * contains at least one element e such that * (o==null ? e==null : o.equals(e)). * * @param o element whose presence in this collection is to be tested * @return true if this collection contains the specified * element * @throws ClassCastException if the type of the specified element * is incompatible with this collection (optional) * @throws NullPointerException if the specified element is null and this * collection does not permit null elements (optional) */ boolean contains(Object o);
@Joaschim注释对于标准集合是正确的,但是如果你想要一个经过检查的集合,我建议你检查一下可以添加什么,而不是针对无效类型的查找进行优化(除非你想抛出exception)你可以做类似的事情。
public class SkipListSet implements SortedSet { private final Class eClass; private final SkipListInternal skiplist; ... public boolean add(Object o) { if(!eClass.isInstanceOf(o)) // OR if(eClass != o.getClass()) throw new IllegalArgumentException("Type "+o.getClass()+" is not a "+eClass); skiplist.add(o); } public boolean contains(Object o){ // if (!(o instanceof E)) return false; // don't need to optmise for this. return skiplist.find((E) o) != null; } ... }
顺便说一句:Java有一个内置线程安全的ConcurrentSkipListSet
和ConcurrentSkipListMap
你可能会发现阅读源代码很有意思。 ;)
为了使有序集实现起作用,集合的元素必须具有排序。 这可能是“自然的”(即,元素实现Comparable
)或“强加”(通过在集合构造期间使用显式Comparator
)。
所以,首先,你可能宁愿使用为set元素定义的排序(毕竟,你实现了一个SortedSet
!)而不是equals
in contains
来提高效率。 我假设你已经在你的内部SkipListInternal
使用了一个排序 – 你如何维持SortedSet
的Sorted
只给出equals
?
contains
实际上在接口中声明为contains(Object key)
的事实真的很不幸。 我会这样做, TreeMap
实现做了什么(这是TreeSet
的底层容器,来自集合框架的标准SortedSet
):
if (comparator != null) return getEntryUsingComparator(key); if (key == null) throw new NullPointerException(); Comparable super K> k = (Comparable super K>) key;
即,cast,假设使用您的集合的客户端应用程序表现得很好。