Перейти к основному содержимому

Итерация по набору в Java

· 3 мин. чтения

Задача: Наибольшая подстрока без повторений

Для заданной строки s, найдите длину наибольшей подстроки без повторяющихся символов. Подстрока — это непрерывная непустая последовательность символов внутри строки...

ANDROMEDA 42

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 .