List: примеры (JAVA)
list: String[]Описание интерфейса List в Java
Интерфейс java.util.List<E> представляет упорядоченную коллекцию элементов с доступом по индексу. List задает поведение для коллекций, в которых допускаются дубликаты и важен порядок вставки. Тип параметра E определяет тип элементов в списке.
Основные назначения: хранение последовательностей объектов, реализация очередей и стеков поверх списков, передача упорядоченных наборов между методами.
Ключевые методы и их назначение, сигнатуры и возвращаемые значения:
boolean add(E e)- добавляет элемент в конец списка. Возвращаетtrue(обычно всегда), может броситьUnsupportedOperationExceptionдля неизменяемых списков.void add(int index, E element)- вставляет элемент по индексу. При неверном индексе -IndexOutOfBoundsException.boolean addAll(Collection<? extends E> c)- добавляет все элементы коллекции в конец. Возвращаетtrue, если список изменился.E get(int index)- возвращает элемент по индексу. При неверном индексе -IndexOutOfBoundsException.E set(int index, E element)- заменяет элемент по индексу, возвращает прежнее значение.E remove(int index)- удаляет элемент по индексу и возвращает его; может вызватьIndexOutOfBoundsException.boolean remove(Object o)- удаляет первое вхождение объекта, возвращаетtrue, если было удаление.int size()- возвращает число элементов.boolean isEmpty()- возвращаетtrue, если пуст.boolean contains(Object o)- проверяет наличие объекта (использует equals).int indexOf(Object o),int lastIndexOf(Object o)- индекс первого и последнего вхождения или -1.Iterator<E> iterator()иListIterator<E> listIterator()- итераторы; при структурном изменении во время обхода (без использования итератора) возможенConcurrentModificationException.List<E> subList(int fromIndex, int toIndex)- представление части списка; изменения в подсписке отражаются в исходном списке и наоборот.Object[] toArray()и<T> T[] toArray(T[] a)- конвертация в массив.- Default-методы в интерфейсе (в зависимости от версии JVM):
removeIf(Predicate<? super E> filter),replaceAll(UnaryOperator<E> operator),sort(Comparator<? super E> c).
Особенности реализации: ArrayList обеспечивает быстрый произвольный доступ (O(1) для get), LinkedList - эффективные операции вставки/удаления в середине при наличии итератора. Некоторые списки - фиксированного размера (результат Arrays.asList) или неизменяемые (результат List.of или Collections.unmodifiableList), тогда модифицирующие операции бросают UnsupportedOperationException.
Короткие примеры использования List
Создание и базовые операции:
import java.util.*;
class Example1 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add(1, "inserted");
System.out.println(list); // вывод
System.out.println(list.get(2));
}
}
[one, inserted, two] two
Итерация и удаление через Iterator:
import java.util.*;
class Example2 {
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>(Arrays.asList(1,2,3,4));
Iterator<Integer> it = nums.iterator();
while (it.hasNext()) {
Integer n = it.next();
if (n % 2 == 0) it.remove();
}
System.out.println(nums);
}
}
[1, 3]
Immutable list через List.of и поведение при модификации:
import java.util.*;
class Example3 {
public static void main(String[] args) {
List<String> imm = List.of("a", "b");
System.out.println(imm);
// imm.add("c"); // бросит исключение
}
}
[a, b]
subList как представление той же области памяти:
import java.util.*;
class Example4 {
public static void main(String[] args) {
List<String> src = new ArrayList<>(Arrays.asList("x","y","z","w"));
List<String> sub = src.subList(1, 3); // [y, z]
sub.set(0, "Y");
System.out.println(src);
}
}
[x, Y, z, w]
Похожие структуры в Java и их особенности
- ArrayList - динамический массив; быстрый доступ по индексу, амортизированная вставка в конец. Подходит для частого чтения и редкой вставки/удаления в середине.
- LinkedList - двусвязный список; эффективные операции добавления/удаления в середине через итератор, медленный произвольный доступ.
- Vector - как ArrayList, но синхронизирован; устаревший вариант, чаще заменяется коллекциями из java.util.concurrent или Collections.synchronizedList.
- CopyOnWriteArrayList - потокобезопасный, копирует внутренний массив при модификации; эффективен для сценариев «много читателей, мало писателей».
- Collections.unmodifiableList и List.of - создают неизменяемые представления; подходят для передачи данных, когда изменение нежелательно.
Выбор основывается на характерной нагрузке: при частом доступе по индексу - ArrayList, при частых вставках/удалениях в середине - LinkedList, при многопоточном чтении - CopyOnWriteArrayList или синхронизация.
Аналоги List в других языках и отличия
- Python - built-in
list. Динамический массив с похожими операциями. Отличие: синтаксис, duck-typing, встроенные методыappend,pop, срезы. Пример:lst = [1, 2, 3] lst.append(4) print(lst)[1, 2, 3, 4]
- JavaScript -
Array. Динамическая коллекция, методыpush/splice/slice. Пример:let a = ["a","b"]; a.push("c"); console.log(a);[ 'a', 'b', 'c' ]
- PHP - массивы (array) используются как списки и ассоциативные массивы. Пример:
$a = [1,2,3]; $a[] = 4; print_r($a);Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 )
- C# -
List<T>в System.Collections.Generic; очень похоже на Java List/ArrayList. Пример:using System; using System.Collections.Generic; class P{ static void Main(){ var l = new List<int>{1,2}; l.Add(3); Console.WriteLine(string.Join(",",l)); }}1,2,3
- Go - срезы (slices) служат списками. Отличие: внутреннее представление и способы расширения через append. Пример:
package main import "fmt" func main(){ s := []int{1,2}; s = append(s,3); fmt.Println(s) }[1 2 3]
- Kotlin -
ListиMutableList; interop с Java коллекциями, но отличия в null-safety и расширенных функциях. Пример:fun main(){ val l = mutableListOf("a","b"); l.add("c"); println(l) }[a, b, c]
- Lua - таблицы как универсальные структуры, используются как массивы. Пример:
t = {1,2,3} table.insert(t,4) for i,v in ipairs(t) do print(v) end1 2 3 4
- SQL - реляционные таблицы не являются списком в памяти, но последовательности хранятся в результирующих наборах; операции отличаются семантикой и масштабом.
Краткое отличие от Java: управление типами компиляцией, модель памяти и готовые методы API. В Java строгие generics и набор реализаций в JDK; в динамических языках синтаксис короче, но меньше гарантий типов во время компиляции.
Типичные ошибки при работе с List
- IndexOutOfBoundsException - при обращении к несуществующему индексу. Пример:
import java.util.*; class E1{ public static void main(String[] a){ List<String> l = List.of("x"); System.out.println(l.get(1)); }}Exception in thread "main" java.lang.IndexOutOfBoundsException: Index 1 out of bounds for length 1 at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64) ...
- UnsupportedOperationException - попытка изменить неизменяемый или фиксированного размера список (например, результат
List.ofилиArrays.asList). Пример:import java.util.*; class E2{ public static void main(String[] a){ List<String> l = Arrays.asList("a","b"); l.add("c"); }}Exception in thread "main" java.lang.UnsupportedOperationException at java.base/java.util.AbstractList.add(AbstractList.java:153) ...
- ConcurrentModificationException - изменение коллекции вне итератора при обходе. Пример:
import java.util.*; class E3{ public static void main(String[] a){ List<Integer> l = new ArrayList<>(Arrays.asList(1,2,3)); for(Integer x: l) { if (x==2) l.remove(x); } }}Exception in thread "main" java.util.ConcurrentModificationException at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013) ...
- NullPointerException - возможен при использовании
nullс частями API (например в Map ключом), или при работе с методами, ожидающими non-null. Многие реализации List допускают null, но некоторая логика (например, сравнения) может дать NPE. - ClassCastException - при использовании сырых типов или при приведения элементов к неверному типу.
Изменения API List в последних версиях Java
Ключевые изменения, актуальные для современной экосистемы:
- Java 8: добавлены default-методы, такие как
removeIf,replaceAll,sort, что упростило манипуляции над списками прямо в интерфейсе коллекции. - Java 9: введены фабричные статические методы
List.ofдля быстрого создания неизменяемых списков и другие фабрики коллекций. - Более поздние версии добавили методы для удобного копирования/создания неизменяемых копий коллекций (например,
List.copyOf) и интеграцию с новыми возможностями языка. Также улучшена совместимость с потоками и API стримов.
В целом эволюция шла в сторону расширения API через default-методы, добавления фабрик неизменяемых коллекций и упрощения преобразования в потоки/массивы.
Расширенные и нестандартные примеры использования List
1) subList как «вид» исходного списка - поведение и подводные камни:
import java.util.*;
class Adv1{
public static void main(String[] args){
List<String> base = new ArrayList<><>(Arrays.asList("a","b","c","d"));
List<String> view = base.subList(1, 3); // [b,c]
view.clear(); // удаляет элементы и из base
System.out.println(base);
}
}
[a, d]
Комментарий: subList не создает отдельного массива, поэтому нужно осторожно модифицировать оба интерфейса, особенно при параллельном доступе.
2) Использование ListIterator для модификации во время обхода (без ConcurrentModificationException):
import java.util.*;
class Adv2{
public static void main(String[] args){
List<Integer> l = new ArrayList<><>(Arrays.asList(1,2,3));
ListIterator<Integer> it = l.listIterator();
while(it.hasNext()){
int v = it.next();
if (v == 2) it.add(20); // безопасная вставка в итераторе
}
System.out.println(l);
}
}
[1, 2, 20, 3]
3) Потокобезопасность: Collections.synchronizedList и CopyOnWriteArrayList:
import java.util.*;
import java.util.concurrent.*;
class Adv3{
public static void main(String[] args){
List<String> sync = Collections.synchronizedList(new ArrayList<><>());
List<String> cow = new CopyOnWriteArrayList<>();
sync.add("a"); cow.add("a");
// при итерации по sync нужен внешний монитор
synchronized(sync){ for(String s: sync) System.out.println(s); }
}
}
a
4) Преобразование в массив с указанием типа (рекомендуется в современных версиях):
import java.util.*;
class Adv4{
public static void main(String[] args){
List<String> l = Arrays.asList("x","y");
String[] arr = l.toArray(new String[0]);
System.out.println(java.util.Arrays.toString(arr));
}
}
[x, y]
5) Использование Stream API и Collectors для создания списков различных типов, включая неизменяемые:
import java.util.*;
import java.util.stream.*;
class Adv5{
public static void main(String[] args){
List<Integer> l = IntStream.range(0,5).boxed().collect(Collectors.toList());
List<Integer> unmod = l.stream().filter(n->n%2==0).collect(Collectors.toUnmodifiableList());
System.out.println(l);
System.out.println(unmod);
}
}
[0, 1, 2, 3, 4] [0, 2, 4]
6) Сортировка и стабильность: list.sort(Comparator) поддерживает стабильную сортировку (сохранение порядка равных элементов) в стандартных реализациях.
7) Удаление по условию и сложные фильтрации:
import java.util.*;
class Adv6{
public static void main(String[] args){
List<String> names = new ArrayList<>(Arrays.asList("Anna","Bob","Alice","Tom"));
names.removeIf(s->s.length() <= 3);
System.out.println(names);
}
}
[Anna, Alice]
8) Пример создания неизменяемой копии и сравнение ссылок:
import java.util.*;
class Adv7{
public static void main(String[] args){
List<String> src = new ArrayList<>(Arrays.asList("x","y"));
List<String> copy = List.copyOf(src); // неизменяемая копия
System.out.println(copy);
// copy.add("z"); // UnsupportedOperationException
}
}
[x, y]
Примечание: некоторые методы поведения зависят от версии JDK и выбранной реализации списка.