Как перебрать Set / HashSet без итератора?


273

Как я могу перебрать Set/ HashSetбез следующего?

Iterator iter = set.iterator();
while (iter.hasNext()) {
    System.out.println(iter.next());
}

31
Вы действительно пытаетесь избежать использования итератора или просто не хотите видеть его в своем исходном коде?
Джон Скит

2
Вы можете сделать код короче и чище, но вы должны использовать итератор. Есть ли причина, по которой вы хотите этого избежать?
Питер Лоури

5
Просто для справки и в качестве объяснения, почему следует избегать итератора: я сейчас сталкиваюсь с этой проблемой в игре для Android, написанной на Java. Прямо сейчас я использую HashSet для хранения слушателей, которые должны регулярно получать уведомления о событиях. Тем не менее, многократная итерация коллекций вызывает большую активность сборщика мусора, которая может обременять игровой цикл. И это не нужно в Java-игре. Я собираюсь переписать эти части.
Тигучи

12
@thecoshman Я говорю только с точки зрения разработки игр на Java, где вы хотите избежать GC во время регулярных обновлений состояния игры любой ценой. Итераторы являются только временно полезными объектами, поскольку вы не можете сбросить их до начала, поэтому они воссоздаются при каждом вызове метода итерации (см., Например, источник ArrayList.java). При использовании в игровом цикле для итерации объектов сцены и рассмотрения не менее 30 обновлений в секунду вы получите не менее 60 (сценарий обычно повторяется дважды за цикл) объектов-итераторов в секунду, ожидающих GC. Это имеет большое влияние на Android.
Тигучи

9
Возможно, следует выбрать более адекватную структуру данных в этом случае? Набор не предназначен для эффективной итерации. Почему бы не использовать ArrayList и перебирать его со стандартным циклом for? Дополнительные итераторы не создаются, доступ для чтения очень быстрый.
stef77

Ответы:


492

Вы можете использовать расширенный цикл for :

Set<String> set = new HashSet<String>();

//populate set

for (String s : set) {
    System.out.println(s);
}

Или с Java 8:

set.forEach(System.out::println);

65
Я считаю, что это использует итератор за кулисами.
munyengm

22
@munyengm Да, это так. Невозможно выполнить итерацию по множеству без итератора, кроме доступа к базовой структуре, которая хранит данные посредством отражения, и репликации кода, предоставленного Set # iterator ...
assylias

1
Это работает, но если это может вызвать проблемы, то это не решение для меня. Я думаю, у меня нет выбора, я должен использовать Iterator. Спасибо за все в любом случае.
user1621988

39
@ user1621988 Какие проблемы? Нет проблем с кодом, который я предоставил. Это просто хороший и простой способ перебора набора без явного использования итератора.
assylias

90

Существует как минимум шесть дополнительных способов перебора набора. Мне известны следующие:

Способ 1

// Obsolete Collection
Enumeration e = new Vector(movies).elements();
while (e.hasMoreElements()) {
  System.out.println(e.nextElement());
}

Способ 2

for (String movie : movies) {
  System.out.println(movie);
}

Способ 3

String[] movieArray = movies.toArray(new String[movies.size()]);
for (int i = 0; i < movieArray.length; i++) {
  System.out.println(movieArray[i]);
}

Способ 4

// Supported in Java 8 and above
movies.stream().forEach((movie) -> {
  System.out.println(movie);
});

Способ 5

// Supported in Java 8 and above
movies.stream().forEach(movie -> System.out.println(movie));

Способ 6

// Supported in Java 8 and above
movies.stream().forEach(System.out::println);

Это то, HashSetчто я использовал для моих примеров:

Set<String> movies = new HashSet<>();
movies.add("Avatar");
movies.add("The Lord of the Rings");
movies.add("Titanic");

10
Привет @ benny-neugebauer Для метода 4 , метода 5 , метода 6 вы можете просто удалить лишние stream().
Евгений Т

5
Не говоря уже о том, что метод 4, метод 5 и метод 6 одинаковы.
GustavoCinque

24

Преобразование вашего набора в массив может также помочь вам для перебора элементов:

Object[] array = set.toArray();

for(int i=0; i<array.length; i++)
   Object o = array[i];

45
Просто для записи, toArrayвызывает итератор набора.
assylias

13

Для демонстрации рассмотрим следующий набор, содержащий различные объекты Person:

Set<Person> people = new HashSet<Person>();
people.add(new Person("Tharindu", 10));
people.add(new Person("Martin", 20));
people.add(new Person("Fowler", 30));

Модель лица

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    //TODO - getters,setters ,overridden toString & compareTo methods

}
  1. Оператор for имеет форму, предназначенную для итерации по коллекциям и массивам. Эту форму иногда называют расширенной инструкцией for, и ее можно использовать для того, чтобы сделать ваши циклы более компактными и удобными для чтения.
for(Person p:people){
  System.out.println(p.getName());
}
  1. Java 8 - java.lang.Iterable.forEach (Consumer)
people.forEach(p -> System.out.println(p.getName()));
default void forEach(Consumer<? super T> action)

Performs the given action for each element of the Iterable until all elements have been processed or the action throws an exception. Unless otherwise specified by the implementing class, actions are performed in the order of iteration (if an iteration order is specified). Exceptions thrown by the action are relayed to the caller. Implementation Requirements:

The default implementation behaves as if: 

for (T t : this)
     action.accept(t);

Parameters: action - The action to be performed for each element

Throws: NullPointerException - if the specified action is null

Since: 1.8

11

Вы можете использовать функциональную операцию для более аккуратного кода

Set<String> set = new HashSet<String>();

set.forEach((s) -> {
     System.out.println(s);
});

Для вызова требуется API 24
djdance

11

Вот несколько советов о том, как итерировать набор вместе с их выступлениями:

public class IterateSet {

    public static void main(String[] args) {

        //example Set
        Set<String> set = new HashSet<>();

        set.add("Jack");
        set.add("John");
        set.add("Joe");
        set.add("Josh");

        long startTime = System.nanoTime();
        long endTime = System.nanoTime();

        //using iterator
        System.out.println("Using Iterator");
        startTime = System.nanoTime();
        Iterator<String> setIterator = set.iterator();
        while(setIterator.hasNext()){
            System.out.println(setIterator.next());
        }
        endTime = System.nanoTime();
        long durationIterator = (endTime - startTime);


        //using lambda
        System.out.println("Using Lambda");
        startTime = System.nanoTime();
        set.forEach((s) -> System.out.println(s));
        endTime = System.nanoTime();
        long durationLambda = (endTime - startTime);


        //using Stream API
        System.out.println("Using Stream API");
        startTime = System.nanoTime();
        set.stream().forEach((s) -> System.out.println(s));
        endTime = System.nanoTime();
        long durationStreamAPI = (endTime - startTime);


        //using Split Iterator (not recommended)
        System.out.println("Using Split Iterator");
        startTime = System.nanoTime();
        Spliterator<String> splitIterator = set.spliterator();
        splitIterator.forEachRemaining((s) -> System.out.println(s));
        endTime = System.nanoTime();
        long durationSplitIterator = (endTime - startTime);


        //time calculations
        System.out.println("Iterator Duration:" + durationIterator);
        System.out.println("Lamda Duration:" + durationLambda);
        System.out.println("Stream API:" + durationStreamAPI);
        System.out.println("Split Iterator:"+ durationSplitIterator);
    }
}

Код не требует пояснений.

Результатом продолжительности являются:

Iterator Duration: 495287
Lambda Duration: 50207470
Stream Api:       2427392
Split Iterator:    567294

Мы видим, что Lambdaдубли самые длинные, а Iteratorсамые быстрые.


2

Перечисление (?):

Enumeration e = new Vector(set).elements();
while (e.hasMoreElements())
    {
        System.out.println(e.nextElement());
    }

Другой способ (java.util.Collections.enumeration ()):

for (Enumeration e1 = Collections.enumeration(set); e1.hasMoreElements();)
    {
        System.out.println(e1.nextElement());
    }

Java 8:

set.forEach(element -> System.out.println(element));

или

set.stream().forEach((elem) -> {
    System.out.println(elem);
});

0

Однако для этого уже есть очень хорошие ответы. Вот мой ответ:

1. set.stream().forEach(System.out::println); // It simply uses stream to display set values
2. set.forEach(System.out::println); // It uses Enhanced forEach to display set values

Кроме того, если этот набор имеет тип пользовательского класса, например, для клиента.

Set<Customer> setCust = new HashSet<>();
    Customer c1 = new Customer(1, "Hena", 20);
    Customer c2 = new Customer(2, "Meena", 24);
    Customer c3 = new Customer(3, "Rahul", 30);

setCust.add(c1);
setCust.add(c2);
setCust.add(c3);
    setCust.forEach((k) -> System.out.println(k.getId()+" "+k.getName()+" "+k.getAge()));

// Класс клиента:

class Customer{
private int id;
private String name;
private int age;

public Customer(int id,String name,int age){
this.id=id;
this.name=name;
this.age=age;
} // Getter, Setter methods are present.}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.