我正在用java编写一个skiplist类作为练习。我写了一个名为SkipListInternal<E>
的类,其中包含实际的跳转列表。我还创建了一个名为SkipListSet<E>
的包装类,它实现了SortedSet<E>
接口并包含SkipListInternal<E>
的实例。
除其他外,SkipListInternal<E>
包含一个方法E find(E e)
,如果它存在,则返回等于e
的元素,否则返回null。
在编写boolean contains(Object o)
(继承自Collection<E>
via SortedSet<E>
)方法时,我注意到它的参数是一个Object而不是E.我打算做这样的事情,但是由于类型擦除而无法实现:
public class SkipListSet<E> implements SortedSet<E>{
private SkipListInternal<E> skiplist;
...
public boolean contains(Object o){
if (!(o instanceof E)) return false;
return skiplist.find((E) o) != null;
}
...
}
由于不能这样做,我该怎么做?
答案 0 :(得分:8)
严格来说,这样的实施是错误的。
这样做的原因是,即使某个对象不属于E
类型,它仍然可以在true
来电时返回equals()
。
假设你有一个这样的课:
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<String> strings = Arrays.asList("foo", "bar", "baz");
System.out.println(strings.contains(new FakeString("bar")));
只是为了澄清:此行为意图,这是contains()
采用Object
代替E
的原因。顺便说一句,remove()
也是如此。
答案 1 :(得分:3)
由于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 <tt>true</tt> 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);
答案 2 :(得分:1)
@Joaschim注释对于标准集合是正确的,但是如果你想要一个已检查的集合,我建议你检查一下可以添加什么,而不是针对无效类型的查找进行优化(除非你想抛出异常)你可以做点什么等。
public class SkipListSet<E> implements SortedSet<E>{
private final Class<E> eClass;
private final SkipListInternal<E> 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
您可能会发现阅读源代码很有意思。 ;)
答案 3 :(得分:0)
为了使有序集实现起作用,集合的元素必须具有排序。这可能是“自然的”(即元素实现Comparable
)或“强加”(通过在集合构造期间使用显式Comparator
)。
所以,首先,你可能宁愿使用为set元素定义的排序(毕竟,你在SortedSet
中实现了equals
而不是contains
为了效率。我假设您已在内部使用排序SkipListInternal<E>
- 您如何仅在Sorted
SortedSet
中维护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,假设使用您的集合的客户端应用程序表现得很好。