[解決済み】array[idx++]+="a "は、Java 8ではidxを1回増やすが、Java 9と10では2回増やすのはなぜか?
質問
挑戦するために コードゴルファー仲間 は、次のようなコードを書きました。 :
import java.util.*;
public class Main {
public static void main(String[] args) {
int size = 3;
String[] array = new String[size];
Arrays.fill(array, "");
for (int i = 0; i <= 100;) {
array[i++ % size] += i + " ";
}
for (String element: array) {
System.out.println(element);
}
}
}
このコードをJava 8で実行すると、次のような結果が得られます。
1 4 7 10 13 16 19 22 25 28 31 34 37 40 43 46 49 52 55 58 61 64 67 70 73 76 79 82 85 88 91 94 97 100
2 5 8 11 14 17 20 23 26 29 32 35 38 41 44 47 50 53 56 59 62 65 68 71 74 77 80 83 86 89 92 95 98 101
3 6 9 12 15 18 21 24 27 30 33 36 39 42 45 48 51 54 57 60 63 66 69 72 75 78 81 84 87 90 93 96 99
このコードをJava 10で実行すると、次のような結果が得られます。
2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68 70 72 74 76 78 80 82 84 86 88 90 92 94 96 98
2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68 70 72 74 76 78 80 82 84 86 88 90 92 94 96 98 100 102
2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68 70 72 74 76 78 80 82 84 86 88 90 92 94 96 98 100
Java10を使用すると、ナンバリングが完全にずれてしまいます。では、ここで何が起こっているのでしょうか?Java 10のバグなのでしょうか?
コメントからのフォローアップ
-
Java9以降でコンパイルした場合に問題が発生します(Java10で確認しました)。このコードをJava 8でコンパイルし、Java 9またはJava 11 early accessを含むそれ以降のバージョンで実行すると、期待通りの結果が得られます。
-
このようなコードは非標準ですが、仕様上は有効です。これは ケビン・クルイセン の中で の中で議論しています。 ゴルフに挑戦 そのため、奇妙なユースケースに遭遇しました。
-
ディディエ・L は、このはるかに小さく、より理解しやすいコードで問題を簡素化しました。
class Main { public static void main(String[] args) { String[] array = { "" }; array[test()] += "a"; } static int test() { System.out.println("evaluated"); return 0; } }
evaluated
evaluated evaluated
-
この問題は、文字列の連結と代入の演算子に限定されているようです(
+=
のように、副作用のある式を左オペランドとして指定した場合。array[test()]+="a"
,array[ix++]+="a"
,test()[index]+="a"
またはtest().field+="a"
. 文字列の連結を有効にするには、少なくとも片方の辺の型がString
. 他の型や構成要素で再現しようとすると失敗します。
解決方法は?
のバグです。
javac
は、JDK 9 から始まった(文字列の連結に関していくつかの変更があり、それが問題の一部であると思われる)。
で確認したように
javac
チームが、バグID JDK-8204322で
. その行に対応するバイトコードを見てみると。
array[i++%size] += i + " ";
です。
21: aload_2
22: iload_3
23: iinc 3, 1
26: iload_1
27: irem
28: aload_2
29: iload_3
30: iinc 3, 1
33: iload_1
34: irem
35: aaload
36: iload_3
37: invokedynamic #5, 0 // makeConcatWithConstants:(Ljava/lang/String;I)Ljava/lang/String;
42: aastore
最後の
aaload
は、配列からの実際の読み込みです。しかし、その部分
21: aload_2 // load the array reference
22: iload_3 // load 'i'
23: iinc 3, 1 // increment 'i' (doesn't affect the loaded value)
26: iload_1 // load 'size'
27: irem // compute the remainder
という表現にほぼ対応する。
array[i++%size]
(実際のロードとストアを除いて)2回入っています。これは誤りであり、仕様では
jls-15.26.2
:
複合代入式は、次のような形式です。
E1 op= E2
は次のように等価です。E1 = (T) ((E1) op (E2))
ここでT
のタイプです。E1
, ただしE1
は一度だけ評価されます。
ということは、式
array[i++%size] += i + " ";
の部分は
array[i++%size]
は1回しか評価されないはずです。しかし、これは2回評価されます(1回はロードのため、もう1回はストアのため)。
そう、これはバグなんです。
いくつかの更新を行いました。
このバグはJDK11で修正され、JDK10にバックポートされました( こちら と こちら ) が、JDK 9 には適用されません。 パブリックアップデートを受けなくなった .
Aleksey Shipilevは、以下のように言及しています。 日英協会ページ (そしてここのコメントでは@DidierL)。
回避策: コンパイル時に
-XDstringConcat=inline
を使用することに戻ります。
StringBuilder
を使用して連結を行い、バグを発生させません。
関連
-
[解決済み] Eclipse デフォルトのフォント名
-
[解決済み] XX:MaxDirectMemorySizeの既定値
-
[解決済み] javac ソースファイルが見つかりません
-
[解決済み] Java の条件付きコンパイル:コードチャンクをコンパイルしないようにするには?
-
[解決済み] 型の不一致:ArrayListからListへの変換ができない
-
[解決済み] eclipseからTomcatに物理的に発行されるmyjspはどこにあるのでしょうか?
-
[解決済み] Javaコンパイラーエラー:ステートメントではありません
-
[解決済み] Javaで配列を宣言し、初期化する方法は?
-
[解決済み] なぜJavaにはtransientフィールドがあるのですか?
-
[解決済み] 整数の平方根が整数であるかどうかを判断する最速の方法
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] この配列の中の数字を入れ替えるには、何が足りないのでしょうか?ジャバ
-
[解決済み] double 型を Int 型に変換、切り捨て
-
[解決済み] android.support.v4.app.FragmentActivity' で 'TAG' がプライベートアクセスされている。
-
[解決済み] JOGLまたはLWJGLの既成のプロジェクト
-
[解決済み] ファイルを作成せずに、ファイルが存在するかどうかをチェックする
-
[解決済み] eclipseからTomcatに物理的に発行されるmyjspはどこにあるのでしょうか?
-
[解決済み] アクティビティに割り当てられない
-
[解決済み] 文字列が一意な文字であるかどうかを判定する
-
[解決済み] publicId と systemId の間に空白が必要です。
-
[解決済み] Java: getInstanceとStaticの比較