¿Cómo puedo generar una lista o matriz de enteros secuenciales en Java?

130

¿Hay una manera corta y dulce de generar un List<Integer>, o tal vez un Integer[]o int[], con valores secuenciales de algún startvalor a un endvalor?

Es decir, algo más corto que, pero equivalente a 1, lo siguiente:

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

El uso de la guayaba está bien.

Actualizar:

Análisis de rendimiento

Dado que esta pregunta ha recibido varias buenas respuestas, utilizando tanto Java 8 nativas como bibliotecas de terceros, pensé en probar el rendimiento de todas las soluciones.

La primera prueba simplemente prueba la creación de una lista de 10 elementos [1..10]utilizando los siguientes métodos:

  • classicArrayList : el código dado anteriormente en mi pregunta (y esencialmente el mismo que la respuesta de adarshr).
  • eclipseCollections : el código dado en la respuesta de Donald a continuación usando Eclipse Collections 8.0.
  • guavaRange : el código dado en la respuesta de daveb a continuación. Técnicamente, esto no crea un List<Integer>sino más bien un ContiguousSet<Integer>, pero como se implementa Iterable<Integer>en orden, funciona principalmente para mis propósitos.
  • intStreamRange : el código proporcionado en la respuesta de Vladimir a continuación, que utiliza IntStream.rangeClosed(), que se introdujo en Java 8.
  • streamIterate : el código proporcionado en la respuesta de Catalin a continuación, que también utiliza la IntStreamfuncionalidad introducida en Java 8.

Aquí están los resultados en kilo-operaciones por segundo (los números más altos son mejores), para todo lo anterior con listas de tamaño 10:

Rendimiento de creación de lista

... y nuevamente para listas de tamaño 10,000:

ingrese la descripción de la imagen aquí

Ese último gráfico es correcto: las soluciones que no sean Eclipse y Guava son demasiado lentas para incluso obtener una sola barra de píxeles. Las soluciones rápidas son 10,000 a 20,000 veces más rápidas que el resto.

Lo que está sucediendo aquí, por supuesto, es que las soluciones de guayaba y eclipse en realidad no materializan ningún tipo de lista de 10,000 elementos, simplemente son envoltorios de tamaño fijo alrededor del inicio y los puntos finales. Cada elemento se crea según sea necesario durante la iteración. Como no realizamos iteraciones en esta prueba, el costo se difiere. Todas las otras soluciones en realidad materializan la lista completa en la memoria y pagan un alto precio en un punto de referencia de solo creación.

Hagamos algo un poco más realista y también iteremos sobre todos los enteros, resumiéndolos. Entonces, en el caso de la IntStream.rangeClosedvariante, el punto de referencia se ve así:

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

Aquí, las imágenes cambian mucho, aunque las soluciones que no se materializan siguen siendo las más rápidas. Aquí está la longitud = 10:

List <Integer> Iteración (longitud = 10)

... y longitud = 10,000:

List <Integer> Iteración (longitud = 10,000)

La larga iteración sobre muchos elementos iguala las cosas mucho, pero el eclipse y la guayaba permanecen más del doble de rápido incluso en la prueba de 10,000 elementos.

Entonces, si realmente desea una List<Integer>, las colecciones de eclipse parecen ser la mejor opción, pero, por supuesto, si usa secuencias de una manera más nativa (por ejemplo, olvidando .boxed()y haciendo una reducción en el dominio primitivo), probablemente terminará más rápido que todos estos variantes


1 Quizás con la excepción del manejo de errores, por ejemplo, si end< begin, o si el tamaño excede algunos límites de implementación o JVM (por ejemplo, matrices más grandes que 2^31-1.

BeeOnRope
fuente
Para apache commons, stackoverflow.com/a/5744861/560302
MoveFast

Respuestas:

184

Con Java 8 es tan simple que ya ni siquiera necesita un método separado:

List<Integer> range = IntStream.rangeClosed(start, end)
    .boxed().collect(Collectors.toList());
Vladimir Matveev
fuente
2
Agregué resultados de rendimiento para esta respuesta anterior con la etiqueta intStreamRange .
BeeOnRope
Requiere API 24+
gcantoni
28

Bueno, este trazador de líneas podría calificar (usa gamas de guayaba )

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

Esto no crea una List<Integer>, pero ContiguousSetofrece la misma funcionalidad, en particular la implementación Iterable<Integer>que permite la foreachimplementación de la misma manera que List<Integer>.

En versiones anteriores (en algún lugar antes de Guava 14) podría usar esto:

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

Ambos producen:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
daveb
fuente
77
No lo usaría asList()allí a menos que realmente necesitaras un List... el ContiguousSetproducido por asSetes liviano (solo necesita el rango y el dominio), pero asList()creará una lista que realmente almacena todos los elementos en la memoria (actualmente).
ColinD
1
Convenido. El PO estaba pidiendo una lista o matriz, sin embargo, de lo contrario habría dejado fuera
DaveB
1
Creo que para 18.0, Rangeexiste pero no Rangesy han eliminado el asSetmétodo. En mi versión anterior, asSetestá en desuso y parece que lo han eliminado. Al parecer, los rangos solo se deben usar para colecciones contiguas y lo han hecho cumplir, aunque me encanta esta solución.
demongolem
La API ahora requiere un código similar a este: ContiguousSet.create (Range.closed (1, count), DiscreteDomain.integers ()
Ben
Agregué resultados de rendimiento para esta respuesta anterior con la etiqueta guavaRange .
BeeOnRope
11

La siguiente versión de Java 8 de una línea generará [1, 2, 3 ... 10]. El primer argumento de iteratees el primer nr de la secuencia, y el primer argumento de limites el último número.

List<Integer> numbers = Stream.iterate(1, n -> n + 1)
                              .limit(10)
                              .collect(Collectors.toList());
Catalin Ciurea
fuente
Agregué resultados de rendimiento para esta respuesta anterior con la etiqueta streamIterate .
BeeOnRope
1
Como punto de aclaración, el límite arg no es el último número, es el número de enteros en la lista.
neilireson
7

Puede usar la Intervalclase de Eclipse Collections .

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

La Intervalclase es perezosa, por lo que no almacena todos los valores.

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

Su método podría implementarse de la siguiente manera:

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

Si desea evitar las entradas de boxeo como enteros, pero aún así desea una estructura de lista como resultado, puede usarlo IntListcon IntIntervalEclipse Collections.

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

IntListtiene los métodos sum(), min(), minIfEmpty(), max(), maxIfEmpty(), average()y median()disponible en la interfaz.

Actualización para mayor claridad: 27/11/2017

An Intervales a List<Integer>, pero es vago e inmutable. Es extremadamente útil para generar datos de prueba, especialmente si manejas mucho las colecciones. Si lo desea, puede copiar fácilmente un intervalo en a List, Seto de la Bagsiguiente manera:

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

Un IntIntervales un ImmutableIntListque se extiende IntList. También tiene métodos de conversión.

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

An Intervaly an IntIntervalno tienen el mismo equalscontrato.

Actualización para Eclipse Collections 9.0

Ahora puede crear colecciones primitivas a partir de flujos primitivos. Existen withAlly ofAllmétodos según su preferencia. Si tienes curiosidad, te explico por qué tenemos ambos aquí . Estos métodos existen para listas, conjuntos, bolsas y pilas Int / Long / Double mutables e inmutables.

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

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

Nota: Soy un committer para Eclipse Collections

Donald Raab
fuente
Agregué resultados de rendimiento para esta respuesta anterior con la etiqueta eclipseCollections .
BeeOnRope
Ordenado. Actualicé mi respuesta con una versión primitiva adicional que debería evitar cualquier boxeo.
Donald Raab
6

Esto es lo más corto que pude obtener usando 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;  
}
adarshr
fuente
3
Puede afeitarse un par de caracteres más cambiando ese bucle a for(int i = begin; i <= end; ret.add(i++));:)
vaughandroid
No estoy realmente seguro de que mover la ret.add(i)parte al incremento for loop lo haga "más corto". Supongo que según esa lógica, si lo escribiera todo en una línea, sería más corto :)
BeeOnRope
@BeeOnRope Sí, definitivamente no es el más corto, pero sí más corto por dos líneas :) Como dije, esto es lo más cerca que podemos llegar a acortarlo en Core Java.
adarshr
Agregué resultados de rendimiento para esta respuesta anterior con la etiqueta classicArrayList .
BeeOnRope
3

Podrías usar gamas de guayaba

Puedes obtener un SortedSetusando

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

Esto es lo más corto que pude encontrar.

Lista de versiones

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

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

    return ret;
}

Versión de matriz

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;
}
Código detrás
fuente
-2

Este podría funcionar para ti ...

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;  
}
Shehzad
fuente
Usar AtomicInteger es muy pesado para los recursos, aproximadamente diez veces más lento en mi prueba. Pero es seguro para multiproceso. fin <comienzo no verificado
cl-r
1
El uso de AtomicInteger no tiene sentido dentro de un método. Todas las oraciones en una llamada al método se ejecutan secuencialmente por el subproceso que llamó al método, por lo que no obtiene nada del AtomicInteger sino ralentizaciones y llamadas molestas getAndIncrement ().
Igor Rodriguez