Comment puis-je générer une liste ou un tableau d'entiers séquentiels en Java?


130

Existe-t-il un moyen court et simple de générer un List<Integer>, ou peut-être un Integer[]ou int[], avec des valeurs séquentielles d'une startvaleur à une endvaleur?

C'est-à-dire quelque chose de plus court que, mais équivalent à 1, ce qui suit:

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

L'utilisation de la goyave est très bien.

Mettre à jour:

Analyse de performance

Étant donné que cette question a reçu plusieurs bonnes réponses, à la fois en utilisant Java 8 natif et des bibliothèques tierces, j'ai pensé tester les performances de toutes les solutions.

Le premier test teste simplement la création d'une liste de 10 éléments en [1..10]utilisant les méthodes suivantes:

  • classicArrayList : le code donné ci-dessus dans ma question (et essentiellement le même que la réponse d'adarshr).
  • eclipseCollections : le code donné dans la réponse de Donald ci-dessous en utilisant Eclipse Collections 8.0.
  • guavaRange : le code donné dans la réponse de daveb ci-dessous. Techniquement, cela ne crée pas un List<Integer>mais plutôt un ContiguousSet<Integer>- mais comme il implémente Iterable<Integer>dans l'ordre, cela fonctionne principalement pour mes besoins.
  • intStreamRange : le code donné dans la réponse de Vladimir ci-dessous, qui utilise IntStream.rangeClosed()- qui a été introduit dans Java 8.
  • streamIterate : le code donné dans la réponse de Catalin ci-dessous qui utilise également des IntStreamfonctionnalités introduites dans Java 8.

Voici les résultats en kilo-opérations par seconde (les nombres plus élevés sont meilleurs), pour tout ce qui précède avec des listes de taille 10:

Débit de création de liste

... et encore pour les listes de taille 10 000:

entrez la description de l'image ici

Ce dernier graphique est correct - les solutions autres que Eclipse et Guava sont trop lentes pour même obtenir une seule barre de pixels! Les solutions rapides sont 10 000 à 20 000 fois plus rapides que les autres.

Ce qui se passe ici, bien sûr, c'est que les solutions de goyave et d'éclipse ne matérialisent en fait aucune sorte de liste de 10000 éléments - ce sont simplement des enveloppes de taille fixe autour des points de départ et d'arrivée. Chaque élément est créé selon les besoins lors de l'itération. Puisque nous n'itérons pas réellement dans ce test, le coût est différé. Toutes les autres solutions matérialisent en fait la liste complète en mémoire et paient un lourd tribut dans un benchmark de création uniquement.

Faisons quelque chose d'un peu plus réaliste et itérons également sur tous les nombres entiers, en les additionnant. Donc, dans le cas de la IntStream.rangeClosedvariante, le benchmark ressemble à:

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

Ici, les images changent beaucoup, même si les solutions non matérialisantes sont toujours les plus rapides. Voici longueur = 10:

Liste <Integer> Itération (longueur = 10)

... et longueur = 10 000:

Liste <Integer> Itération (longueur = 10 000)

La longue itération sur de nombreux éléments égalise beaucoup les choses, mais l'éclipse et la goyave restent plus de deux fois plus rapides même sur le test de 10000 éléments.

Donc, si vous voulez vraiment une List<Integer>collection, éclipse semble être le meilleur choix - mais bien sûr, si vous utilisez les flux de manière plus native (par exemple, en oubliant .boxed()et en réduisant le domaine primitif), vous finirez probablement plus vite que tous ceux-ci. variantes.


1 Peut-être à l'exception de la gestion des erreurs, par exemple, si end< begin, ou si la taille dépasse certaines limites d'implémentation ou de JVM (par exemple, des tableaux plus grands que 2^31-1.


Réponses:


185

Avec Java 8, c'est si simple qu'il n'a même plus besoin de méthode séparée:

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

2
J'ai ajouté les résultats de performance pour cette réponse ci-dessus avec l'étiquette intStreamRange .
BeeOnRope

Nécessite API 24+
gcantoni

28

Eh bien, cette doublure pourrait être admissible (utilise des gammes de goyaves )

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

Cela ne crée pas de List<Integer>, mais ContiguousSetoffre à peu près les mêmes fonctionnalités, en particulier la mise en œuvre Iterable<Integer>qui permet la foreachmise en œuvre de la même manière que List<Integer>.

Dans les anciennes versions (quelque part avant Guava 14), vous pouvez utiliser ceci:

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

Les deux produisent:

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

7
Je ne l'utiliserais pas à asList()moins que vous n'ayez vraiment besoin d'un List... le ContiguousSetproduit par asSetest léger (il a juste besoin de la plage et du domaine), mais asList()créerai une liste qui stocke réellement tous les éléments en mémoire (actuellement).
ColinD le

1
D'accord. L'OP demandait cependant une liste ou un tableau, sinon je l'aurais laissé de côté
daveb

1
Je crois que pour 18.0, Rangeexiste mais pas Rangeset ils ont éliminé la asSetméthode. Dans mon ancienne version, asSetest obsolète et il semble qu'ils l'ont supprimé. Les plages ne doivent apparemment être utilisées que pour les collections contiguës et elles l'ont appliquée bien que j'adore cette solution.
demongolem

L'API nécessite maintenant un code similaire à celui-ci: ContiguousSet.create (Range.closed (1, count), DiscreteDomain.integers ()
Ben

J'ai ajouté les résultats de performance pour cette réponse ci-dessus avec l'étiquette guavaRange .
BeeOnRope

11

La version Java 8 à une ligne suivante générera [1, 2, 3 ... 10]. Le premier argument de iterateest le premier nr de la séquence et le premier arg de limitest le dernier nombre.

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

J'ai ajouté les résultats de performance pour cette réponse ci-dessus avec le libellé streamIterate .
BeeOnRope

1
Pour clarifier, l'argument limite n'est pas le dernier nombre, c'est le nombre d'entiers dans la liste.
neilireson

7

Vous pouvez utiliser la Intervalclasse d' Eclipse Collections .

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

La Intervalclasse est paresseuse, donc ne stocke pas toutes les valeurs.

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

Votre méthode pourrait être implémentée comme suit:

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

Si vous souhaitez éviter de boxer des entiers en tant que nombres entiers, mais que vous souhaitez toujours une structure de liste en conséquence, vous pouvez utiliser IntListavec IntIntervaldes collections Eclipse.

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

IntLista les méthodes sum(), min(), minIfEmpty(), max(), maxIfEmpty(), average()et median()disponible sur l'interface.

Mise à jour pour plus de clarté: 27/11/2017

An Intervalest un List<Integer>, mais il est paresseux et immuable. C'est extrêmement utile pour générer des données de test, surtout si vous traitez beaucoup avec des collections. Si vous le souhaitez, vous pouvez facilement copier un intervalle dans un List, Setou Bagcomme suit:

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

Un IntIntervalest un ImmutableIntListqui s'étend IntList. Il a également des méthodes de conversion.

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

An Intervalet an IntIntervaln'ont pas le même equalscontrat.

Mise à jour pour Eclipse Collections 9.0

Vous pouvez désormais créer des collections primitives à partir de flux primitifs. Il existe withAllet des ofAllméthodes en fonction de vos préférences. Si vous êtes curieux, j'explique pourquoi nous avons les deux ici . Ces méthodes existent pour les listes, ensembles, sacs et piles int / longs / doubles mutables et immuables.

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

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

Remarque: je suis un committer pour les collections Eclipse


J'ai ajouté les résultats de performance pour cette réponse ci-dessus avec l'étiquette eclipseCollections .
BeeOnRope

Soigné. J'ai mis à jour ma réponse avec une version primitive supplémentaire qui devrait éviter toute boxe.
Donald Raab

6

C'est le plus court que je puisse obtenir en utilisant Core Java.

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
Vous pouvez raser quelques personnages supplémentaires en changeant cette boucle en for(int i = begin; i <= end; ret.add(i++));:)
vaughandroid

Je ne suis pas vraiment sûr que le fait de déplacer la ret.add(i)partie dans l'incrément de boucle for rende cela "plus court". Je suppose que par cette logique, si j'écrivais tout sur une seule ligne, ce serait plus court :)
BeeOnRope

@BeeOnRope Oui, certainement pas le plus court, mais plus court de deux lignes, c'est sûr :) Comme je l'ai dit, c'est le plus proche que nous puissions parvenir à raccourcir dans Core Java.
adarshr

J'ai ajouté les résultats de performance pour cette réponse ci-dessus avec l'étiquette classicArrayList .
BeeOnRope

3

Vous pouvez utiliser des gammes de goyaves

Vous pouvez obtenir un SortedSeten utilisant

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

0

C'est le plus court que j'ai pu trouver.

Version de la liste

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

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

    return ret;
}

Version de la baie

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

Celui-ci pourrait fonctionner pour vous ...

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

Utiliser AtomicInteger est très lourd en ressources, environ dix fois plus lent dans mon test. Mais il est sécurisé pour le multithread. fin <début non vérifié
cl-r

1
L'utilisation d'AtomicInteger n'a pas de sens à l'intérieur d'une méthode. Toutes les phrases d'un appel de méthode sont exécutées séquentiellement par le thread qui a appelé la méthode, donc vous n'obtenez rien de l'AtomicInteger mais des appels de ralentissement et de getAndIncrement () ennuyeux.
Igor Rodriguez
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.