Javaのはなし

この記事は、 PMOB Advent Calendar 2016 の 12日目 の記事です。

埋め埋め

TL;DR

d.hatena.ne.jp

subListは親のListを参照している

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> animals = new ArrayList<>();

        animals.add("犬");
        animals.add("猫");
        animals.add("鳥");
        animals.add("人間");

        animals.forEach(System.out::println);
        System.out.println("---");

        // List<String> notHuman = animals.subList(0, 2);
        // -> これは["犬", "猫" ]となる。
        List<String> notHuman = animals.subList(0, 3);

        notHuman.remove(0);

        animals.forEach(System.out::println);
        System.out.println("---");

    }

}

実行結果:

犬
猫
鳥
人間
---
猫
鳥
人間
---

下手に操作するとsubListはバグる

以下のコードを実行

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> animals = new ArrayList<>();

        animals.add("犬");
        animals.add("猫");
        animals.add("鳥");
        animals.add("人間");

        animals.forEach(System.out::println);
        System.out.println("===");

        List<String> notHuman = animals.subList(0, 3);
        List<String> human = animals.subList(3, 4);

        notHuman.add("猿"); // ウッキー!

        System.out.println("- NOT HUMAN");
        notHuman.forEach(System.out::println);
        System.out.println("- HUMAN");
        human.forEach(System.out::println);

    }

}

実行結果:

犬
猫
鳥
人間
===
- NOT HUMAN
犬
猫
鳥
猿
- HUMAN
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1231)
    at java.util.ArrayList$SubList.listIterator(ArrayList.java:1091)
    at java.util.AbstractList.listIterator(AbstractList.java:299)
    at java.util.ArrayList$SubList.iterator(ArrayList.java:1087)
    at java.lang.Iterable.forEach(Iterable.java:74)
    at Main.main(Main.java:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

この例外を見ても僕の頭では難しくて脳が壊れる。

原因

d.hatena.ne.jp

上記記事を見れば大体解決します。

今回の場合「猿」をサブリストに追加したことで親のリストの数が変化した、 ということがhumanリストに伝える手段が無いために置きたエラーであると解釈できる。

解決

同様の操作をするために参照を切りましょう。

new ArrayList<>(list.subList(from, to));

みたいな。

そもそもsubListをするってことは別に親を参照する必要がない場合のほうが多いのでは?

参考リンク