zoukankan      html  css  js  c++  java
  • FCL(4):: ArrayList & List (2)

     ArrayList & List (1) 里讲到:
    ArrayList底层用的数据对象就是一组Object的Array,提供了一个List功能的接口.
    ArrayList.InsertRange(int index, ICollection c) 和 List.InsertRange(int index, IEnumerable collection) 方法参数类型的改变和实现方式.

    这一篇先讲讲ArrayList的同步类.
    以前在做多线程的时候,为了保持线程安全,一般我是这样写的:

    Lock


    后来知道原来是提供了一个同步的ArrayList的.ArrayList提供了一个static方法 public static ArrayList Synchronized(ArrayList list),返回来的是一个同步的ArrayList.往此List里加数据就不用担心多个线程同时操作的问题了.呵呵,可以少写很多重复哦,免得对ArrayList操作时总要lock一下.

    不过这个接口感觉有点别扭,需要传入一个ArrayList类型参数.于是乎得这样:
    ArrayList syncedList = new ArrayList();
    syncedList = ArrayList.Synchronized(syncedList);
    要是你想偷懒 ArrayList syncedList = ArrayList.Synchronized(null); 那么我只能很抱歉的告诉你,没戏!等待你的只有ArgumentNullException异常.
    对于实现Synchronized或者unSynchronized ArrayList实现,要是我的话,最开始能够想到的就是提供一个Synchronized属性,,ture or false.每次对集合操作的时候判断一下Synchronized属性,如何true的话我就lock一下.这样的想法对于我来说是非常直接,顺其自然的事.然而事实上ArrayLsit根本就没有提供一个这样的属性供我选择.通过提供static的ArrayList.Synchronized方法也能猜测它不是这么干的.
    我想这一点如果不反射一下是不会知道的.原来里面定义了个SyncArrayList类,它继承自ArrayList,而且是private.用户根本看不到.SyncArrayList类重写了ArrayList所有会对集合状态做更改的操作.但它其实只是简单地调用一下ArrayList的操作,只不过在操作之前lock一下. 这就是为什么ArrayList.Synchronized()方法需要传入一个ArrayList的参数的引用了.它是对普通ArrayList的封装.

    SyncArrayList.Add(object)


    这样的实现好像是体现了什么模式,一时想不起来了.ArrayList只需要关心涉及集合的事就可以了,做好本份的事,不需要考虑同步不同步问题.然后再提供一个SyncArrayList封装类为你解决同步的问题,而其内部实现就是简单地调用ArrayList的方法.也有点 单一责任 的味道.

    再看上面的方法: lock (this._root)语句,lock了什么呢,原来是ArrayList.SyncRoot属性,这个干吗用的看名字就能猜到了.唉,想想以前的代码多丑陋,还要new一个object的locker对象,直接:
    lock(myList.SyncRoot)
     myList.Add(object);
    不就好了.所以说扩大知识面,好处大大地有.再看看List<T>,发现已经没有Synchronized()方法了,自己想保持同步就只能自己lock一下SyncRoot.而且很多方法已经不是virtual了.这样更改的理由是什么?自己一时真想不到.

    接下来说说搞笑的一个方法. ArrayList.GetRange(int index, int count).印象中自己好像没有调用过这个方法. 以前想要得到集合的一部分数据时就会自己遍历,拷贝.无知惹的祸啊.
    为什么说这个方法好笑呢,因为它返回来的是一个Range对象,继承自ArrayList.实现手法跟刚才的SyncArrayList类差不多.那到底为什么我说它好笑呢.看看下面代码先:

    ArrayList.GetRange()测试

    执行到最后一行会抛出一个InvalidOperationException异常.为什么呢,好像我们上面代码的调用没什么不妥啊.原因看看Range类是如何实现就知道了.
    首先Range类初始化的时候需要传入一个原先的ArrayList实例.Range类所有涉及到对集合状态改变的操作都会额外做点工作,然后调用原来的ArrayList的相应方法. 它做的额外操作包括check一下本身的version与原来的ArrayList的version是否一致,调用完原来ArrayList的方法后,同时将两个对象version + 1.

    Range.Add(object)

    在我给出的测试代码片段中,当执行完第6行copyList.Add(4)后,list和copyList的版本会增1. 执行完第7行list.Add(5)后list的版本加1了,但copyList不会加,于是在调用第8行copyList.Add(6)时检查到两者版本不一致就抛出了刚才的异常.
     Range类由于完全依赖于传入的ArrayList,自己只是对其做了访问范围的限制.实现方式相当于一个"浅拷贝".而为了避免原来ArrayList数据源发生改变影响自己数据的访问,于是每次操作时总要判断两者的版本是不是一致.如果不一致,又怕数据发生错误只好抛出一个InvalidOperationException异常. 而用户在看到这个异常的时候会觉得很莫名,因为这个异常的隐患是在第7行list.Add(5)语句中埋下的,可能离获得异常的位置很远.
     这样的实现明显是不合理的,框架设计者对于ArrayList.GetRange()函数应该提供的功能定位是有问题的.
     GetRange()操作应该是返回一个ArrayList,此返回的List除了数据是原来ArrayLsit的一部分外,不应该跟原来的ArrayList有任何其它的关系.这种方式相当于"深拷贝", 我仅仅是从你那里拿了一组数据而已,而不应该是对原来ArrayList的封装,对访问范围做下限制就可以了.

    所幸的是在List<T>.GetRange()方面里可以看到已经对这个做了修改.我觉得这才是我们想要的GetRange方法.

    List.GetRange();


     

  • 相关阅读:
    淘宝放大镜
    碰撞的小球
    模拟微博发布
    CSS兼容IE Firefox问题与解决方法
    太阳八大行星运行轨迹
    Canvas标签基础
    offsetTop、clientTop、scrollTop、offsetTop
    js绘制圆形时钟
    js时钟
    js五星好评2
  • 原文地址:https://www.cnblogs.com/anders06/p/739064.html
Copyright © 2011-2022 走看看