it-swarm.com.de

Gibt es eine Möglichkeit, auf einen Iterationszähler in Java für jede Schleife zuzugreifen?

Gibt es einen Weg in Javas for-each-Schleife?

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

um herauszufinden, wie oft die Schleife bereits abgearbeitet wurde?

Abgesehen von der Verwendung der alten und bekannten for(int i=0; i < boundary; i++) - Schleife ist das Konstrukt

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

die einzige Möglichkeit, einen solchen Zähler in einer For-Each-Schleife zur Verfügung zu haben?

253
Kosi2801

Nein, aber Sie können Ihren eigenen Zähler zur Verfügung stellen.

Der Grund dafür ist, dass die for-each-Schleife intern keinen Zähler have hat; Es basiert auf der Iterable - Schnittstelle, dh es verwendet eine Iterator, um die "Sammlung" zu durchlaufen - wobei es sich möglicherweise überhaupt nicht um eine Sammlung handelt und es sich tatsächlich um etwas handelt, das sich nicht in der "Sammlung" befindet Alle basieren auf Indizes (wie z. B. einer verknüpften Liste).

184

Es geht auch anders.

Vorausgesetzt, Sie schreiben Ihre eigene Klasse Index und eine statische Methode, die über Instanzen dieser Klasse ein Iterable zurückgibt, können Sie

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

Wo die Implementierung von With.index ist so etwas wie

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++); }
                    ...
                }
            }
        }
    }
}
60
akuhn

Die einfachste Lösung ist ist einfach einen eigenen Counter zu betreiben:

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

Der Grund dafür ist, dass es keine tatsächliche Garantie dafür gibt, dass Elemente in einer Sammlung (über die diese Variante von for iteriert) sogar haben einen Index oder sogar ein definiert) haben order (Einige Sammlungen können die Reihenfolge ändern, wenn Sie Elemente hinzufügen oder entfernen.).

Siehe zum Beispiel den folgenden Code:

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");
  }
}

Wenn Sie das ausführen, sehen Sie etwas wie:

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

dies weist darauf hin, dass die Reihenfolge zu Recht nicht als hervorstechendes Merkmal einer Menge angesehen wird.

Es gibt andere Möglichkeiten, dies zu tun ohne einen manuellen Zähler, aber es ist ein gutes Stück Arbeit zum zweifelhaften Nutzen.

41
paxdiablo

Die Verwendung von Lambdas und Funktionsschnittstellen in Java 8 ermöglicht das Erstellen neuer Schleifenabstraktionen. Ich kann eine Sammlung mit dem Index und der Sammlungsgröße durchlaufen:

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

Welche Ausgänge:

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

Was ich umgesetzt habe als:

   @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());
      }
   }

Die Möglichkeiten sind endlos. Zum Beispiel erstelle ich eine Abstraktion, die eine spezielle Funktion nur für das erste Element verwendet:

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

Welche druckt eine durch Kommas getrennte Liste richtig:

one,two,three,four

Was ich umgesetzt habe als:

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);
      }
   }
}

Bibliotheken werden auftauchen, um diese Art von Dingen zu erledigen, oder Sie können Ihre eigenen rollen.

25
James Scriven

In Java 8 wurde die Methode Iterable#forEach()/Map#forEach() eingeführt, die für viele Implementierungen von Collection/Map effizienter ist als die "klassische" Methode für jede Implementierung Schleife. Ein Index ist jedoch auch in diesem Fall nicht vorgesehen. Der Trick hier ist, AtomicInteger außerhalb des Lambda-Ausdrucks zu verwenden. Hinweis: Variablen, die im Lambda-Ausdruck verwendet werden, müssen effektiv final sein. Deshalb können wir kein gewöhnliches int verwenden.

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

Ich fürchte, das ist mit foreach nicht möglich. Aber ich kann Ihnen eine einfache altmodische for-Schleife vorschlagen:

    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();
    }

Beachten Sie, dass Sie über die Schnittstelle Liste auf it.nextIndex() zugreifen können.

(bearbeiten)

Zu Ihrem geänderten Beispiel:

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

Eine der Änderungen, die Sun für Java7 In Betracht zieht, besteht darin, den Zugriff auf die inneren Iterator in foreach-Schleifen zu ermöglichen. Die Syntax sieht ungefähr so ​​aus (wenn dies akzeptiert wird):

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

Dies ist syntaktischer Zucker, aber anscheinend wurden viele Anfragen für diese Funktion gestellt. Aber bis es genehmigt ist, müssen Sie die Iterationen selbst zählen oder eine reguläre for-Schleife mit einem Iterator verwenden.

9
Yuval

Idiomatische Lösung:

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();
}

dies ist eigentlich die Lösung, die Google in der Guava-Diskussion vorschlägt, warum sie kein CountingIterator angegeben haben.

5
user177800

Wenn Sie einen Zähler in einer For-Each-Schleife benötigen, müssen Sie sich selbst zählen. Soweit ich weiß, gibt es keine eingebaute Theke.

2
EricSchaefer

Obwohl es so viele andere Möglichkeiten gibt, um dasselbe zu erreichen, werde ich einigen unzufriedenen Benutzern meinen Weg zeigen. Ich verwende die Java 8 IntStream-Funktion.

1. Arrays

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. Liste

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));
});
2
int i = 0;
for(String s : stringArray) {
    doSomethingWith(s,i);
    ++i;
} 

Es gibt viele Wege, aber die Methode ist einer der Wege

1
Sankar Karthi

Es gibt eine "Variante", um die Antwort zu beantworten ... ;-)

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

Ich fand, dass die Verwendung einer einfachen for-Schleife mit einem inkrementierenden Index die effizienteste und zuverlässigste Lösung ist, wie hier angegeben: https://stackoverflow.com/a/3431543/2430549

0
HoldOffHunger

In Situationen, in denen ich den Index nur gelegentlich benötige, wie in einer catch-Klausel, verwende ich manchmal 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);
  }
}
0
Jeremy