【问题标题】:How works ArrayList's removeRange method?ArrayList 的 removeRange 方法如何工作?
【发布时间】:2019-06-21 16:21:42
【问题描述】:

ArrayList 的 removeRange 工作异常。检查下面的示例代码,我给输入反向。我还以为列表被反向删除了。

例如:3 比 1 表示移除三个元素(第 3、第 2 和第 1)。

但是,输出完全让我感到困惑。想知道它是如何工作的?

从 JavaDoc 中,我找到了以下语句。如果toIndex

IndexOutOfBoundsException - 如果 fromIndex 或 toIndex 超出范围 (fromIndex = size() || toIndex > size() || toIndex )

import java.util.*;
public class TestRemoveRange extends ArrayList {

  public static void main(String arg[]){

    TestRemoveRange list = new TestRemoveRange();
    list.add("a");
    list.add("b");
    list.add("c");
    list.add("d");
    list.add("e");

    System.out.println("before remove : " + list);

    list.removeRange(3, 1);

    System.out.println("after remove (3, 1) : " + list); //[a, b, c, b, c, d, e]


  }     
}

【问题讨论】:

  • 无法复制。我得到Exception in thread "main" java.lang.IndexOutOfBoundsException: From Index: 3 > To Index: 1 at java.base/java.util.ArrayList.removeRange(ArrayList.java:664) at TestRemoveRange.main(TestRemoveRange.java:15)
  • 我的java版本是1.8.0_121。我仍然面临这个问题。

标签: java arraylist


【解决方案1】:

查看实际的源代码可能有助于澄清您的问题:

Java 8 中,ArrayList.removeRange() 如下所示:

protected void removeRange(int fromIndex, int toIndex) {
    modCount++;
    int numMoved = size - toIndex;
    System.arraycopy(elementData, toIndex, elementData, fromIndex,
                     numMoved);

    // clear to let GC do its work
    int newSize = size - (toIndex-fromIndex);
    for (int i = newSize; i < size; i++) {
        elementData[i] = null;
    }
    size = newSize;
}

Java 9 中,ArrayList.removeRange() 更改为:

protected void removeRange(int fromIndex, int toIndex) {
    if (fromIndex > toIndex) {
        throw new IndexOutOfBoundsException(
                outOfBoundsMsg(fromIndex, toIndex));
    }
    modCount++;
    shiftTailOverGap(elementData, fromIndex, toIndex);
}

private void shiftTailOverGap(Object[] es, int lo, int hi) {
    System.arraycopy(es, hi, es, lo, size - hi);
    for (int to = size, i = (size -= hi - lo); i < to; i++)
        es[i] = null;
}

正如您在上面的 sn-ps 中看到的,两种实现都使用System.arraycopy() 来删除列表中的项目。但只有从 Java 9 开始,检查才会抛出 IndexOutOfBoundsException if fromIndex &gt; toIndex

由于System.arraycopy() is implemented native the source code 在不同平台上可能会有所不同。根据javadoc,它的行为应该是这样的:

从指定的源数组复制一个数组,从指定位置开始,到目标数组的指定位置。 [...]

如果srcdest 参数引用同一个数组对象,则执行复制,就好像位置srcPossrcPos+length-1 的组件首先复制到带有length 组件的临时数组一样然后将临时数组的内容复制到目标数组的destPosdestPos+length-1 位置。

对于IndexOutOfBoundException 它说:

如果以下任何一项为真,则抛出 IndexOutOfBoundsException 并且不修改目标:

  • srcPos 参数是否定的。
  • destPos 参数是否定的。
  • length 参数是否定的。
  • srcPos+length 大于源数组的长度 src.length
  • destPos+length 大于目标数组的长度 dest.length

因此,如果您使用 Java 8 或更低版本运行示例,您可能会得到以下结果:

before remove : [a, b, c, d, e]
after remove (3, 1) : [a, b, c, b, c, d, e]

如果您使用 Java 9 或更高版本运行您的示例,您将收到以下异常:

before remove : [a, b, c, d, e]
Exception in thread "main" java.lang.IndexOutOfBoundsException: From Index: 3 > To Index: 1
    at java.base/java.util.ArrayList.removeRange(ArrayList.java:769)
    at TestRemoveRange.main(TestRemoveRange.java:16)

【讨论】:

  • 想知道上面的代码是如何工作的(版本
  • @sprabhakaran 是的,但是在不同的平台上可能会有所不同,因为System.arraycopy() 是本机实现的。我在帖子中添加了更多详细信息。
猜你喜欢
  • 1970-01-01
  • 2014-01-11
  • 1970-01-01
  • 2016-10-14
  • 2011-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-07
相关资源
最近更新 更多