Подтвердить что ты не робот

Есть ли способ получить доступ к счетчику итераций в Java для каждого цикла?

Есть ли способ в Java для каждого цикла

for(String s : stringArray) {
  doSomethingWith(s);
}

чтобы узнать, как часто цикл уже обрабатывается?

Помимо использования старого и известного цикла for(int i=0; i < boundary; i++) - это конструкция

int i = 0;
for(String s : stringArray) {
  doSomethingWith(s);
  i++;
}

единственный способ получить такой счетчик в цикле for-each?

4b9b3361

Ответ 1

Нет, но вы можете предоставить свой счетчик.

Причина этого в том, что внутри каждого цикла внутри нет счетчика; он основан на интерфейсе Iterable, т.е. использует Iterator для прокрутки "коллекции" - это может быть не коллекция на самом деле, и на самом деле может быть чем-то совсем не на основе индексов (например, связанного списка).

Ответ 2

Есть и другой способ.

Учитывая, что вы пишете свой собственный класс Index и статический метод, который возвращает Iterable над экземплярами этого класса, вы можете

for (Index<String> each: With.index(stringArray)) {
    each.value;
    each.index;
    ...
}

Где реализация With.index - это что-то вроде

class With {
    public static <T> Iterable<Index<T>> index(final T[] array) {
        return new Iterable<Index<T>>() {
            public Iterator<Index<T>> iterator() {
                return new Iterator<Index<T>>() {
                    index = 0;
                    public boolean hasNext() { return index < array.size }
                    public Index<T> next() { return new Index(array[index], index++); }
                    ...
                }
            }
        }
    }
}

Ответ 3

Самое простое решение - просто запустить свой собственный счетчик таким образом:

int i = 0;
for (String s : stringArray) {
    doSomethingWith(s, i);
    i++;
}

Причина этого заключается в том, что нет никакой реальной гарантии того, что элементы в коллекции (которые итерация этого варианта for итерации) имеют даже индекс или даже имеют определенный порядок (некоторые коллекции могут изменять порядок при добавлении или удалить элементы).

См. например, следующий код:

import java.util.*;

public class TestApp {
  public static void AddAndDump(AbstractSet<String> set, String str) {
    System.out.println("Adding [" + str + "]");
    set.add(str);
    int i = 0;
    for(String s : set) {
        System.out.println("   " + i + ": " + s);
        i++;
    }
  }

  public static void main(String[] args) {
    AbstractSet<String> coll = new HashSet<String>();
    AddAndDump(coll, "Hello");
    AddAndDump(coll, "My");
    AddAndDump(coll, "Name");
    AddAndDump(coll, "Is");
    AddAndDump(coll, "Pax");
  }
}

Когда вы запустите это, вы увидите что-то вроде:

Adding [Hello]
   0: Hello
Adding [My]
   0: Hello
   1: My
Adding [Name]
   0: Hello
   1: My
   2: Name
Adding [Is]
   0: Hello
   1: Is
   2: My
   3: Name
Adding [Pax]
   0: Hello
   1: Pax
   2: Is
   3: My
   4: Name

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

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

Ответ 4

Использование lambdas и функциональных интерфейсов в Java 8 позволяет создавать новые абстракции цикла. Я могу перебрать коллекцию с индексом и размером коллекции:

List<String> strings = Arrays.asList("one", "two","three","four");
forEach(strings, (x, i, n) -> System.out.println("" + (i+1) + "/"+n+": " + x));

Какие выходы:

1/4: one
2/4: two
3/4: three
4/4: four

Который я реализовал как:

   @FunctionalInterface
   public interface LoopWithIndexAndSizeConsumer<T> {
       void accept(T t, int i, int n);
   }
   public static <T> void forEach(Collection<T> collection,
                                  LoopWithIndexAndSizeConsumer<T> consumer) {
      int index = 0;
      for (T object : collection){
         consumer.accept(object, index++, collection.size());
      }
   }

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

forEachHeadTail(strings, 
                (head) -> System.out.print(head), 
                (tail) -> System.out.print(","+tail));

Правильно печатает список, разделенный запятыми:

one,two,three,four

Который я реализовал как:

public static <T> void forEachHeadTail(Collection<T> collection, 
                                       Consumer<T> headFunc, 
                                       Consumer<T> tailFunc) {
   int index = 0;
   for (T object : collection){
      if (index++ == 0){
         headFunc.accept(object);
      }
      else{
         tailFunc.accept(object);
      }
   }
}

Библиотеки начнут всплывать, чтобы делать такие вещи, или вы можете катить их.

Ответ 5

Я боюсь, что это невозможно с foreach. Но я могу предложить вам простые старомодные for-loops:

    List<String> l = new ArrayList<String>();

    l.add("a");
    l.add("b");
    l.add("c");
    l.add("d");

    // the array
    String[] array = new String[l.size()];

    for(ListIterator<String> it =l.listIterator(); it.hasNext() ;)
    {
        array[it.nextIndex()] = it.next();
    }

Обратите внимание, что интерфейс List дает вам доступ к it.nextIndex().

(редактировать)

К вашему измененному примеру:

    for(ListIterator<String> it =l.listIterator(); it.hasNext() ;)
    {
        int i = it.nextIndex();
        doSomethingWith(it.next(), i);
    }

Ответ 6

В Java 8 представлен метод Iterable#forEach()/Map#forEach(), который более эффективен для многих реализаций Collection/Map по сравнению с "классическим" для каждого цикла. Однако и в этом случае индекс не предоставляется. Трюк здесь заключается в использовании AtomicInteger вне выражения лямбда. Примечание: переменные, используемые в выражении лямбда, должны быть действительно окончательными, поэтому мы не можем использовать обычный int.

final AtomicInteger indexHolder = new AtomicInteger();
map.forEach((k, v) -> {
    final int index = indexHolder.getAndIncrement();
    // use the index
});

Ответ 7

Одним из изменений, которые Sun рассматривает для Java7 является предоставление доступа к внутреннему Iterator в циклах foreach. синтаксис будет примерно таким (если это будет принято):

for (String str : list : it) {
  if (str.length() > 100) {
    it.remove();
  }
}

Это синтаксический сахар, но, очевидно, было сделано много запросов на эту функцию. Но до тех пор, пока он не будет одобрен, вам придется самостоятельно считать итерации или использовать регулярный цикл for с Iterator.

Ответ 8

Идиоматическое решение:

final Set<Double> doubles; // boilerplate
final Iterator<Double> iterator = doubles.iterator();
for (int ordinal = 0; iterator.hasNext(); ordinal++)
{
    System.out.printf("%d:%f",ordinal,iterator.next());
    System.out.println();
}

на самом деле это решение, которое Google предлагает в обсуждении в Гуаве, почему они не предоставили CountingIterator.

Ответ 9

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

Ответ 10

Хотя для достижения того же самого было упомянуто много других способов, я поделюсь своим путем для некоторых неудовлетворенных пользователей. Я использую функцию Java 8 IntStream.

1. Массивы

Object[] obj = {1,2,3,4,5,6,7};
IntStream.range(0, obj.length).forEach(index-> {
    System.out.println("index: " + index);
    System.out.println("value: " + obj[index]);
});

2. Список

List<String> strings = new ArrayList<String>();
Collections.addAll(strings,"A","B","C","D");

IntStream.range(0, strings.size()).forEach(index-> {
    System.out.println("index: " + index);
    System.out.println("value: " + strings.get(index));
});

Ответ 11

Существует "вариант" для ответа pax...; -)

int i = -1;
for(String s : stringArray) {
    doSomethingWith(s, ++i);
}

Ответ 12

int i = 0;
for(String s : stringArray) {
    doSomethingWith(s,i);
    ++i;
} 

Существует много способов, но метод является одним из способов

Ответ 13

Я обнаружил, что использование простого цикла for с инкрементирующим индексом было самым эффективным и надежным решением, которое размещено здесь: fooobar.com/questions/31752/...

Ответ 14

В ситуациях, когда мне нужен только индекс, например, в предложении catch, я иногда использую indexOf.

for(String s : stringArray) {
  try {
    doSomethingWith(s);
  } catch (Exception e) {
    LOGGER.warn("Had some kind of problem with string " +
      stringArray.indexOf(s) + ": " + s, e);
  }
}

Ответ 15

Счетчик это круто, но более короткий путь будет:

Stream.of(yourArray).forEach(i -> Arrays.asList(yourArray).indexOf(i));

Небольшая демонстрация:

import java.io.PrintStream;
import java.util.Arrays;
import java.util.stream.*;

class Main {
  public static void main(String[] args) {
    PrintStream p = System.out;


    String[] arr = {"Abraham", "James", "Donald", "Max"};
    Stream.of(arr).forEach(i -> p.println(Arrays.asList(arr).indexOf(i) + " : " + i));
  }
}

Смотри сам в действии: https://repl.it/@abranhe/forEachIndex

Ответ 16

Я немного удивлен, никто не предложил следующее (я признаю, что это ленивый подход...); Если stringArray является списком некоторого типа, вы можете использовать что-то вроде stringArray.indexOf(S), чтобы вернуть значение для текущего счетчика.

Примечание: это предполагает, что элементы списка уникальны или что это не имеет значения, если они не являются уникальными (потому что в этом случае он вернет индекс найденной первой копии).

Есть ситуации, когда этого было бы достаточно...

Ответ 17

Вот пример того, как я это сделал. Это получает индекс для каждого цикла. Надеюсь это поможет.

public class CheckForEachLoop {

    public static void main(String[] args) {

        int[] myIntArray = new int[11];
        String[] months = new String[] { "JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE", "JULY", "AUGUST",
                "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER" };
        for (String s : months) {
            if (s == months[2]) { // location where you can change
            }

        }
        System.out.println(s); 


    }
}