

以下のような、二つの配列を併合させ、数字を小さい順に並べ替えた新たな配列を作りたいのですが、ArrayIndexOutOfBoundsExceptionというエラーが表示されます。

なぜこのようになるか見当がつきません。返答お待ちしております。

[code]

    public static int[] merge(int[] arr1, int[] arr2) {
          int[] arr3 = new int[arr1.length+arr2.length];
          int dim=0;
    
            for(int i=0;i<arr1.length; i++) {
                for(int j=0; j<arr2.length; j++) {
                    if(arr1[i]<arr2[j]) {
                        arr3[dim]=arr1[i];
                        dim++;
                    }
                    else if(arr1[i]==arr2[j]) {
                        arr3[dim]=arr1[i];
                        dim++;
                        arr3[dim]=arr2[j];
                        dim++;
                    }
                    else {
                        arr3[dim]=arr2[j];
                        dim++;
                    }
               }
    
            }
    
        return arr3;
    }
    
[/code]


CC BY-SA 4.0
2019-11-18T15:17:45.800
60636
2019-11-18T18:03:26.643
2019-11-18T15:38:13.657
3060
36172
question
java
Java で配列の作成時に ArrayIndexOutOfBoundsException エラー


`ArrayIndexOutOfBoundsException`という例外が発生するのであれば、ArrayのIndexを指定している部分を網羅的に追っていくと良いでしょう。

デバッグ機能のあるIDEを使用しているなら、小さな配列を渡して1ステップずつ実行しながら、変数の変化を見ていくのも手ですが、ここではひたすらデバッグ出力を仕込んでみます。

[code]

        public static int[] merge(int[] arr1, int[] arr2) {
            System.out.println("arr1.length="+arr1.length);
            System.out.println("arr2.length="+arr2.length);
            int[] arr3 = new int[arr1.length+arr2.length];
            System.out.println("arr3.length="+arr3.length);
            int dim=0;
            for(int i=0;i<arr1.length; i++) {
                for(int j=0; j<arr2.length; j++) {
                    System.out.println("i="+i+",j="+j);
                    if(arr1[i]<arr2[j]) {
                        System.out.println("[1]dim="+dim);
                        arr3[dim]=arr1[i];
                        dim++;
                    }
                    else if(arr1[i]==arr2[j]) {
                        System.out.println("[2]dim="+dim);
                        arr3[dim]=arr1[i];
                        dim++;
                        System.out.println("[3]dim="+dim);
                        arr3[dim]=arr2[j];
                        dim++;
                    }
                    else {
                        System.out.println("[4]dim="+dim);
                        arr3[dim]=arr2[j];
                        dim++;
                    }
               }
            }
            return arr3;
        }
    
[/code]

テスト用に`arr1`に長さ2の配列`{2, 4}`、`arr2`に長さ3の配列`{3,2,5}`を渡すと、こんな感じの出力が得られました。

[code]

    arr1.length=2
    arr2.length=3
    arr3.length=5
    i=0,j=0
    [1]dim=0
    i=0,j=1
    [2]dim=1
    [3]dim=2
    i=0,j=2
    [1]dim=3
    i=1,j=0
    [4]dim=4
    i=1,j=1
    [4]dim=5
    java.lang.ArrayIndexOutOfBoundsException: 5
        at arraymerge.ArrayMerge.merge(ArrayMerge.java:52)
        at arraymerge.ArrayMerge.main(ArrayMerge.java:19)
    
[/code]

`arr1`の長さが2ですから、`i`は0から1まで変化します。また`arr2`の長さは3なので`j`は0から2まで変化するはずですが、(`i`,
`j`)の値が最後の(1,
2)に達する前に`arr3`のIndexとして使われている`dim`の値が限界の4を超えてしまっているので、そこで`ArrayIndexOutOfBoundsException`の例外が発生してしまっています。

* * *

あなたのコードでは、

[code]

            for(int i=0;i<arr1.length; i++) {
                for(int j=0; j<arr2.length; j++) {
                    //...
                }
            }
    
[/code]

の二重ループがあるので、この例の場合2×3=6回ループ内の処理を実行しようとします。(この時点で、`arr3`の長さ5より多い!)

しかも、ループ内の処理は、

[code]

                    if(arr1[i]<arr2[j]) {
                        //...
                        dim++;
                    }
                    else if(arr1[i]==arr2[j]) {
                        //...
                        dim++;
                        //...
                        dim++;
                    }
                    else {
                        //...
                        dim++;
                    }
    
[/code]

となっているので、条件内のどの分岐を通ろうと、少なくとも1回、多いときには2回`dim++`を実行します。従って、二重`for`の2×3=6回の実行の間に、`dim`の値は少なくとも6に到達することになります。

`arr3`の長さは2+3=5しかないのですから、`dim`の値が4を超えてしまった時点で`arr3[dim]`を実行したときに、`ArrayIndexOutOfBoundsException`の例外が発生してしまっているわけです。

* * *

配列を前から順にスキャンして、結果配列に詰めていく、という処理を1回だけで「数字を小さい順に並べ替えた新たな配列」を作成することはできません。いわゆる「マージソート」で使われるマージ処理ですと、以下のような単純なマージ処理を繰り返し呼ぶことで、最終的に「数字を小さい順に並べ替えた新たな配列」を得ることができます。

[code]

        public static int[] merge(int[] arr1, int[] arr2) {
            int[] arr3 = new int[arr1.length+arr2.length];
            int idx = 0;
            for( int i = 0, j = 0; i < arr1.length || j < arr2.length; ) {
                if( (i < arr1.length && j < arr2.length && arr1[i] < arr2[j])
                 || (i < arr1.length && j == arr2.length) ) {
                    arr3[idx] = arr1[i];
                    ++idx;
                    ++i;
                } else {
                    arr3[idx] = arr2[j];
                    ++idx;
                    ++j;
                }
            }
            return arr3;
        }
    
[/code]

こちらの方のマージ処理を`{2, 4}`と`{3, 2, 5}`に対して1回だけ呼ぶと`{2, 3, 2, 4,
5}`と、完全に「小さい順に並べ替えた」ものにはなっていないのですが、それを部分的に順番通りになっている塊で切り直して、`{2, 3}`と`{2, 4,
5}`にした後にもう一度呼んでやると`{2, 2, 3, 4, 5}`が得られます。

* * *

あなたの今回の`merge`メソッドの目的が、マージソートの原理を知るための学習なのか、単純に配列をソートしたいだけなのかがわからないのですが、後者の場合であるなら、配列の中身を連結した後に、Javaに標準で備わっているソート系のメソッドを利用した方が良いでしょう。

(マージソートは元の配列の2倍の領域が必要なため、通常配列のソートには使われません。)


CC BY-SA 4.0
2019-11-18T18:03:26.643
60642
2019-11-18T18:03:26.643
13972
60636
answer