Set.contains: примеры (JAVA)
Set.contains(Object o): booleanОбщее описание метода
В Java метод Set.contains(Object) проверяет, содержится ли указанный объект в коллекции, которая реализует интерфейс java.util.Set. Метод определяется в интерфейсе Collection и возвращает логическое значение: true, если множествo содержит объект, и false в противном случае.
Поддерживаемые аргументы и возвращаемое значение:
- Аргумент: один параметр типа
Object. Может бытьnullдля реализаций, которые допускаютnull(например,HashSet), и недопустим для других (например,TreeSetпри отсутствии соответствующего компаратора, обрабатывающегоnull). - Возвращаемое значение:
boolean-trueпри наличии элемента, иначеfalse.
Модель сравнения зависит от реализации множества:
- HashSet использует
equals()иhashCode()для определения соответствия. Для корректной работы требуется согласованная реализация этих методов в классе элементов. - TreeSet использует либо предоставленный компаратор, либо естественный порядок (
Comparable) для сравнения элементов. В этом случае сравнение не опирается наequals()напрямую, а на сравнение черезcompareToилиComparator. - LinkedHashSet наследует поведение
HashSetпо сравнению, но сохраняет порядок вставки.
Сложность операции обычно:
- HashSet: ожидаемая амортизированная временная сложность O(1).
- TreeSet / ConcurrentSkipListSet: O(log n).
Особенности при работе с параллельными коллекциями: у множеств на основе структуры ConcurrentHashMap (например, ConcurrentHashMap.newKeySet()) метод contains является потокобезопасным и отражает текущее состояние в момент вызова, но не гарантирует стабильность при одновременных модификациях.
Короткие примеры использования
Пример 1. HashSet с разрешением null и проверками:
import java.util.*;
class Test1 {
public static void main(String[] args) {
Set s = new HashSet<>();
s.add("a");
s.add(null);
System.out.println(s.contains("a"));
System.out.println(s.contains("b"));
System.out.println(s.contains(null));
}
}
true false true
Пример 2. TreeSet и проверка с null (ожидается исключение):
import java.util.*;
class Test2 {
public static void main(String[] args) {
Set s = new TreeSet<>();
s.add(1);
s.add(2);
try {
System.out.println(s.contains(null));
} catch (Exception ex) {
System.out.println(ex.getClass().getSimpleName()+": "+ex.getMessage());
}
}
}
NullPointerException: null
Пример 3. Пользовательский объект: работа equals и hashCode:
import java.util.*;
class Person {
final int id;
Person(int id){this.id=id;}
public boolean equals(Object o){
return o instanceof Person && ((Person)o).id==id;
}
public int hashCode(){return Integer.hashCode(id);}
}
class Test3{
public static void main(String[] args){
Set s = new HashSet<>();
s.add(new Person(1));
System.out.println(s.contains(new Person(1))); // новый объект с тем же id
}
}
true
Пример 4. Проверка наличия через view от Map (keySet):
import java.util.*;
class Test4{
public static void main(String[] args){
Map m = new HashMap<>();
m.put("x",10);
Set keys = m.keySet();
System.out.println(keys.contains("x"));
}
}
true
Похожие методы в Java
Несколько близких по назначению методов и их особенности:
- Collection.containsAll(Collection) - проверяет, содержатся ли все элементы переданной коллекции. Полезно при проверке подмножества.
- Map.containsKey(Object) - для ассоциативных структур проверка ключа в мапе; семантически похожа на
keySet().contains(), но иногда быстрее, так как реализована внутри Map. - List.contains(Object) - аналогично, но у списков поиск может быть O(n). Для отсортированных списков предпочтительнее
Collections.binarySearch. - Arrays.binarySearch - для отсортированных массивов даёт более быстрый поиск (O(log n)), но требует поддержания порядка и корректного сравнения.
Выбор зависит от требований к производительности и структуре данных: для уникальных элементов и быстрых проверок предпочтительнее хеш-основанные множества, для упорядоченных наборов - деревья.
Аналоги в других языках и отличия
Краткие эквиваленты и нюансы в популярных языках:
- JavaScript:
Set.prototype.has(value). Пример:const s = new Set(['a']); console.log(s.has('a')); console.log(s.has('b'));true false
- Python: оператор
inилиset.__contains__. Пример:s = {'a'} print('a' in s) print('b' in s)True False
Отличие: для пользовательских объектов требуется реализация__hash__и__eq__для корректной работы с хеш-основанными множествами. - PHP: в массивах используется
in_array(), но это O(n). Для объектов можно использоватьSPLструктуры вродеSplObjectStorage. Пример:$a = ['x']; var_dump(in_array('x', $a)); var_dump(in_array('y', $a));bool(true) bool(false)
- C#:
HashSet<T>.Contains(T). Семантика совпадает с Java HashSet; требуется корректныйEqualsиGetHashCode. - Go: отсутствие встроенного Set; идиома через map[T]struct{} и проверка наличия через _, ok:
m := map[string]struct{}{"a":{}} _, ok := m["a"] fmt.Println(ok) _, ok = m["b"] fmt.Println(ok)true false
- Lua: обычная таблица с ключами; проверка
if t[val] then. Отличие: отсутствует выделенная структура множества. - Kotlin: метод
containsуSetи операторin. Поведение совпадает с Java для JVM-реализаций. - SQL: логика соответствия выражается через
EXISTSилиINв запросах, что по смыслу проверяет наличие записи в наборе результатов.
Типичные ошибки и их проявления
Частые ошибки при использовании Set.contains и примеры:
- Отсутствие корректных equals/hashCode: для хеш-основанных множеств объекты считаются разными, даже при одинаковых полях.
import java.util.*; class Bad { int id; Bad(int id){this.id=id;} } class E1{public static void main(String[] args){ Sets = new HashSet<>(); s.add(new Bad(1)); System.out.println(s.contains(new Bad(1))); }} false
- ClassCastException в TreeSet: добавление разных по типу элементов или наличие несоответствующего компаратора.
import java.util.*; class E2{public static void main(String[] args){ Set s = new TreeSet(); s.add("a"); s.add(1); // на этапе сравнения при contains или add может возникнуть исключение System.out.println(s.contains(1)); }}Exception in thread "main" java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer
- NullPointerException для сортированных множеств: вызов
contains(null)вTreeSetбез поддержкиnullкомпаратором.// как в примере TreeSet вышеNullPointerException: null
- Ожидание побочных эффектов: метод только читает; при параллельных модификациях результат может устареть, но сам вызов не изменяет структуру.
- Путаница между contains и containsAll: проверка одного элемента вместо набора или наоборот приводит к логической ошибке.
Изменения в API и поведении
Метод contains как часть интерфейса Collection сохраняет прежнюю сигнатуру с первых версий Java и не претерпевал изменений. Начиная с Java 9 появились фабричные методы создания множеств (Set.of), которые возвращают иммутабельные реализации; для них contains работает как обычно, но модифицировать такие множества нельзя (вызов add приведет к UnsupportedOperationException).
Производительность и внутренние структуры улучшались в реализации JDK (оптимизации HashMap, ConcurrentHashMap), однако семантика contains для стандартных реализаций осталась прежней.
Расширенные и редкие сценарии использования
Пример A. Identity-based множество (сравнение по ссылке) и отличие от стандартного HashSet:
import java.util.*;
class IdentityExample{
public static void main(String[] args){
Set hash = new HashSet<>();
Set identity = Collections.newSetFromMap(new IdentityHashMap<>());
String a = new String("x");
String b = new String("x");
hash.add(a);
identity.add(a);
System.out.println(hash.contains(b)); // true, equals() у String
System.out.println(identity.contains(b)); // false, т.к. сравнение по ==
}
}
true false
Пояснение: IdentityHashMap использует ссылочное сравнение, поэтому contains отражает поведение ссылочной семантики.
Пример B. Параллельное множество через ConcurrentHashMap.newKeySet и видимость:
import java.util.*;
import java.util.concurrent.*;
class Concur{
public static void main(String[] args) throws Exception{
Set s = ConcurrentHashMap.newKeySet();
s.add(1);
Runnable reader = () -> {
for(int i=0;i<3;i++) System.out.println(s.contains(i));
};
new Thread(reader).start();
}
}
true false false
Пояснение: реализация потокобезопасна и отражает актуальное состояние при вызове, но без внешних гарантий консистентности между последовательными вызовами.
Пример C. contains для подмножеств и views: проверка на view от Map или результат фильтрации:
import java.util.*;
class ViewExample{
public static void main(String[] args){
Map m = new HashMap<>();
m.put("a",1);
Set keys = m.keySet();
System.out.println(keys.contains("a"));
m.remove("a");
System.out.println(keys.contains("a"));
}
}
true false
Пояснение: view отражает текущее состояние исходной Map; contains на view соответствует containsKey у Map.
Пример D. Поиск по предикату используя Stream в сочетании с contains для базовой проверки:
import java.util.*;
class StreamCheck{
public static void main(String[] args){
Set s = new HashSet<>(Arrays.asList("one","two","three"));
boolean anyWithT = s.stream().anyMatch(x -> x.startsWith("t"));
System.out.println(anyWithT);
// contains применяется только при точном совпадении
System.out.println(s.contains("two"));
}
}
true true
Пояснение: для поиска по свойствам элементов используется stream; contains служит для точного совпадения.