为通用集合编写contains()

时间:2011-02-14 12:17:52

标签: java generics collections

我正在用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;
   }

   ...

}

由于不能这样做,我该怎么做?

4 个答案:

答案 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内置线程安全ConcurrentSkipListSetConcurrentSkipListMap您可能会发现阅读源代码很有意思。 ;)

答案 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,假设使用您的集合的客户端应用程序表现得很好。