【问题标题】:System.arraycopy() throwing out-of-bounds exceptionSystem.arraycopy() 抛出越界异常
【发布时间】:2013-09-08 19:41:55
【问题描述】:

我创建了一个基于数组的通用大小调整循环缓冲区,它在我的测试中表现良好,直到出现这种特殊情况

  1. 头 == 尾和
  2. 整个数组被元素填充

我希望数组像往常一样调整大小,把 head = 0 , tail = 要插入的新位置,一切都会好起来的。但这就是我得到的输出:

adding : e
head : 5 tail : 5 , size : 12
a b c d e f g h i j k l 

adding : f
resizing array to size: 24
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
    at java.lang.System.arraycopy(Native Method)
    at ResizingCircularArray.resize(ResizingCircularArray.java:16)
    at ResizingCircularArray.enqueue(ResizingCircularArray.java:33)
    at ResizingCircularArray.main(ResizingCircularArray.java:105)

可能是一些微小但重要的东西,但我似乎无法找到可能是什么。有人可以帮忙吗?

注意
head > 下一次出队的位置
tail > 定位到下一个队列将发生的位置

编辑: 按照 ns47731 的建议,我将 arraycopy 行从system.arraycopy(arr, head, tempArr, 0, size); 更改为System.arraycopy(arr, 0, tempArr, 0, size);。这虽然解决了异常问题,但会导致逻辑错误,如下所示:

43.dequeing : f
head : 13 tail : 20 , size : 7
null null null null null null null null null null null null null g h i j k l m null null null null 

44.dequeing : g
resizing array to size: 12
head : 0 tail : 6 , size : 6
null null null null null null null null null null null null 

即,数据部分 g h i j k l m 正在被丢弃。

我意识到问题出在 System.arraycopy() 本身,它是为线性数组设计的,而不是循环数组,所以我为自己创建了一个简单的版本,它适用于循环数组:

private void arrayCopy(E[] srcArr , int srcpos , E[] destArr , int destpos , int length){
        for(int index = 0 ; index < length ; index++){
            destArr[index] = srcArr[head++];
            if(head == srcArr.length){
                head = (head % srcArr.length);
            }
        }
    }

稍后我将不得不添加异常情况,但这通常是可行的。

修改后的代码

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

class ResizingCircularArray<E> {

    private int head = 0;
    private int tail = 0;
    private int size = 0; // a measure of non-null elements in the array
    private E[] arr;

    // Modified version of System.arraycopy() to work with circular array.
    private void arrayCopy(E[] srcArr , int srcpos , E[] destArr , int destpos , int length){
        for(int index = 0 ; index < length ; index++){
            destArr[index] = srcArr[head++];
            if(head == srcArr.length){
                head = (head % srcArr.length);
            }
        }
    }

    private void resize() {
        System.out.println("resizing array to size: " + 2 * size);
        @SuppressWarnings("unchecked")
        E[] tempArr = (E[]) new Object[2 * size];
        arrayCopy(arr, head, tempArr, 0, size);
        head = 0;
        tail = size; // tail point to where the NEXT element will land
        arr = tempArr;
    }

    @SuppressWarnings("unchecked")
    public ResizingCircularArray() {
        arr = (E[]) new Object[3];

    }

    public void enqueue(E item) {
        if (item == null)
            throw new NullPointerException(
                    " adding null values is not allowed ");
        if (size == arr.length) {
            resize();
        }
        if (tail == arr.length) {
            // going round
            tail = (tail % arr.length);
        }
        arr[tail++] = item;
        size++;
        System.out.println("head : " + head + " tail : " + tail + " , size : "
                + size);
    }

    public E dequeue() {
        if (!(size > 0))
            throw new java.util.NoSuchElementException("size is negative");
        E item = arr[head];
        arr[head++] = null;
        if (head == (arr.length)) {
            head = (head % arr.length); // =0
        }
        --size;
        if (size == arr.length / 4) {
            resize();
        }
        System.out.println("head : " + head + " tail : " + tail + " , size : "
                + size);
        return item;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public E sample(int offset) {
        if (offset < 0)
            throw new java.lang.IllegalArgumentException(
                    "provided offset is out of bounds");
        return arr[head + offset];
        /*
         * NOTE : the check for (head+offset)>tail as pointed out by sos will
         * work in case of linear array , Not in case of Circular array because
         * when tail comes around in a circle , tail will be < than head and the
         * above check will create trouble
         */
    }

    public int size() {
        return size;
    }

    public void display() {
        for (E item : arr)
            System.out.print(item + " ");
        System.out.println("\n");
    }

    public static void main(String[] args) {

        ResizingCircularArray<String> r = new ResizingCircularArray<String>();
        String line = null;
        String[] segment, parsed;
        boolean endFlag = false;
        int count = 0;

        try (BufferedReader is = new BufferedReader(new FileReader(
                "CircArrayPoints.txt"))) {
            line = is.readLine();
            segment = line.trim().split(";");
            System.out.println("total commands : " + segment.length);
            for (int i = 0; !segment[i].equals("stop") && !endFlag; i++) {
                parsed = segment[i].split(" ");
                count++;
                switch (parsed[0]) {
                case "enq":
                    System.out.println(count+ ".adding : " + parsed[1]);
                    r.enqueue(parsed[1]);
                    r.display();
                    break;
                case "deq":
                    if (r.isEmpty()) {
                        System.out.println("Empty queue");
                        endFlag = true;
                        break;
                    }
                    // print after checking isEmpty() to make sure
                    // sample(0) doesn't call null etc
                    System.out.println(count+ ".dequeing : " + r.sample(0));
                    r.dequeue();
                    r.display();
                    break;
                case "disp":
                    r.display();
                    break;
                default:
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

文件:CircArrayPoints.txt

enq a;enq b;enq c;enq d;enq e;enq f;enq g;enq h;enq i;enq j;enq k;enq l;deq;deq;deq;deq;deq;enq a;enq b;enq c;enq d;enq e;enq f;enq g;enq h;enq i;enq j;enq k;enq l;enq m;deq;deq;deq;deq;deq;deq;deq;deq;deq;deq;deq;deq;deq;deq;deq;deq;deq;disp;stop

【问题讨论】:

  • ` if (tail == arr.length) { // 循环 tail = (tail % arr.length); }` 这有点没用。如果你要实际检查平等,那么你可以说tail=0。如果你使用tail=tail%arr.length,那么你根本不需要if语句,当tail小于arr.length时,tail不会改变。
  • 你是对的。我保留它是为了告诉自己我在做什么。如果这是有道理的。完成最终测试后,我将删除它。

标签: java generics data-structures indexoutofboundsexception


【解决方案1】:

让你的数组副本被读取

System.arraycopy(arr, 0, tempArr, 0, size);

为什么?参数 #2 (int) startPos 是它应该从源数组开始复制的位置,您从数组的长度开始导致索引超出范围。

查看http://docs.oracle.com/javase/6/docs/api/java/lang/System.html#arraycopy(java.lang.Object%2C%20int%2C%20java.lang.Object%2C%20int%2C%20int) 了解有关该方法的更多详细信息。

编辑:首先我解决了你的初始问题,你应该自己解决剩下的问题。阅读arraycopy javadoc(顺便说一句写得很好)。它应该是 System.arraycopy(arr, head, tempArr, 0, tail-head);

【讨论】:

  • @SomjitNag 我复制了你的代码和文本文件并运行它,它对我来说很好。
  • 我编辑了我之前的评论,因为虽然您的回答解决了被抛出的异常,但它会产生不同的问题。我正在更新我的帖子以反映它。
  • 我认为 System.arraycopy 不适用于循环数组,因此为自己创建了一个适用于它们的版本。我已经相应地更新了我的帖子和代码。 ps:稍后我将不得不将异常部分添加到我的 arrayCopy() 版本中。
  • 你试过改成 System.arraycopy(arr, head, tempArr, 0, tail-head);
  • 这样做有什么好处? tail - head 似乎不是获取圆形数组大小的万无一失的方法。不过我可能错了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-30
  • 1970-01-01
  • 2018-06-18
  • 1970-01-01
相关资源
最近更新 更多