请教大家,对于HashSet的remove方法,是否找到第一个匹配删除了就返回了,即使Set还有其他匹配的,也不管了?
我刚才发了个帖子,可能描述不清除。。现在重新发下吧import java.util.*;
/**
 * @author super
 *
 */
public class TestDeleteFromHashSet { /**
 * @param args
 */
public static void main(String[] args) {
// TODO Auto-generated method stub Set<SomeElement> set = new HashSet<SomeElement>();
SomeElement so1 = new SomeElement(1);
SomeElement so2 = new SomeElement(2);
set.add(so1);
set.add(so2);
//显示set中的所有元素
System.out.println("起始状态");
Iterator it = set.iterator();
while(it.hasNext()){
System.out.print(it.next());
}
System.out.println();

//现在将so2改成和so1相等
System.out.println("将so2改成和so1相等");
so2.setInt(1);
Iterator it2 = set.iterator();
while(it2.hasNext()){
System.out.print(it2.next());
}
System.out.println();

//现在删除set中与so1相等的元素
System.out.println("现在删除set中与so1相等的元素");
set.remove(so1);
Iterator it3 = set.iterator();
while(it3.hasNext()){
System.out.print(it3.next());
}
System.out.println();
}}class SomeElement{
private int i;
public SomeElement(int i){
this.i = i;
}
public int hashCode(){
return i;
}
public boolean equals(Object o){
 return i== ( (SomeElement)o).i;
}
public String toString(){
return "[SomeElement,i= "+i+"]";
}
public void setInt(int i){
this.i=i;
}
}
结果是
起始状态
[SomeElement,i= 2][SomeElement,i= 1]
将so2改成和so1相等
[SomeElement,i= 1][SomeElement,i= 1]
现在删除set中与so1相等的元素
[SomeElement,i= 1]

我想问,红色部分为什么只删除了一个元素? 可能问题有点幼稚,请大家谅解。。

解决方案 »

  1.   

    我在那个帖子里面已经回了..
    你删除的只是so1这个对象...你的remove操作并不是 删除 对象值为1的所有对象..  所以不能删除so2..最后就只剩下 so2了.
      

  2.   

    自己去看remove的代码不就清楚了。
      

  3.   


    我看了下源代码
    HashSet.java
    [   public boolean remove(Object o) {
    return map.remove(o)==PRESENT;
        }
    /code]由于HashSet 内部用到了HashMap
    HashMap.java
    [code=Java]public V remove(Object key) {
            Entry<K,V> e = removeEntryForKey(key);
            return (e == null ? null : e.value);
        }Entry<K,V> removeEntryForKey(Object key) {
            Object k = maskNull(key);
            int hash = hash(k);
            int i = indexFor(hash, table.length);
            Entry<K,V> prev = table[i];
            Entry<K,V> e = prev;        while (e != null) {
                Entry<K,V> next = e.next;
                if (e.hash == hash && eq(k, e.key)) {
                    modCount++;
                    size--;
                    if (prev == e) 
                        table[i] = next;
                    else
                        prev.next = next;
                    e.recordRemoval(this);
                    return e;
                }
                prev = e;
                e = next;
            }
       
            return e;
        }
    明白了,其实只要找到第一个匹配的就返回了。
      

  4.   


    解释好像不对哦,根据代码,是删除 调用.equals(so1)返回true的对象,也就是这2个中的任何一个都可能被删除,具体看运气了,因为当吧so2中的i改为1 之后,so1,so2已经相等了。
      

  5.   

    http://topic.csdn.net/u/20080917/00/86204fcd-c910-4170-a16d-cdcfcf63d169.html
    原来有人问过,说得很清楚
      

  6.   

    这个问题挺有意思的,查看源代码就可以知道,找到一个删除掉就返回了。但是对于Set来说加入重复的值是会有判断的,因为Set中是不允许有重复值的,但是对LZ上面动态的修改Set中的值相等的,看来是不能直接通过Set中元素类型的hashCode和equals方法删除。
      

  7.   

    看来你还是没搞懂.. equles ..  
    equles 这个只有在 String的时候是被重写了.  判断的是值..而其他的equles 都是 与 "=="相同..so1  与  so2  是不 equles的..
      

  8.   


    Set的重复性的判断仅仅在于添加元素时候,至于具体后来发生了什么变化就是不可知因素了,我总感觉这个是JAVA 设计的一个败笔,,因为对于Set来说,添加删除操作是常有的事情,由于元素之间彼此都不知道,所以一不小心吧某个元素的状态设置为和另外一个元素完全一样也是可能的,这样,一旦调用了remove操作,就有可能带来不可预知的结果,由于Set的无序性,你无法保证remove的元素是一开始的那个元素还是别的元素通过改变状态后成为的这个元素。
      

  9.   

    你看了我提供的链接没?你这里只会删除so1对象,不会删除so2,所以不存在你说的败笔问题
      

  10.   

    最近论坛里发现java的bug的人很多
      

  11.   

     现在sol 和 so2 已经equal了,
    不信你可以加上代码if (so1.equals(so2))
    System.out.println(true);返回结果是true
      

  12.   

    import java.util.*;public class TestDeleteFromHashSet { public static void main(String[] args) { Set<SomeElement> set = new HashSet<SomeElement>();
    SomeElement so1 = new SomeElement(1,1);
    SomeElement so2 = new SomeElement(2,2);
    set.add(so1);
    set.add(so2);
    // 显示set中的所有元素
    System.out.println("起始状态");
    Iterator it = set.iterator();
    while (it.hasNext()) {
    System.out.print(it.next());
    }
    System.out.println(); // 现在将so2改成和so1相等
    System.out.println("将so2改成和so1相等");
    so2.setInt(1);
    Iterator it2 = set.iterator();
    while (it2.hasNext()) {
    System.out.print(it2.next());
    }
    System.out.println(); // 现在删除set中与so1相等的元素
    System.out.println("现在删除set中与so2相等的元素");
    set.remove(so2);
    Iterator it3 = set.iterator();
    while (it3.hasNext()) {
    System.out.print(it3.next());
    }
    System.out.println();
    }}class SomeElement {
    private int i; private int j; public SomeElement(int i, int j) {//这里加了一个参数..
    this.j = j;
    this.i = i;
    } public int hashCode() {
    return i;
    } public boolean equals(Object o) {
    return i == ((SomeElement) o).i;
    } public String toString() {
    return "[SomeElement"+j+",i= " + i + "]";
    } public void setInt(int i) {
    this.i = i;
    }
    }这真的是 Java的BUG,我把楼主的程序稍作改动..看得更清楚一些..起始状态
    [SomeElement1,i= 1][SomeElement2,i= 2]
    将so2改成和so1相等
    [SomeElement1,i= 1][SomeElement2,i= 1]
    现在删除set中与so2相等的元素
    [SomeElement2,i= 1]这是结果.. PF楼主啊..高瞻远瞩啊...害我乱喷了好多.. 汗..
      

  13.   


    总结下,equals 返回true 还是不返回true是根据自己的方法定义,或者就是按照Object中的定义,它更加像是比较(字面上的相等 literal equivalent)
    == 则判断的是2个内存地址是否指向内存的同一个区域至于String,
    (1)如果是在编译期的字符串常量池中创建的,则这些String是共享的, 他们指向相同的内存地址
    String s1= "hello";
    String s2="hello";
    s1==s2 返回的是true
    (2)如果在运行期的堆上创建的,则这些String是指向不同的内存地址
    String s3= new String("hello");
    String s4 = new String("hello");
    s3==s4 返回的是false
    但是,任何String,只要他们字面上的内容一样,就是equals 返回true 了,也就是
    s1,s2,s3,s4 彼此调用equals 都是返回true 的
      

  14.   


    你说的正确,是我刚才理解错了,
    其实,到那个时候,so1 还是固定在 以前由hashCode()=1 散列到的位置记作 POSITION A,so2还是固定在以前有hashCode()=2 散列到的位置,记作POSITION B,所以 remove(so1),,其实就是删除以 sol当前的hashCode为索引的位置上的元素,这里哪怕不是so1,是so100也可以,只要它的hashCode()返回的位置是POSITION A ,就可以。所以,//现在删除set中与so1相等的元素
            System.out.println("现在删除set中与so1相等的元素");
            set.remove(so1);
            Iterator it3 = set.iterator();
            while(it3.hasNext()){
                System.out.print(it3.next());
            }
            System.out.println();此处执行,是将POSITION A 位置处的元素删除了
    所以大胆猜想,如果接下来调用 set.remove(so2) ,应该不会删除结果,因为so2目前的hashCode0 所指示的位置还是POSITION A(因为 so2.hashCode()==1),而此处已经没有任何元素了。。也就是说,在POSTION B 处的元素已经没办法通过hashCode()定位来访问了。
      

  15.   

    相当正确.. 再调用set.remove(so2)并没有再删除东西.
      

  16.   

    接着17楼, 经过测试证明,猜想正确//现在删除set中与so2相等的元素
    System.out.println("现在删除set中与so2相等的元素");
    set.remove(so2);
    Iterator it4 = set.iterator();
    while(it4.hasNext()){
    System.out.print(it4.next());
    }
    System.out.println();
    返回结果是 
    起始状态
    [SomeElement,i= 2][SomeElement,i= 1]
    将so2改成和so1相等
    [SomeElement,i= 1][SomeElement,i= 1]
    现在删除set中与so1相等的元素
    [SomeElement,i= 1]
    现在删除set中与so2相等的元素
    [SomeElement,i= 1]
    红色部分表明猜想正确,利用so2.hashCode()方法指定的位置已经没有元素了,(原本只有那个so1),所以结论如上
      

  17.   

    发现JAVA2 核心技术讲解的知识点太浅显了,凡是这种敏感的,混淆的问题和难点都没涉及到,看核心技术简单是简单,实际遇到题目还是难度很大,就和看女人全只给你看最外面的衣服一样。。等于没看。还好还有几年才毕业。慢慢研究之。
      

  18.   

    hasCode的返回值不同,造成的。在HashSet里面,判断一个对象,是否相同,是依靠hasCode方法的返回值,与equals方法的返回值,共同决定的。hasCode值,是用来确定该对象的存储位置。在HashSet里面查找一个元素的时候,是分两步的。
    1.通过hasCode找到链表位置。
    2.通过equals方法来判断对象是否是要查找的元素。那么,对HashSet的增删操作,都要先进行查找的,因为Set集合里面,是不允许“相同”的元素存在的。由于楼主先后new的两个对象,其hasCode不相同,那么,其存储位置也就不同。
    所以,在进行删除的时候,所访问的链表就不同,因为另一个对象存储在不同的链表里,所以,另一个对象,虽然equals相同,也无法被删除。