Add: примеры (JAVA)

Добавление элементов и особенности метода add
Раздел: Коллекции (Collection Framework) - List
add(E e): boolean

Общее описание метода add

В Java под названием add встречается набор одноимённых методов в разных классах и интерфейсах. Наиболее часто употребляемый - Collection.add(E), который добавляет элемент в коллекцию. Поведение и сигнатуры различаются в зависимости от реализации:

  • Collection.add(E element) - добавляет элемент в коллекцию. Возвращает boolean: true, если коллекция изменилась в результате операции, false для некоторых реализаций (например, Set, когда элемент уже присутствует).
  • List.add(int index, E element) - вставляет элемент по индексу. Сигнатура возвращает void; при неправильном индексе возникает IndexOutOfBoundsException.
  • Set.add(E element) - спецификация Collection.add для множеств. Возвращает true, если элемент добавлен; false, если элемент уже присутствует (сравнение через equals и/или сравнение для отсортированных множеств).
  • Queue.add(E element) - добавление в очередь. В отличие от offer, бросает IllegalStateException при переполнении ограниченной очереди.
  • BigInteger.add(BigInteger val) - арифметическая операция сложения больших целых, возвращает новый BigInteger.
  • AtomicInteger.addAndGet(int delta) и LongAdder.add(long x) - атомарные операции для многопоточной среды, возвращающие итоговое или не возвращающие значение в зависимости от API.
  • Math.addExact(int x, int y) - сложение с контролем переполнения; при переполнении бросает ArithmeticException.

Частые исключения и результаты:

  • UnsupportedOperationException - когда коллекция неизменяема (например, списки через List.of(...) или Arrays.asList(...) у фиксированного размера).
  • IndexOutOfBoundsException - для List.add(index,...) при неверном индексе.
  • IllegalStateException - для Queue.add при переполнении ограниченной очереди.
  • NullPointerException - в некоторых реализациях коллекций, если разрешение null запрещено (например, ConcurrentHashMap и некоторые другие структуры).

Таким образом, при работе с add важно ориентироваться на конкретный класс или интерфейс: сигнатуры, возвращаемое значение и возможные исключения могут существенно отличаться.

Короткие примеры использования

Добавление в ArrayList - возвращаемое значение у add(E) обычно true (имплементация изменяется).

import java.util.*;
class Ex1 {
  public static void main(String[] args) {
    List list = new ArrayList<>();
    boolean r = list.add("one");
    System.out.println(r);
    System.out.println(list);
  }
}
true
[one]

Добавление в HashSet и попытка добавить дубликат - видно поведение возвращаемого значения.

import java.util.*;
class Ex2 {
  public static void main(String[] args) {
    Set s = new HashSet<>();
    System.out.println(s.add("a"));
    System.out.println(s.add("a"));
    System.out.println(s);
  }
}
true
false
[a]

Вставка по индексу в список. Метод возвращает void, после вызова список изменяется.

import java.util.*;
class Ex3 {
  public static void main(String[] args) {
    List l = new ArrayList<>();
    l.add("x");
    l.add(0, "y");
    System.out.println(l);
  }
}
[y, x]

Queue.add против offer: при переполнении ограниченной очереди add бросит исключение.

import java.util.concurrent.ArrayBlockingQueue;
class Ex4 {
  public static void main(String[] args) {
    ArrayBlockingQueue q = new ArrayBlockingQueue<>(1);
    q.add(1);
    System.out.println(q);
    q.add(2); // вызовет IllegalStateException
  }
}
[1]
// Исключение:
// Exception in thread "main" java.lang.IllegalStateException: Queue full

BigInteger.add - арифметика больших чисел возвращает новый объект.

import java.math.BigInteger;
class Ex5 {
  public static void main(String[] args) {
    BigInteger a = new BigInteger("12345678901234567890");
    BigInteger b = new BigInteger("10");
    System.out.println(a.add(b));
  }
}
12345678901234567900

Math.addExact обнаруживает переполнение.

class Ex6 {
  public static void main(String[] args) {
    int x = Integer.MAX_VALUE;
    System.out.println(x);
    System.out.println(Math.addExact(x, 1));
  }
}
2147483647
// Exception in thread "main" java.lang.ArithmeticException: integer overflow

Похожие методы в Java

  • addAll(Collection) - массовое добавление элементов; предпочтительнее при переносе сразу нескольких элементов ради производительности и читаемости.
  • put / putIfAbsent у Map - для ассоциативных структур; Map не имеет add, поэтому для пар ключ-значение применяется put.
  • offer у очередей - похожа на add, но при переполнении возвращает false вместо исключения; предпочтительна для мягкой обработки переполнения.
  • append у StringBuilder - добавление текстовых данных в строковый буфер; отличается по назначению от коллекций.

Выбор между ними определяется задачей: добавление множества элементов - addAll, работа с парами ключ-значение - put, безопасное добавление в очередь без исключений - offer.

Аналоги в других языках

  • PHP: array_push($arr, $val) - добавляет в конец массива. Результат возвращает новую длину массива.
  • <?
    $arr = [1];
    $r = array_push($arr, 2);
    var_dump($r);
    var_dump($arr);
    ?>
    int(2)
    array(2) { [0]=> int(1) [1]=> int(2) }
  • JavaScript: Array.prototype.push - добавляет элементы, возвращает новая длина массива.
  • let a = [1];
    let r = a.push(2);
    console.log(r);
    console.log(a);
    2
    [1, 2]
  • Python: list.append(x) - добавление в конец, возвращает None; list.insert(i, x) - вставка по индексу; set.add(x) - добавление в множество, результат не возвращается.
  • l = [1]
    print(l.append(2))
    print(l)
    
    s = set()
    print(s.add(1))
    print(s)
    None
    [1, 2]
    None
    {1}
  • SQL: оператор INSERT - добавление строк в таблицу. Различие: операция долговременная и влияет на хранимые данные, возвращает статус выполнения, иногда идентификатор вставленной строки.
  • C#: List<T>.Add(item) - добавляет и не возвращает значение; HashSet<T>.Add(item) возвращает bool (true если добавлен).
  • using System;
    using System.Collections.Generic;
    class P{ static void Main(){ var l=new List(); l.Add(1); var s=new HashSet(); Console.WriteLine(s.Add(1)); Console.WriteLine(s.Add(1)); }}
    
    True
    False
  • Go: функция append(slice, elems...) - возвращает новый срез; семантика отличается от методов коллекций Java, так как срезы в Go являются значениями.
  • package main
    import "fmt"
    func main(){ s:=[]int{1}
    s=append(s,2)
    fmt.Println(s) }
    
    [1 2]
  • Kotlin: MutableList.add и MutableSet.add похожи на Java; у Kotlin также есть оператор += для коллекций (вызов add под капотом).

Ключевое отличие между языками - возвращаемые значения и семантика ошибок: в Java некоторые реализации возвращают булево значение, в Python и C# методы для списка часто ничего не возвращают, в JavaScript и PHP возвращается новая длина.

Типичные ошибки при применении add

  • Попытка изменить неизменяемую коллекцию: вызов add у списков, созданных через List.of(...) или у фиксированного списка из Arrays.asList, приводит к UnsupportedOperationException.
  • Ошибка индекса при List.add(index, element): неверный индекс вызывает IndexOutOfBoundsException.
  • Различие в возвращаемом значении: ожидание булевого результата от List.add(index,...), хотя этот метод возвращает void.
  • ConcurrentModificationException при изменении коллекции внутри итератора с использованием методов самой коллекции вместо итератора.
  • Игнорирование поведения Queue.add при переполнении - ожидание false, тогда как бросается исключение; для безопасной обработки лучше offer.

Пример ConcurrentModificationException.

import java.util.*;
class Err1 {
  public static void main(String[] args) {
    List l = new ArrayList<>(Arrays.asList(1,2,3));
    for (Integer x : l) {
      if (x == 2) l.add(4); // изменение коллекции во время обхода
    }
  }
}
// Exception in thread "main" java.util.ConcurrentModificationException

Пример UnsupportedOperationException при попытке добавить в неизменяемый список.

import java.util.*;
class Err2 { public static void main(String[] args) {
  List l = List.of("a","b");
  l.add("c");
}}
// Exception in thread "main" java.lang.UnsupportedOperationException

Изменения и замечания по версиям Java

  • Непосредственно сигнатуры add в основных коллекциях остаются стабильными на протяжении многих версий Java.
  • Java 8 представила новые удобства в API параллельных структур (например, LongAdder для эффективного суммирования в многопоточности), что расширило спектр методов добавления для атомарных операций.
  • В Java 9 появились фабричные методы List.of, Set.of, Map.of, создающие неизменяемые коллекции - попытка вызвать add приведет к UnsupportedOperationException. Это изменение влияет на код, ожидающий модифицируемые коллекции.
  • В API арифметики для обнаружения переполнения добавлены Math.addExact и аналогичные методы (в Java 8), что обеспечивает безопасную проверку при сложении примитивов.

Расширенные и нестандартные примеры

Атомарное добавление с использованием AtomicInteger.addAndGet в многопоточном контексте.

Пример java
import java.util.concurrent.atomic.AtomicInteger;
class Adv1 {
  public static void main(String[] args) throws Exception{
    AtomicInteger ai = new AtomicInteger(0);
    Thread t1 = new Thread(() -> { for(int i=0;i<1000;i++) ai.addAndGet(1); });
    Thread t2 = new Thread(() -> { for(int i=0;i<1000;i++) ai.addAndGet(1); });
    t1.start(); t2.start();
    t1.join(); t2.join();
    System.out.println(ai.get());
  }
}
2000

Использование Math.addExact для обнаружения переполнения в вычислениях, где важна корректность результата.

Пример java
class Adv2 { public static int safeSum(int a,int b){
  return Math.addExact(a,b);
}
public static void main(String[] args){
  try{ System.out.println(safeSum(Integer.MAX_VALUE, 10)); }
  catch(RuntimeException e){ System.out.println(e.getClass().getSimpleName()); }
}}
ArithmeticException

TreeSet и нестандартный компаратор: пример, где сравнение влияет на возможность добавления элементов (различие equals и compare может привести к неожиданным результатам).

Пример java
import java.util.*;
class Adv3 {
  public static void main(String[] args) {
    Comparator cmp = (a,b) -> Integer.compare(a.length(), b.length());
    SortedSet ts = new TreeSet<>(cmp);
    ts.add("one");
    System.out.println(ts.add("two"));   // length 3 -> считаются равными по компаратору
    System.out.println(ts);
  }
}
false
[one]

Стратегия добавления большого количества элементов: addAll часто эффективнее многократных add из-за внутренних оптимизаций у конкретных реализаций (например, резервирование места в ArrayList).

Пример java
import java.util.*;
class Adv4 { public static void main(String[] args){
  List target = new ArrayList<>();
  List source = new ArrayList<>();
  for(int i=0;i<10000;i++) source.add(i);
  target.addAll(source);
  System.out.println(target.size());
}}
10000

Создание собственной коллекции с контролируемым поведением add - пример переопределения, когда требуется валидация или фильтрация перед добавлением.

Пример java
import java.util.*;
class ValidatingList extends ArrayList {
  @Override
  public boolean add(E e) {
    if (e == null) throw new NullPointerException("null not allowed");
    return super.add(e);
  }
}
class Adv5 { public static void main(String[] args){
  ValidatingList vl = new ValidatingList<>();
  vl.add("ok");
  System.out.println(vl);
}}
[ok]

джава add function comments

En
Add Appends the specified element to the end of this list