今天中午在北京博客园俱乐部中聊天时子秋同学的提问:

     提问, 基础知识: 一个自定义类型t1, 一个集合list, list.add( t1 )后,  更改t1的属性name的值, 再list.add( t1 ). list中的两个值是否相等?此时list中是一个值还是两个值?

脑袋兄马上给出了回答:

    1.相等,2. 两个值

且不说脑袋兄的答案是否正确,先摘录对话内容如下:

子秋 说:
 提问, 基础知识: 一个自定义类型t1, 一个集合list, list.add( t1 )后,  更改t1的属性name的值, 再list.add( t1 ). list中的两个值是否相等?
子秋 说:
此时list中是一个值还是两个值.

Fan Shi(装配脑袋) 说:

1.相等,2. 两个值

子秋 说:
脑袋基础真好.

陈红卫 说:
这个要看你ADD了两次还是一次。
。。。。。[此后省略若干千字聊天记录]

我搬了个小板凳,坐着观看各位兄弟就此问题在争论。此时我并不太十分确定脑袋的答案是正确的,因为没太注意List的内部实现,所以也不敢乱说话。虽然我前段时间研究过Java内部的HashMap的源码,但是也不敢就此断定一定是相等或者一定是不相等。后来脑袋兄弟给出了用VB10写的程序如下:

Dim a = New With {.Name = "Harry" }
Dim l = { a }.ToList()
a.Name = "Potter"
l.Add( a)

……

  当时我并没有.NET开发环境,所以也就没发表意见。后来扯着扯着又扯到HashTable中去了,最后得到结论是“两个值相当, 都是修改后的t1属性”,聊天暂时告一段落。

   任何没有理论作依据,或者没有实践就得出结论者是对他人也是对自己的不负责任。鉴于此,回来之后简单地看了List和HashTable的内部实现,发现其内部容器及其Add方法的逻辑有较大的区别。

1.List

      List<T>的内部容器为类型T的数组,如下

private T[] _items;

 

Add方法如下: 

 public void Add(T item) {
            
if (_size == _items.Length) EnsureCapacity(_size + 1);
            _items[_size
++= item; 
            _version
++;
        } 

     由此看来,无论你Add进的对象的某一字段值是否发生了变化,它内部数组均无条件地保存进去你Add进去的对象T,并且维护的均是指向该对象的同一个引用,同时在获取其Count时返回的其实是你调用Add方法的次数,即其内部的_size字段:

   public int Count {
            
get { return _size; } 
        } 

 

由此便不难回答开头子秋的那个问题的答案为什么会是“两个值,相等”了。

2. HashTable

   当时聊天时我提出了这样的一个问题“如果将一个重写了HashCode和Equals方法的对象作为key放到HashTable中会出现什么现象?”之所以提出这样的问题,是因为前段时间生产机上出现了一个莫名其妙的问题,害得我花了将近一天的时间才找到原来是公司的前辈们写的持久层框架中的Schema用了不正确的重写equals方法导致的,当时郁闷到差点吐血。

  针对我提出的问题,脑袋兄给出的解答如下:如果对象的hashcode会变,它就不适合放到hashtable里。

  先来看HashTable的内部容器,其实是一个结构体,如下:

Code

 

     其中has_coll的作用为维护key的hashCode,在调用Add方法时,会先判断在该结构体类型bucket[]的数组buckets中是否已经存在该key的hashCode的元素,如果不存在则key和value同时放进该结构体数组,如果存在则抛出“已添加此项”的异常,即不允许添加重复健值的项。

   如果按照如下方式重写了hashCode和equals方法:

Code

那么在将该对象的Name改变一个值之后再向HashTable中作为键添加,不会抛异常。

      根据前面的HashTable内部容器Struct可很容易地看出来,HashTable判断某一对象是否已经存在就是通过key的HashCode是否已经存在于它内部维护的这个struct类型的数组中,如果根据我上面说那种方式去重写hashCode和Equals方法,那么在该对象的Name属性变化时就得到的HashCode值也不一样,此时放到HashTable中时就不会认为是一个已经存在的对象,但是改变值之前的放进到HashTable中的对象无法通过key再索引到了,也就是它丢失了(不过如果你将该对象的Name值再改变回原来的值就又可以找到了……)

      对于HashCode有可能改变的对象,是不适合放到HashTable中的。

      经常看看基础,其实对于我们提高很有益。

 

以上如有不对的地方,欢迎拍砖指正。

欢迎更多朋友加入北京博客园俱乐部

 

 

posted on 2009-05-25 22:18  是谁啊?  阅读(1899)  评论(10编辑  收藏  举报