Списки. Интерфейс List, класс ArrayList

Раньше мы познакомились с массивами, но их минус в том, что у них фиксированная длина. У них есть более гибкие собратья — списки. Списки реализуют интерфейс List. Они умеют делать всё то же, что и массивы, только их длина динамическая. Самая известная реализация списка — это ArrayList.

String[] stringArray = new String[4];
// так создаётся массив, который может хранить четыре строки

List stringList = new ArrayList<>();
// так создаётся пустой список строк

При записи в массив обычно устанавливают значение n-го элемента, а при записи в список — добавляют элементы.

stringArray[0] = "firstString";
// установить n-ый элемент массива

stringList.add("firstString");
// добавить элемент в конец списка

Добавляя элементы в список, можно не задумываться о его размере. Так массив можно сравнить с коробкой, а ArrayList — с менеджером коробок, который сам выберет коробку подходящего размера.

String[] stringArray = new String[4];
System.out.println(Arrays.toString(stringArray));
// [null, null, null, null]
List stringList = new ArrayList<>();
System.out.println(stringList);
// []
stringArray[0] = "firstString";
System.out.println(Arrays.toString(stringArray));
// [firstString, null, null, null]
stringList.add("firstString");
System.out.println(stringList);
// [firstString]
stringArray[1] = "secondString";
System.out.println(Arrays.toString(stringArray));
// [firstString, secondString, null, null]
stringList.add("secondString");
System.out.println(stringList);
// [firstString, secondString]

Так как экземпляр ArrayList может быть изменён, передавать его другому коду потенциально опасно: могут что-нибудь испортить.

System.out.println(me.getPreferredMusicGenres());
// [metal, metalcore, symphonic metal, post-hardcore, progressive metal]
List genres = me.getPreferredMusicGenres();
genres.set(0, "pop");
genres.set(1, "popcore");
genres.set(2, "symphonic pop");
genres.set(3, "post-pop");
genres.set(4, "progressive pop");
System.out.println(me.getPreferredMusicGenres());
// [pop, popcore, symphonic pop, post-pop, progressive pop]

Вместо того чтобы непосредственно возвращать экземпляр ArrayList, в этом случае лучше вернуть его, обернув его в неизменяемую обёртку. Collections.unmodifiableList() оборачивает переданный список в экземпляр класса, который который передаёт (проксирует) все операции чтения к нашему списку, но при попытке записи выбрасывает исключение.

class Me {
    private final List preferredMusicGenres;
    Me() {
        List genres = new ArrayList<>();
        genres.add("metal");
        genres.add("metalcore");
        genres.add("symphonic metal");
        genres.add("post-hardcore");
        genres.add("progressive metal");
        this.preferredMusicGenres = genres;
    }
    List getPreferredMusicGenres() {
//        return preferredMusicGenres;
        return Collections.unmodifiableList(preferredMusicGenres);
    }
}

Теперь при попытке выполнить код, изменяющий список, будет выброшено исключение.

[metal, metalcore, symphonic metal, post-hardcore, progressive metal]
Exception in thread "main" java.lang.UnsupportedOperationException
	at java.util.Collections$UnmodifiableList.set(Collections.java:1311)
	at online.javanese.basics.stdlib.lists.ArrayListMutation.main(ArrayListMutation.java:17)

Также можно возвращать копию списка (так называемое защитное копирование), но это повышает потребление памяти, т. к. на каждый вызов метода создаётся новая копия списка.

List getPreferredMusicGenres() {
    return new ArrayList<>(preferredMusicGenres);
}

В этом случае сторонний код не сможет испортить список, т. к. будет работать с собственной копией.

System.out.println(me.getPreferredMusicGenres());
// [metal, metalcore, symphonic metal, post-hardcore, progressive metal]
List genres = me.getPreferredMusicGenres();
genres.set(0, "pop");
genres.set(1, "popcore");
genres.set(2, "symphonic pop");
genres.set(3, "post-pop");
genres.set(4, "progressive pop");
System.out.println(genres);
// [pop, popcore, symphonic pop, post-pop, progressive pop]
System.out.println(me.getPreferredMusicGenres());
// [metal, metalcore, symphonic metal, post-hardcore, progressive metal]

Также есть важная оптимизация. Если заранее известен размер будущего списка, можно передать в его конструктор количество элементов: new ArrayList(5) создаст пустой ArrayList, который выделит память ровно под пять элементов.

List genres = new ArrayList<>(5);
genres.add("metal");
genres.add("metalcore");
genres.add("symphonic metal");
genres.add("post-hardcore");
genres.add("progressive metal");

Изначально ArrayList выделяет память для хранения десяти элементов. Если их меньше, оставшаяся память выделена зря. Если их больше, ArrayList растёт, т. е. выделяет новую память.

Комментарии к уроку

Сообщить об ошибке