为什么HashSet<E>在contains()和remove()方法中不限制参数类型为E?

6

add 添加新元素到列表中,因此必须确保它是正确的类型。 - Bhesh Gurung
4个回答

4
区别在于添加必须是类型安全的,以保持集合的完整性,而项目检查/删除可以承受“类型宽容”,而不会危及集合的类型安全性。换句话说,如果您添加了一个错误类型的元素,则集合将变为无效;另一方面,如果您检查存在错误类型的元素,则只会得到一个false。对于remove也是如此:如果传递了不兼容类型的元素,则它不会出现在集合中+,因此删除将不起作用。
+除非通过利用类型擦除的黑客方法放入它。

1
如果您传递了一个不兼容类型的元素,则该元素仍然可能存在于集合中 - 请参阅我的答案。 - npe
1
@npe 是的,好老的类型擦除技巧!我更新了答案以提到这种可能性,尽管答案的核心仍然是相同的:接口的通用和非通用方法之间的区别已经沿着有害/无害的线路进行了划分。 - Sergey Kalinichenko
除非您通过利用类型擦除的黑客方式将其放入,或者它只等于集合中的一个元素...(有关详细信息,请参见我的答案)。 - DaveFar

2
当然可以。了解 类型擦除 或将您的 HashSet<E> 强制转换为非泛型的 HashSet 并向其中添加不是类型 E 的对象。
请查看以下代码:
Integer testInt = new Integer(3);

// First, create a generic set of strings
HashSet<String> set = new HashSet<String>();
set.add("abc");

// Then make it non-generic and add an integer to it
((HashSet) set).add(testInt);

// Now your set-of-strings contains an integer!
System.out.println(set); // prints: [abc, 3]

// Remove the integer
set.remove(testInt);
System.out.println(set); // prints: [abc]

这种奇怪的情况是由于泛型类型的信息在运行时被擦除,因此你的集合变成了一个简单的对象集合。

HashSet<?> 你能添加元素吗? - Bhesh Gurung
不可以的,我会纠正答案。 - npe
当您使用“?”通配符作为泛型参数时,您告诉编译器您有一个实例,并且泛型参数是某个值,但您不知道它是什么。因此,在该对象上调用任何使用该泛型参数的方法本质上是不安全的。以HashSet为例,您可以构造HashSet<Integer>,但将其分配给HashSet<?>,编译器如何知道参数的真实类型?答案是:它无法知道。即使通配符是有界的(即HashSet<? extends Number>),情况也是如此。 - Matt

2
containsremove的参数不能仅限于E,因为您应该能够给它们相等的对象,这是非常有用的。更精确地说,HashSet.remove的API如下所示:

... 更正式地说,如果此集合包含这样的元素,则删除元素e,使得(o==null ? e==null : o.equals(e))。

Object.equalsObject作为参数,这也很有用,以便使不同类型之间具有相等性。
因此,为了使containsremove的功能更加通用(而不仅仅是对象标识),它们必须采用Object作为参数。
例子:
    HashSet<ArrayList<String>> set = new HashSet<ArrayList<String>>();
    ArrayList<String> list = new ArrayList<String>();
    list.add("foo");
    LinkedList<String> equalList = new LinkedList<String>();
    equalList.add("foo");
    set.add(list);

    System.out.println(list.equals(equalList)); // prints: true
    System.out.println(set.contains(equalList)); // prints: true

    System.out.println(set); // prints: [[foo]]
    set.remove(equalList);
    System.out.println(set); // prints: [[]]

0

对于这两个方法,你并没有向集合中添加任何内容,因此类型参数无需被限制。如果类型不匹配,这些方法可以直接返回 false。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接