System.arraycopy()抛出越界异常

时间:2013-09-08 19:41:55

标签: java generics data-structures indexoutofboundsexception

我创建了一个基于数组的通用大小调整循环缓冲区,这在我的测试中做得很好,直到这个特殊情况

  1. head == tail and
  2. 总数组填充了元素
  3. 我希望数组像往常一样调整大小,把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)
    

    可能是微不足道的,但我似乎无法找到可能的东西。有人可以帮忙吗?

    注意
    头>从下一次出列的位置发生的位置
    尾巴>进入下一个排队的位置

    修改:根据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
    

1 个答案:

答案 0 :(得分: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);