当前位置: 代码迷 >> J2SE >> 大伙来评论下ArrayList中的subList的设计
  详细解决方案

大伙来评论下ArrayList中的subList的设计

热度:45   发布时间:2016-04-23 20:41:51.0
大家来评论下ArrayList中的subList的设计
如下代码

List l = new ArrayList();
l.add("a");
l.add("b");
l.add("c");
List s = l.subList(0, 2);
l.remove(0);
l.add("d");
System.out.println(s.size());

将会抛出java.util.ConcurrentModificationException
看了下源码,发现subList只是复制了引用,并且新生产的subList也是一个ArrayList实例
这样子的话,如果一个ArrayList调用了subList之后,岂不是代表不能再对两个中的任一个list进行修改(即调用add或remove方法)了,原因如下:
假设一个ArrayList A 调用了subList之后生成一个B,由于A,B都是ArrayList实例,并且A和B都是“快速失败”的,而且A和B共用了一块部分,如果此时修改A,B将会抛ConcurrentModificationException,同理修改B,A也会抛异常。
不知道我的理解对不对,如果没理解错的话,这种设计岂不是很糟糕。以后岂不是调用subList要很小心了。
------解决方案--------------------
该回复于2012-01-29 09:48:10被版主删除

------解决方案--------------------
这是API的解释:  
   * Returns a view of the portion of this list between the specified
     * <tt>fromIndex</tt>, inclusive, and <tt>toIndex</tt>, exclusive.  (If
     * <tt>fromIndex</tt> and <tt>toIndex</tt> are equal, the returned list is
     * empty.)  The returned list is backed by this list, so non-structural
     * changes in the returned list are reflected in this list, and vice-versa.
     * The returned list supports all of the optional list operations      supported
     * by this list.<p>

测试:

 List l = new ArrayList();
   l.add("a");
   l.add("b");
    l.add("c");
    List s = l.subList(0, 2);
    s.add("d");
    System.out.println(l.size());
    System.out.println(s.size());





------解决方案--------------------
API的中文说明:

返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。(如果 fromIndex 和 toIndex 相等,则返回的列表为空)。返回的列表由此列表支持,因此返回列表中的非结构性更改将反映在此列表中,反之亦然。返回的列表支持此列表支持的所有可选列表操作。
此方法省去了显式范围操作(此操作通常针对数组存在)。通过传递 subList 视图而非整个列表,期望列表的任何操作可用作范围操作。例如,下面的语句从列表中移除了元素的范围: 

            list.subList(from, to).clear();
 可以对 indexOf 和 lastIndexOf 构造类似的语句,而且 Collections 类中的所有算法都可以应用于 subList。
如果支持列表(即此列表)通过任何其他方式(而不是通过返回的列表)从结构上修改,则此方法返回的列表语义将变为未定义(从结构上修改是指更改列表的大小,或者以其他方式打乱列表,使正在进行的迭代产生错误的结果)。 

所以可以像2楼那样用s进行添加或者删除操作。
------解决方案--------------------
引用:
引用:

The returned list is backed by this list, so non-structural
changes in the returned list are reflected in this list, and vice-versa.

如果把你的代码s.add("d");改成l.add("d");就会抛Concur……

嗯,是,因为subList()返回的是对原List的“视图”,你所说的引用。
其实这是实现上的要求,不是设计上的问题,C++的迭代也会有这种问题。因为对集合的修改可能会造成数组所在内存地址的变化。你也可以这样理解:如果两个list都允许修改且做了不同的修改,实际上仅有一个list该怎么应对这种情况呢?
------解决方案--------------------
既然你是为了高效便捷而去引用subList,那必然在灵活性上要有所牺牲。

如果你强调灵活性,那不如直接复制出来,就可以随便玩了,但必然效率和空间要做微量牺牲。

说到底都是权衡选择的问题。
  相关解决方案