为通用集合编写 contains()

发布于 2024-10-17 10:52:36 字数 833 浏览 2 评论 0原文

我正在用java编写一个skiplist类作为练习。我编写了一个名为 SkipListInternal 的类,其中包含实际的跳过列表。我还创建了一个名为 SkipListSet 的包装类,它实现 SortedSet 接口并包含 SkipListInternal 的实例>。

除此之外,SkipListInternal 包含一个方法 E find(E e),该方法返回等于 e 的元素(如果存在),否则返回 null。

在编写 boolean contains(Object o) (通过 SortedSetCollection 继承)方法时,我注意到它的参数是一个对象而不是 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;
   }

   ...

}

既然不能这样做,我应该怎么做?

I'm writing a skiplist class in java as an excercise. I've written a class called SkipListInternal<E> which contains the actual skiplist. I've also made a wrapper class called SkipListSet<E> which implement the SortedSet<E> interface and contains an instance of SkipListInternal<E>.

Among other things, SkipListInternal<E> contains a method E find(E e) which returns the element equal to e if it is present, and otherwise returns null.

When writing the boolean contains(Object o) (inherited from Collection<E> via SortedSet<E>) method I noticed that its argument is an Object and not an E. I intended to do something like this, but is not possible due to type erasure:

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;
   }

   ...

}

Since it can't be done this way, how should I do it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

蛮可爱 2024-10-24 10:52:36

严格来说,这样的实现是错误的。

这样做的原因是,即使对象不是 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<String> strings = Arrays.asList("foo", "bar", "baz");
System.out.println(strings.contains(new FakeString("bar")));

并且只是为了澄清:此行为是预期,并且是原因contains() 采用 Object 而不是 E。顺便说一句,remove() 也是如此。

Strictly speaking such an implementation would be wrong.

The reason for this is that even if an object is not of type E, it could still return true on an equals() call.

Assume for a second, that you've got a class like this:

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);
  }
}

Then this code would print true:

List<String> strings = Arrays.asList("foo", "bar", "baz");
System.out.println(strings.contains(new FakeString("bar")));

And just to clarify: this behaviour is intended and is the reason why contains() takes an Object instead of E. The same is true for remove(), by the way.

纵性 2024-10-24 10:52:36

由于 contains() 来自 java.util.Collection,我们应该遵循 Collection.contains() contract< /强>。由于抛出 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);

Since the contains() is from java.util.Collection, We are supposed to follow the Collection.contains() contract. Because throwing ClassCastException is an optional behavior, it's correct to return false in your code when cast fails. So I think your implementation comply with the contract.

        /**
         * 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);
饭团 2024-10-24 10:52:36

@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 您可能会发现阅读其源代码很有趣。 ;)

@Joaschim comment is correct for standard collections, however if you want a checked collection I suggest you check what can be added, and not optimise for the lookups of invalid types (unless you want to throw an Exception) You can do something like.

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;
   }

   ...

}

BTW: Java has a builtin thread safe ConcurrentSkipListSet and ConcurrentSkipListMap You might find it interesting to read the source for this. ;)

魂ガ小子 2024-10-24 10:52:36

为了使排序集实现正常工作,集合中的元素必须具有顺序。这可以是“自然的”(即元素实现Comparable)或“强加的”(通过在集合构造期间使用显式的Comparator)。

因此,第一件事是,您可能更愿意使用为 set 元素定义的顺序(毕竟,您正在实现 SortedSet!)而不是 equals contains 以提高效率。我假设您已经在内部 SkipListInternal 中使用排序 - 您如何在给定的 SortedSet 中维护 Sorted只有等于吗?

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;

即,强制转换,假设使用您的集合的客户端应用程序行为正常。

In order for a sorted set implementation to work, the elements of the set have to have an ordering. This may either be "natural" (i.e., the elements implement Comparable) or "imposed" (by using an explicit Comparator during set construction).

So, the first thing is, that you'd probably rather use the ordering defined for the set elements (after all, you are implementing a SortedSet!) instead of equals in contains for efficiency. I assume, that you are already using an ordering in your internal SkipListInternal<E> -- how'd you maintain the Sorted in SortedSet given only equals?

The fact that contains is actually declared as contains(Object key) in the interface is really unfortunate. I'd do, what the TreeMap implementation does (which is the underlying container for TreeSet, the standard SortedSet from the collections framework):

if (comparator != null)
    return getEntryUsingComparator(key);
if (key == null)
    throw new NullPointerException();
Comparable<? super K> k = (Comparable<? super K>) key;

i.e., cast, assuming the client application using your collection behaves sanely.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文