Wie kann ich in Java eine Liste oder ein Array von sequentiellen Ganzzahlen generieren?


128

Gibt es eine kurze und süße Möglichkeit, ein List<Integer>oder vielleicht ein Integer[]oder int[]mit sequentiellen Werten von einem startWert zu einem endWert zu generieren ?

Das heißt, etwas kürzer als, aber gleich 1 der folgenden:

void List<Integer> makeSequence(int begin, int end) {
  List<Integer> ret = new ArrayList<>(end - begin + 1);
  for (int i=begin; i<=end; i++) {
    ret.add(i);
  }
  return ret;  
}

Die Verwendung von Guave ist in Ordnung.

Aktualisieren:

Performance-Analyse

Da diese Frage mehrere gute Antworten erhalten hat, sowohl mit nativem Java 8 als auch mit Bibliotheken von Drittanbietern, dachte ich, ich würde die Leistung aller Lösungen testen.

Der erste Test testet einfach das Erstellen einer Liste von 10 Elementen [1..10]mit den folgenden Methoden:

  • classicArrayList : Der oben in meiner Frage angegebene Code (und im Wesentlichen der gleiche wie die Antwort von adarshr).
  • eclipseCollections : Der in Donalds Antwort unten angegebene Code unter Verwendung von Eclipse Collections 8.0.
  • guavaRange : Der Code, der in der Antwort von daveb unten angegeben ist. Technisch gesehen erzeugt dies kein, List<Integer>sondern ein ContiguousSet<Integer>- aber da es Iterable<Integer>in der richtigen Reihenfolge implementiert wird, funktioniert es hauptsächlich für meine Zwecke.
  • intStreamRange : Der Code in der folgenden Antwort von Vladimir , der verwendet IntStream.rangeClosed()- der in Java 8 eingeführt wurde.
  • streamIterate : Der in der folgenden Antwort von Catalin angegebene Code, der auch die IntStreamin Java 8 eingeführten Funktionen verwendet .

Hier sind die Ergebnisse in Kilobetrieben pro Sekunde (höhere Zahlen sind besser) für alle oben genannten mit Listen der Größe 10:

Durchsatz für die Listenerstellung

... und noch einmal für Listen der Größe 10.000:

Geben Sie hier die Bildbeschreibung ein

Das letzte Diagramm ist korrekt - andere Lösungen als Eclipse und Guava sind zu langsam, um überhaupt einen einzelnen Pixelbalken zu erhalten! Die schnellen Lösungen sind 10.000 bis 20.000 Mal schneller als die anderen.

Was hier natürlich vor sich geht, ist, dass die Guaven- und Eclipse-Lösungen tatsächlich keine 10.000-Elemente-Liste materialisieren - sie sind einfach Wrapper fester Größe um den Start- und Endpunkt. Jedes Element wird nach Bedarf während der Iteration erstellt. Da wir in diesem Test nicht wirklich iterieren, werden die Kosten zurückgestellt. Alle anderen Lösungen materialisieren tatsächlich die vollständige Liste im Speicher und zahlen einen hohen Preis für einen Benchmark nur für die Erstellung.

Lassen Sie uns etwas Realistischeres tun und auch alle ganzen Zahlen durchlaufen und summieren. Bei der IntStream.rangeClosedVariante sieht der Benchmark also so aus:

@Benchmark
public int intStreamRange() {
    List<Integer> ret = IntStream.rangeClosed(begin, end).boxed().collect(Collectors.toList());  

    int total = 0;
    for (int i : ret) {
        total += i;
    }
    return total;  
}

Hier ändern sich die Bilder stark, obwohl die nicht materialisierenden Lösungen immer noch die schnellsten sind. Hier ist Länge = 10:

List <Integer> Iteration (Länge = 10)

... und Länge = 10.000:

List <Integer> Iteration (Länge = 10.000)

Die lange Iteration über viele Elemente gleicht die Dinge viel aus, aber Eclipse und Guave bleiben selbst beim 10.000-Elemente-Test mehr als doppelt so schnell.

Wenn Sie also wirklich eine List<Integer>Eclipse-Sammlung wünschen , scheint dies die beste Wahl zu sein. Wenn Sie Streams jedoch nativer verwenden (z. B. .boxed()die primitive Domäne vergessen und reduzieren), werden Sie wahrscheinlich schneller als alle diese sein Varianten.


1 Möglicherweise mit Ausnahme der Fehlerbehandlung, z. B. wenn end< beginoder wenn die Größe einige Implementierungs- oder JVM-Grenzwerte überschreitet (z. B. Arrays größer als 2^31-1.


Antworten:


184

Mit Java 8 ist es so einfach, dass es nicht einmal mehr eine separate Methode benötigt:

List<Integer> range = IntStream.rangeClosed(start, end)
    .boxed().collect(Collectors.toList());

2
Ich habe oben Leistungsergebnisse für diese Antwort mit der Bezeichnung intStreamRange hinzugefügt .
BeeOnRope

Benötigt API 24+
gcantoni

28

Nun, dieser eine Liner könnte sich qualifizieren (verwendet Guava Ranges )

ContiguousSet<Integer> integerList = ContiguousSet.create(Range.closedOpen(0, 10), DiscreteDomain.integers());
System.out.println(integerList);

Dies schafft keine List<Integer>, ContiguousSetbietet aber fast die gleiche Funktionalität, insbesondere die Implementierung, Iterable<Integer>die die foreachImplementierung auf die gleiche Weise wie ermöglicht List<Integer>.

In älteren Versionen (irgendwo vor Guava 14) können Sie Folgendes verwenden:

ImmutableList<Integer> integerList = Ranges.closedOpen(0, 10).asSet(DiscreteDomains.integers()).asList();
System.out.println(integerList);

Beide produzieren:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

7
Ich würde es dort nicht verwenden, asList()es sei denn, Sie benötigen wirklich ein List... das ContiguousSetvon asSetist leicht (es benötigt nur den Bereich und die Domäne), sondern asList()erstellt eine Liste, in der (derzeit) tatsächlich alle Elemente im Speicher gespeichert sind.
ColinD

1
Einverstanden. Das OP bat jedoch um eine Liste oder ein Array, sonst hätte ich es
weggelassen

1
Ich glaube für 18.0, Rangeexistiert aber nicht Rangesund sie haben die asSetMethode abgeschafft. In meiner älteren Version asSetist veraltet und es scheint, dass sie es entfernt haben. Bereiche sollen anscheinend nur für zusammenhängende Sammlungen verwendet werden, und sie haben sie durchgesetzt, obwohl ich diese Lösung liebe.
Demongolem

Die API erfordert jetzt Code ähnlich dem folgenden: ContiguousSet.create (Range.closed (1, count), DiscreteDomain.integers ()
Ben

Ich habe oben Leistungsergebnisse für diese Antwort mit dem Label guavaRange hinzugefügt .
BeeOnRope

11

Die folgende einzeilige Java 8-Version generiert [1, 2, 3 ... 10]. Das erste Argument von iterateist das erste nr in der Sequenz, und das erste Argument von limitist die letzte Zahl.

List<Integer> numbers = Stream.iterate(1, n -> n + 1)
                              .limit(10)
                              .collect(Collectors.toList());

Ich habe die Leistungsergebnisse für diese Antwort oben mit dem Label streamIterate hinzugefügt .
BeeOnRope

1
Zur Verdeutlichung ist das Grenzwertargument nicht die letzte Zahl, sondern die Anzahl der Ganzzahlen in der Liste.
Neilireson

7

Sie können die IntervalKlasse aus Eclipse-Sammlungen verwenden .

List<Integer> range = Interval.oneTo(10);
range.forEach(System.out::print);  // prints 12345678910

Die IntervalKlasse ist faul, speichert also nicht alle Werte.

LazyIterable<Integer> range = Interval.oneTo(10);
System.out.println(range.makeString(",")); // prints 1,2,3,4,5,6,7,8,9,10

Ihre Methode kann wie folgt implementiert werden:

public List<Integer> makeSequence(int begin, int end) {
    return Interval.fromTo(begin, end);
}

Wenn Sie Boxing Ints als Ganzzahlen vermeiden möchten, aber dennoch eine Listenstruktur als Ergebnis wünschen, können Sie diese IntListmit IntIntervalaus Eclipse-Sammlungen verwenden.

public IntList makeSequence(int begin, int end) {
    return IntInterval.fromTo(begin, end);
}

IntListhat die Methoden sum(), min(), minIfEmpty(), max(), maxIfEmpty(), average()und median()an der Schnittstelle zur Verfügung.

Update aus Gründen der Übersichtlichkeit: 27.11.2017

An Intervalist a List<Integer>, aber es ist faul und unveränderlich. Es ist äußerst nützlich, um Testdaten zu generieren, insbesondere wenn Sie viel mit Sammlungen zu tun haben. Wenn Sie möchten , können Sie ein Intervall auf eine leicht zu kopieren List, Setoder Bagwie folgt:

Interval integers = Interval.oneTo(10);
Set<Integer> set = integers.toSet();
List<Integer> list = integers.toList();
Bag<Integer> bag = integers.toBag();

Ein IntIntervalist ein, ImmutableIntListdas sich erstreckt IntList. Es hat auch Konvertermethoden.

IntInterval ints = IntInterval.oneTo(10);
IntSet set = ints.toSet();
IntList list = ints.toList();
IntBag bag = ints.toBag();

An Intervalund an IntIntervalhaben nicht den gleichen equalsVertrag.

Update für Eclipse-Sammlungen 9.0

Sie können jetzt primitive Sammlungen aus primitiven Streams erstellen. Es gibt withAllund ofAllMethoden je nach Ihren Vorlieben. Wenn Sie neugierig sind, erkläre ich, warum wir beide hier haben . Diese Methoden existieren für veränderbare und unveränderliche Int / Long / Double-Listen, Sets, Bags und Stacks.

Assert.assertEquals(
        IntInterval.oneTo(10),
        IntLists.mutable.withAll(IntStream.rangeClosed(1, 10)));

Assert.assertEquals(
        IntInterval.oneTo(10),
        IntLists.immutable.withAll(IntStream.rangeClosed(1, 10)));

Hinweis: Ich bin ein Committer für Eclipse-Sammlungen


Ich habe oben Leistungsergebnisse für diese Antwort mit der Bezeichnung eclipseCollections hinzugefügt .
BeeOnRope

Ordentlich. Ich habe meine Antwort mit einer zusätzlichen primitiven Version aktualisiert, die jegliches Boxen vermeiden sollte.
Donald Raab

6

Dies ist die kürzeste Zeit, die ich mit Core Java bekommen kann.

List<Integer> makeSequence(int begin, int end) {
  List<Integer> ret = new ArrayList(end - begin + 1);

  for(int i = begin; i <= end; i++, ret.add(i));

  return ret;  
}

3
Sie können ein paar weitere Zeichen rasieren, indem Sie diese Schleife in for(int i = begin; i <= end; ret.add(i++));:)
ändern

Ich bin mir nicht sicher, ob das Verschieben des ret.add(i)Teils in das for-Schleifeninkrement dies "kürzer" macht. Ich denke nach dieser Logik wäre es kürzer, wenn ich alles in eine Zeile
schreiben

@BeeOnRope Ja, definitiv nicht das kürzeste, aber mit Sicherheit um zwei Zeilen kürzer :) Wie gesagt, dies ist das, was wir der Verkürzung in Core Java am nächsten kommen können.
Adarshr

Ich habe oben für diese Antwort Leistungsergebnisse mit dem Label classicArrayList hinzugefügt .
BeeOnRope

3

Sie könnten Guavenbereiche verwenden

Sie können eine erhalten, SortedSetindem Sie

ImmutableSortedSet<Integer> set = Ranges.open(1, 5).asSet(DiscreteDomains.integers());
// set contains [2, 3, 4]

0

Dies ist die kürzeste, die ich finden konnte.

Listenversion

public List<Integer> makeSequence(int begin, int end)
{
    List<Integer> ret = new ArrayList<Integer>(++end - begin);

    for (; begin < end; )
        ret.add(begin++);

    return ret;
}

Array-Version

public int[] makeSequence(int begin, int end)
{
    if(end < begin)
        return null;

    int[] ret = new int[++end - begin];
    for (int i=0; begin < end; )
        ret[i++] = begin++;
    return ret;
}

-2

Dieser könnte für Sie arbeiten ....

void List<Integer> makeSequence(int begin, int end) {

  AtomicInteger ai=new AtomicInteger(begin);
  List<Integer> ret = new ArrayList(end-begin+1);

  while ( end-->begin) {

    ret.add(ai.getAndIncrement());

  }
  return ret;  
}

Die Verwendung von AtomicInteger ist für Ressourcen sehr schwer und in meinem Test etwa zehnmal langsamer. Aber es ist sicher für Multithread. end <begin nicht verifiziert
cl-r

1
Die Verwendung von AtomicInteger ist innerhalb einer Methode nicht sinnvoll. Alle Sätze in einem Methodenaufruf werden nacheinander von dem Thread ausgeführt, der die Methode aufgerufen hat, sodass Sie von AtomicInteger nur langsame und nervige getAndIncrement () -Aufrufe erhalten.
Igor Rodriguez
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.