1. Введение
Перебор элементов — одна из самых фундаментальных операций, которые мы можем выполнять с коллекцией.
В этом руководстве мы рассмотрим, как перебирать элементы набора
и чем он отличается от аналогичных задач в списке
или массиве.
2. Получение доступа к элементам в наборе
Набор ,
в отличие от списка
и многих других коллекций, не является последовательным. Их элементы не индексируются, и в зависимости от реализации они могут не поддерживать порядок.
Это означает, что мы не можем спросить о конкретном элементе множества по его номеру. Из-за этого мы не можем использовать типичный цикл for
или любой другой метод, основанный на индексах.
2.1. Итератор
Самый простой и близкий к металлу метод перебора набора — это вызов метода итератора
, предоставляемого каждым набором
:
Set<String> names = Sets.newHashSet("Tom", "Jane", "Karen");
Iterator<String> namesIterator = names.iterator();
Затем мы можем использовать полученный итератор
для получения элементов этого набора
один за другим . Самый знаковый способ — проверить, есть ли у итератора
следующий элемент в цикле while
:
while(namesIterator.hasNext()) {
System.out.println(namesIterator.next());
}
Мы также можем использовать метод forEachRemaining
, добавленный в Java 8:
namesIterator.forEachRemaining(System.out::println);
Мы также можем смешивать эти решения:
String firstName = namesIterator.next(); // save first name to variable
namesIterator.forEachRemaining(System.out::println); // print rest of the names
Все остальные методы будут каким-то образом использовать итератор
под капотом.
3. Поток
с
Каждый набор
предоставляет метод spliterator()
. Из-за этого Set
можно легко преобразовать в Stream
:
names.stream().forEach(System.out::println);
Мы также можем использовать богатый Streams
API для создания более сложных конвейеров. Например, давайте сопоставим, зарегистрируем, а затем сократим элементы набора до одной строки:
String namesJoined = names.stream()
.map(String::toUpperCase)
.peek(System.out::println)
.collect(Collectors.joining());
4. Расширенный цикл
Хотя мы не можем использовать простой индексированный цикл for
для перебора Set
, мы можем использовать расширенную функцию цикла, представленную в Java 5:
for (String name : names) {
System.out.println(name);
}
5. Итерация с индексом
5.1. Преобразование в массив
Наборы
не индексируются, но мы можем добавить индекс искусственно. Одним из возможных решений является простое преобразование Set
в какую-то более доступную структуру данных, такую как массив :
Object[] namesArray = names.toArray();
for (int i = 0; i < namesArray.length; i++) {
System.out.println(i + ": " + namesArray[i]);
}
Имейте в виду, что преобразование в массив само по себе будет выполнять итерацию по набору
один раз. Итак, с точки зрения сложности, мы будем перебирать Set
дважды. Это может быть проблемой, если производительность имеет решающее значение.
5.2. Сжатие с индексом
Другой подход — создать индекс и заархивировать его с помощью нашего Set
. Хотя мы могли бы сделать это на ванильной Java, существуют библиотеки, предоставляющие инструменты именно для этого.
Например, мы можем использовать потоки Vavr:
Stream.ofAll(names)
.zipWithIndex()
.forEach(t -> System.out.println(t._2() + ": " + t._1()));
6. Резюме
В этом уроке мы рассмотрели различные способы перебора элементов экземпляра Set .
Мы изучили использование итераторов, потоков и циклов, а также различия между ними.
Как всегда, примеры доступны на GitHub .