SlideShare a Scribd company logo
ピュアJavaだと思った?
残念Androidでした
~いつからAndroidをJavaだと錯覚していた?~
空中清高
空中清高 @soranakk
● 所属:ジャストシステム
○ スマイルゼミ中学生コース
■ ホームアプリ
■ システムアプリ
● Android開発歴:4年半ぐらい
目次
Oracle JavaとAndroid Javaの違い
ArrayListの不思議な現象
ピュアJavaだと思った?残念Androidでした
ArrayListで例外が発生する仕組み
いつからAndroidをJavaだと錯覚していた?
AndroidとJavaはAPIが同じなだけ
その他の事例もあるぞ(先駆者紹介)
目次
Oracle JavaとAndroid Javaの違い
ArrayListの不思議な現象
ピュアJavaだと思った?残念Androidでした
ArrayListで例外が発生する仕組み
いつからAndroidをJavaだと錯覚していた?
AndroidとJavaはAPIが同じなだけ
その他の事例もあるぞ(先駆者紹介)
Oracle JavaとAndroid Javaの違い
Android JavaOracle Java
● javaはclassファイルにされ、さらにdexファ
イルにコンパイルされる
● 実行環境はDalvik or ART
● javaはclassファイルにコンパイルされ
る
● 実行環境はLinux, Mac OS,
WindowsなどのJRE
Oracle Javaの世界
JVM
Java実行環境
rt.jar
● Object
● String
● List   … etc
標準ライブラリ
classjava
Android Javaの世界
Dalvik or ART
android.jar
● Object
● String
● List   … etc
標準ライブラリ
dexclass
java
Oracle JavaとAndroid Javaの違い
Android JavaOracle Java
● buildツール
● バイトコード
● 標準ライブラリ
● 実行マシン
● 実行OS
● etc...
Oracle JavaとAndroid Javaの違い
Android JavaOracle Java
● buildツール
● バイトコード
● 標準ライブラリ
● 実行マシン
● 実行OS
● etc...
今日話すのはココ!
標準ライブラリの違い
Oracle JavaとAndroid Javaの違い
Android JavaOracle Java
rt.jar
● Object
● String
● List   … etc
標準ライブラリ
android.jar
● Object
● String
● List   … etc
標準ライブラリ
VS
目次
Oracle JavaとAndroid Javaの違い
ArrayListの不思議な現象
ピュアJavaだと思った?残念Androidでした
ArrayListで例外が発生する仕組み
いつからAndroidをJavaだと錯覚していた?
AndroidとJavaはAPIが同じなだけ
その他の事例もあるぞ(先駆者紹介)
ArrayListの不思議な現象
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("baz".equals(str)) {
System.out.println("find!");
}
System.out.println(str);
}
ArrayListの不思議な現象
> foo
> bar
> find!
> baz
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("baz".equals(str)) {
System.out.println("find!");
}
System.out.println(str);
}
for文中にあることをすると・・・おおっと
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
for文中にあることをすると・・・おおっと
> foo
> bar
> baz
> ConcurrentModificationException
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
ConcurrentModificationException is 何?
● マルチスレッドで同じArrayListを変更したときなどに発生する例外
● シングルスレッドでもループ文などで発生することがある
● 仕組み(概要)
○ ArrayListは内部的に変更回数を持っている
○ Iteratorで変更回数をチェックしている
○ 変更回数がループ開始時点から変わっていたら例外を投げる
「あるはずの終端が無い!」とか、「終わったと思ったのにまだ残ってる!?」
とかを防ぐための機構。
JavaDocによると、「完全に防げるわけではない、best-effort」
https://docs.oracle.com/javase/9/docs/api/java/util/ConcurrentModificationExc
eption.html
ConcurrentModificationException is 何?
● マルチスレッドで同じArrayListを変更したときなどに発生する例外
● シングルスレッドでもループ文などで発生することがある
● 仕組み(概要)
○ ArrayListは内部的に変更回数を持っている
○ Iteratorで変更回数をチェックしている
○ 変更回数がループ開始時点から変わっていたら例外を投げる
「あるはずの終端が無い!」とか、「終わったと思ったのにまだ残ってる!?」
とかを防ぐための機構。
JavaDocによると、「完全に防げるわけではない、best-effort」
https://docs.oracle.com/javase/9/docs/api/java/util/ConcurrentModificationExc
eption.html
best-effort?
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
ArrayListの不思議な現象
> foo
> bar
> baz
> ConcurrentModificationException
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
ArrayListの不思議な現象
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
> foo
> bar
> baz
> ConcurrentModificationException
ArrayListの不思議な現象
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
> foo
> bar
> baz
> ConcurrentModificationException
ココが違う
ArrayListの不思議な現象
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
> foo
> bar
> baz
> ConcurrentModificationException
ココが違う
> foo
> bar
ArrayListの不思議な現象
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
> foo
> bar
> baz
> ConcurrentModificationException
ココが違う
例外が発生しない!?
> foo
> bar
ArrayListの不思議な現象
● ConcurrentModificationExceptionの発生はbest-effort
● 例外は発生したり、しなかったりする
● たとえばリストの最後から二番目をremoveしたときは発生しない
ArrayListの不思議な現象
● ConcurrentModificationExceptionの発生はbest-effort
● 例外は発生したり、しなかったりする
● たとえばリストの最後から二番目をremoveしたときは発生しない
Javaは不思議だなぁ
Androidでも同じかなぁ?
目次
Oracle JavaとAndroid Javaの違い
ArrayListの不思議な現象
ピュアJavaだと思った?残念Androidでした
ArrayListで例外が発生する仕組み
いつからAndroidをJavaだと錯覚していた?
AndroidとJavaはAPIが同じなだけ
その他の事例もあるぞ(先駆者紹介)
ピュアJavaだと思った?残念Androidでした
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
ココが違う
ピュアJavaだと思った?残念Androidでした
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
ココが違う
> foo
> bar
> baz
> 例外発生
Oracle Java
> foo
> bar
Oracle Java
ピュアJavaだと思った?残念Androidでした
> foo
> bar
> baz
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
ココが違う
> foo
> bar
> baz
> 例外発生
Android JavaOracle Java
> foo
> bar
> 例外発生
> foo
> bar
Android JavaOracle Java
ピュアJavaだと思った?残念Androidでした
> foo
> bar
> baz
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
ココが違う
> foo
> bar
> baz
> 例外発生
Android JavaOracle Java
> foo
> bar
> 例外発生
> foo
> bar
Android JavaOracle Java
JavaとAndroidで結果が違う
目次
Oracle JavaとAndroid Javaの違い
ArrayListの不思議な現象
ピュアJavaだと思った?残念Androidでした
ArrayListで例外が発生する仕組み
いつからAndroidをJavaだと錯覚していた?
AndroidとJavaはAPIが同じなだけ
その他の事例もあるぞ(先駆者紹介)
ループ開始
ArrayListで例外が発生する仕組み:概要
ループ開始 変更回数をチェック
ArrayListで例外が発生する仕組み:概要
ループ開始 変更回数をチェック あれ?変更回数が違ってる?
ArrayListで例外が発生する仕組み:概要
ループ開始 変更回数をチェック あれ?変更回数が違ってる?
ArrayListで例外が発生する仕組み:概要
もしかするとあるはずの終端が無いかもしれない?
終わったと思ったのにまだ残ってるかもしれない!?
ループ開始 変更回数をチェック
List警察だ!例外を投げる!
あれ?変更回数が違ってる?
ArrayListで例外が発生する仕組み:概要
もしかするとあるはずの終端が無いかもしれない?
終わったと思ったのにまだ残ってるかもしれない!?
ループ開始 変更回数をチェック
List警察だ!例外を投げる!
あれ?変更回数が違ってる?
ArrayListで例外が発生する仕組み:概要
もしかするとあるはずの終端が無いかもしれない?
終わったと思ったのにまだ残ってるかもしれない!?
概要はわかったけど、
まだよくわかんないなぁ
ループ開始 変更回数をチェック
List警察だ!例外を投げる!
あれ?変更回数が違ってる?
ArrayListで例外が発生する仕組み:概要
もしかするとあるはずの終端が無いかもしれない?
終わったと思ったのにまだ残ってるかもしれない!?
やっぱエンジニアだしコード
で理解したい
ArrayListで例外が発生する仕組み
for (String str : list) {
...省略
}
拡張for文は次のように展開されます
https://jcp.org/aboutJava/communityprocess/jsr/tiger/enhanced-for.html
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
...省略
}
ArrayListで例外が発生する仕組み
● ListはmodCountというint値を持っている
● modCountはadd() remove()などでインクリメントされる
● IteratorはmodCountを監視している
Java 9.0.4 java.util.ArrayList.javaより一部抜粋 (872行あたりから)
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
…省略
}
public Iterator<E> iterator() {
return new Itr();
}
class Itr implements Iterator<E> {
int expectedModCount = modCount;
…省略
}
ArrayListで例外が発生する仕組み
class Itr implements Iterator<E> {
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
public E next() {
checkForComodification();
int i = cursor;
...省略
cursor = i + 1;
return (E) elementData[lastRet = i];
}
…省略
Java 9.0.4 java.util.ArrayList.javaより一部抜粋 (872行あたりから)
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
…省略
}
ArrayListで例外が発生する仕組み
Java 9.0.4 java.util.ArrayList.javaより一部抜粋 (872行あたりから)
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
…省略
}
class Itr implements Iterator<E> {
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
public E next() {
checkForComodification();
int i = cursor;
...省略
cursor = i + 1;
return (E) elementData[lastRet = i];
}
…省略
例外発生の仕組み Oracle Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
例外発生の仕組み Oracle Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
例外発生の仕組み Oracle Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
例外発生の仕組み Oracle Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "baz"のときcursor = 3
例外発生の仕組み Oracle Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "baz"のときcursor = 3
remove(str)でmodCount++, size = 2
例外発生の仕組み Oracle Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "baz"のときcursor = 3
remove(str)でmodCount++, size = 2
次のhasNext()はcursor != size == true で継続
例外発生の仕組み Oracle Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "baz"のときcursor = 3
remove(str)でmodCount++, size = 2
次のhasNext()はcursor != size == true で継続
next()のcheckForComodification()で
modCount != expectedModCount == true
例外発生の仕組み Oracle Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "baz"のときcursor = 3
remove(str)でmodCount++, size = 2
次のhasNext()はcursor != size == true で継続
next()のcheckForComodification()で
modCount != expectedModCount == true
ん~・・・
例外発生の仕組み Oracle Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "baz"のときcursor = 3
remove(str)でmodCount++, size = 2
次のhasNext()はcursor != size == true で継続
next()のcheckForComodification()で
modCount != expectedModCount == true
変わってる
例外発生の仕組み Oracle Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "baz"のときcursor = 3
remove(str)でmodCount++, size = 2
次のhasNext()はcursor != size == true で継続
next()のcheckForComodification()で
modCount != expectedModCount == true
List警察だ!例外を投げる!
変わってる
例外が発生しない仕組み Oracle Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
例外が発生しない仕組み Oracle Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
例外が発生しない仕組み Oracle Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
例外が発生しない仕組み Oracle Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "bar"のときcursor = 2
例外が発生しない仕組み Oracle Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "bar"のときcursor = 2
remove(str)でmodCount++, size = 2
例外が発生しない仕組み Oracle Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "bar"のときcursor = 2
remove(str)でmodCount++, size = 2
次のhasNext()はcursor != size == false でループ終了
例外が発生しない仕組み Oracle Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "bar"のときcursor = 2
remove(str)でmodCount++, size = 2
次のhasNext()はcursor != size == false でループ終了
next()は呼ばれないので
checkForComodification()は呼ばれない
例外が発生しない仕組み Oracle Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
・・・
拡張for文が展開される
str == "bar"のときcursor = 2
remove(str)でmodCount++, size = 2
次のhasNext()はcursor != size == false でループ終了
next()は呼ばれないので
checkForComodification()は呼ばれない
目次
Oracle JavaとAndroid Javaの違い
ArrayListの不思議な現象
ピュアJavaだと思った?残念Androidでした
ArrayListで例外が発生する仕組み
いつからAndroidをJavaだと錯覚していた?
AndroidとJavaはAPIが同じなだけ
その他の事例もあるぞ(先駆者紹介)
いつからAndroidをJavaだと錯覚していた?
Java 9.0.4 java.util.ArrayList.javaより一部抜粋 (872行あたりから)
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
…省略
}
class Itr implements Iterator<E> {
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
public E next() {
checkForComodification();
int i = cursor;
...省略
cursor = i + 1;
return (E) elementData[lastRet = i];
}
…省略
いつからAndroidをJavaだと錯覚していた?
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
…省略
}
class Itr implements Iterator<E> {
protected int limit = ArrayList.this.size;
int expectedModCount = modCount;
public boolean hasNext() {
return cursor < limit;
}
public E next() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
int i = cursor;
...省略
cursor = i + 1;
return (E) elementData[lastRet = i];
}
...省略
Android 26 java.util.ArrayList.javaより一部抜粋 (833行あたりから)
class Itr implements Iterator<E> {
protected int limit = ArrayList.this.size;
int expectedModCount = modCount;
public boolean hasNext() {
return cursor < limit;
}
public E next() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
int i = cursor;
...省略
cursor = i + 1;
return (E) elementData[lastRet = i];
}
...省略
いつからAndroidをJavaだと錯覚していた?
limitが追加され、
hasNext()の条件が変わっている
listのsizeが変わっても、limitは固
定されているので
hasNext()の動作が変わった!
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
…省略
}
Android 26 java.util.ArrayList.javaより一部抜粋 (833行あたりから)
例外が発生しない仕組み Android Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
例外が発生しない仕組み Android Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
例外が発生しない仕組み Android Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
例外が発生しない仕組み Android Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "baz"のときcursor = 3
例外が発生しない仕組み Android Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "baz"のときcursor = 3
remove(str)でmodCount++, size = 2だけどlimit = 3のまま
例外が発生しない仕組み Android Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "baz"のときcursor = 3
remove(str)でmodCount++, size = 2だけどlimit = 3のまま
次のhasNext()はcursor < limit == false でループ終了
例外が発生しない仕組み Android Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "baz"のときcursor = 3
remove(str)でmodCount++, size = 2だけどlimit = 3のまま
次のhasNext()はcursor < limit == false でループ終了
next()は呼ばれないので
modCount != expectedModCount はチェックされない
例外が発生しない仕組み Android Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("baz".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "baz"のときcursor = 3
remove(str)でmodCount++, size = 2だけどlimit = 3のまま
次のhasNext()はcursor < limit == false でループ終了
next()は呼ばれないので
modCount != expectedModCount はチェックされない
・・・
例外発生の仕組み Android Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
例外発生の仕組み Android Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (String str : list) {
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
例外発生の仕組み Android Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
例外発生の仕組み Android Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "bar"のときcursor = 2
例外発生の仕組み Android Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "bar"のときcursor = 2
remove(str)でmodCount++, size = 2だけどlimit = 3のまま
例外発生の仕組み Android Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "bar"のときcursor = 2
remove(str)でmodCount++, size = 2だけどlimit = 3のまま
次のhasNext()はcursor < limit == true で継続
例外発生の仕組み Android Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "bar"のときcursor = 2
remove(str)でmodCount++, size = 2だけどlimit = 3のまま
次のhasNext()はcursor < limit == true で継続
next()で
modCount != expectedModCount == true
例外発生の仕組み Android Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
ん~・・・
拡張for文が展開される
str == "bar"のときcursor = 2
remove(str)でmodCount++, size = 2だけどlimit = 3のまま
次のhasNext()はcursor < limit == true で継続
next()で
modCount != expectedModCount == true
例外発生の仕組み Android Java
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "bar"のときcursor = 2
remove(str)でmodCount++, size = 2だけどlimit = 3のまま
次のhasNext()はcursor < limit == true で継続
next()で
modCount != expectedModCount == true
変わってる
例外発生の仕組み Android Java
List警察だ!例外を投げる!
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("bar".equals(str)) {
list.remove(str);
}
System.out.println(str);
}
拡張for文が展開される
str == "bar"のときcursor = 2
remove(str)でmodCount++, size = 2だけどlimit = 3のまま
次のhasNext()はcursor < limit == true で継続
next()で
modCount != expectedModCount == true
変わってる
目次
Oracle JavaとAndroid Javaの違い
ArrayListの不思議な現象
ピュアJavaだと思った?残念Androidでした
ArrayListで例外が発生する仕組み
いつからAndroidをJavaだと錯覚していた?
AndroidとJavaはAPIが同じなだけ
その他の事例もあるぞ(先駆者紹介)
AndroidとJavaはAPIが同じなだけ
Oracle Java
● ConcurrentModificationExceptionの発生はbest-effort
● 例外は発生したり、しなかったりする
● たとえばリストの最後から二番目をremoveしたときは発生しない
Android Java
● ConcurrentModificationExceptionの発生はbest-effort
● 例外は発生したり、しなかったりする
● たとえばリストの最後をremoveしたときは発生しない
AndroidとJavaはAPIが同じなだけ
Oracle Java
● ConcurrentModificationExceptionの発生はbest-effort
● 例外は発生したり、しなかったりする
● たとえばリストの最後から二番目をremoveしたときは発生しない
Android Java
● ConcurrentModificationExceptionの発生はbest-effort
● 例外は発生したり、しなかったりする
● たとえばリストの最後をremoveしたときは発生しない
標準ライブラリが違う!
目次
Oracle JavaとAndroid Javaの違い
ArrayListの不思議な現象
ピュアJavaだと思った?残念Androidでした
ArrayListで例外が発生する仕組み
いつからAndroidをJavaだと錯覚していた?
AndroidとJavaはAPIが同じなだけ
その他の事例もあるぞ(先駆者紹介)
その他の事例もあるぞ(先駆者紹介)
● Java と Android の正規表現の動作の違い
   https://qiita.com/KeithYokoma/items/2915a904a18760ee67c4
○ 正規表現ライブラリの違いに解説されています
● Android Javaの正規表現の落とし穴
   https://www.ecoop.net/memo/archives/regular-expression-problem-o-android-java.html
○ こちらも正規表現ですが、Stringクラスの違いについて解説されています
ご静聴ありがとうございました。
細かい補足
じゃあfor文中にリストから削除したいときはどうしたらいいの?
拡張for文の中でlist#removeは使ってはいけません。
代わりにIteratorでfor文を回して、Iterator#removeを使いましょう。
あるいはKotlinのfilterを使うと良いです。
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
String str = iter.next();
if ("baz".equals(str)) {
iter.remove();
}
System.out.println(str);
}
細かい補足
Android N 以降はOpen JDKを使っているって聞いたけど?
はい、その通りです。
でも実はAndroidのJavaはOpen JDKからさらに変更を加えています
そのため、今回紹介したArrayListの操作では
Open JDK != Android Java
となります。
Open JDKだと思った?
残念Androidでした
コミットログ
https://android.googlesource.com/platform/libcore/+/b10b2a3ab693cfd6156d06ffe4e00ce69b9c9194
細かい補足
Android N より前はHarmonyを使っているって聞いたけど?
はい、その通りです。
でも実はAndroidのJavaはApache Harmonyからさらに変更を加えています
そのため、今回紹介したArrayListの操作では
Apache Harmony != Android Java
となります。
Apache Harmonyだと思った?
残念Androidでした
コミットログ
https://android.googlesource.com/platform/libcore/+/7de2d41b95fc968b0ccc530c28d66f003ff9ab2a
細かい補足
なんでこんな違いが生まれたの?
コミットコメントやソースコードのコメントを読んでみると
Android側は意図して修正しています。
Open JDKのhasNext()はlistのsizeに依存して
false返したりtrue返したりするので、固定させるために修正したみたいです。
コミットログ
https://android.googlesource.com/platform/libcore/+/b10b2a3ab693cfd6156d06ffe4e00ce69b9c9194
ソースコードのコメント
https://android.googlesource.com/platform/libcore/+/master/ojluni/src/main/java/java/util/ArrayList.jav
a#841
ご静聴ありがとうございました。

More Related Content

ピュアJavaだと思った?残念androidでした~いつからAndroidをJavaだと錯覚していた?~