Правильный способ перебора набора при отслеживании индекса

avatar
Will Kanga
9 августа 2021 в 06:45
101
4
0

Я хочу по существу отобразить набор в массив после вызова метода someMethod для каждого элемента. Должен ли я выполнять цикл, используя цикл foreach и переменную «i» снаружи, или использовать обычный цикл for и итератор?

int[] arr = new int[set.size()];
int i = 0;
for(int ele : set) {
  arr[i] = someMethod(ele);
  i++;
}

или

int[] arr = new int[set.size()];
Iterator<Integer> iterator = set.iterator();
for(int i=0; i<set.size(); i++) {
  arr[i] = someMethod(iterator.next().intValue());
}
Источник

Ответы (4)

avatar
Basil Bourque
9 августа 2021 в 07:43
1

tl;dr

Оба ваших цикла эквивалентны.

Вы можете использовать потоки для создания однострочника, где поток выполняет за вас цикл.

int[] arrayOfInts =
        Set
                .of( 1 , 2 , 3 )
                .stream()
                .mapToInt( Integer :: intValue )
                .map( integer -> Math.multiplyExact( integer , integer ) )
                .sorted()
                .toArray();

arrayOfInts = [1, 4, 9]

Любая петля подходит

Оба ваших контура работают хорошо.

Выберите то, что проще всего прочитать и понять. Для меня это обычно будет синтаксис for-each, как показано в вашем первом цикле.

Кстати, первую петлю можно было бы укоротить. Синтаксис i++ извлекает и использует значение i до приращения. Таким образом, вы можете вложить i++ в свой метод доступа к индексу массива.

Set < Integer > set = Set.of( 1 , 2 , 3 );
int[] arr = new int[ set.size() ];
int i = 0;
for ( int element : set ) {
    arr[ i++ ] = Math.multiplyExact( element , element );  // Auto-boxing converts `Integer` objects into `int` primitive values.
}

System.out.println( "arr = " + Arrays.toString( arr ) );

обр = [1, 9, 4]

Поток вместо цикла

Ответ Пранава Чоудхари правильный. Но вы также упомянули о желании применить метод для изменения вашего номера перед назначением в массив.

Потоки Java упрощают выполнение такой модификации во время сбора данных.

Определить наши Set из Integer объектов.

Set < Integer > set = Set.of( 1 , 2 , 3 );

Создайте поток примитивов int, упакованных из наших объектов Integer.

IntStream intStream = set.stream().mapToInt( Integer :: intValue );

Примените вашу модификацию. Здесь мы возводим в квадрат каждое число. The Math.multiplyExact method is handy because it will throw an ArithmeticException if we overflow the limits of the 32-bit int type. Мы собираем каждый полученный квадрат как int в нашем массиве.

int[] arrayOfInts = intStream.map( integer -> Math.multiplyExact( integer , integer ) ).toArray();

Дамп на консоль.

System.out.println("arrayOfInts = " + Arrays.toString(arrayOfInts));

При запуске.

arrayOfInts = [9, 4, 1]

Обратите внимание на порядок вывода. A Set по определению не имеет определенного порядка итерации. Чтобы подчеркнуть это, метод Set.of оставляет за собой право использовать реализацию Set, которая случайным образом меняет свою итерацию. Таким образом, каждый раз, когда вы запускаете этот код, вы можете видеть другой порядок результатов.

Если вы заботитесь о порядке, добавьте его в свою потоковую работу. Добавить вызов на sorted().

int[] arrayOfInts = intStream.map( integer -> Math.multiplyExact( integer , integer ) ).sorted().toArray();

При запуске мы получаем постоянный порядок. См. этот код, запущенный в реальном времени на IdeOne.com.

arrayOfInts = [1, 4, 9]

Не то чтобы я это рекомендовал, но мы могли бы превратить этот код в однострочный.

int[] arrayOfInts = Set.of( 1 , 2 , 3 ).stream().mapToInt( Integer :: intValue ).map( integer -> Math.multiplyExact( integer , integer ) ).sorted().toArray();

Или:

int[] arrayOfInts =
        Set
                .of( 1 , 2 , 3 )
                .stream()
                .mapToInt( Integer :: intValue )
                .map( integer -> Math.multiplyExact( integer , integer ) )
                .sorted()
                .toArray();

Посмотрите этот код на IdeOne.com.

arrayOfInts = [1, 4, 9]

avatar
Stephen C
9 августа 2021 в 07:35
1

Обе версии правильны в том смысле, что обе они дают одинаковый результат.

Однако, на мой взгляд, первая версия предпочтительнее из двух.

  • Среднему Java-программисту его легче понять.
  • Это более лаконично. Меньше строк кода. Более простые операторы.
  • Вероятно, это будет быстрее... если только оптимизатор не будет удивительно умен.

Если бы это был я, я бы сохранил строку и написал бы так:

for(int ele : set) {
  arr[i++] = someMethod(ele);
}

но это просто просвечиваются мои старые привычки C.


Как указывали другие, в Java 8+ есть еще более краткий способ написать это; например см. этот ответ.

avatar
Pranav Choudhary
9 августа 2021 в 06:55
2

Оба метода одинаково хороши, но цикл for немного быстрее, чем цикл forEach,

Существуют и другие методы преобразования набора в массив, приведенные ниже. Вы можете использовать функцию toArray():

    int[] arr = new int[set.size()];
    arr = set.toArray(arr);

или вы можете использовать поток в Java 8 или выше:

  int[] arr = set.stream().toArray(int[] ::new);

Другой метод заключается в использовании Arrays.copyOf

int[] arr = Arrays.copyOf(set.toArray(), set.size(), Integer[].class);
Stephen C
9 августа 2021 в 07:30
0

Хотя цикл for немного быстрее, чем эквивалентный для каждого цикла, этот цикл for не эквивалентен простому циклу for each. Если вы посмотрите внимательно, версия цикла for одновременно увеличивает переменную и повторяет итератор. Это медленнее, чем простое for и простое для каждого.

tostao
10 августа 2021 в 19:07
0

но это не то, что ищет OP: after calling someMethod on each element.

avatar
tostao
9 августа 2021 в 06:50
0

С java8:

    Integer[] arr = set.stream().map(element -> doSomething(element)).toArray(Integer[]::new);

Где doSomething() ваш метод, который вы можете вызвать -

вызов метода someMethod для каждого элемента.