Como posso gerar uma lista ou matriz de números inteiros seqüenciais em Java?


130

Existe uma maneira curta e agradável de gerar um List<Integer>, ou talvez um Integer[]ou int[], com valores seqüenciais de algum startvalor para um endvalor?

Ou seja, algo menor que, mas equivalente a 1 o seguinte:

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

O uso de goiaba é bom.

Atualizar:

Análise de desempenho

Como esta pergunta recebeu várias boas respostas, usando bibliotecas Java 8 nativas e de terceiros, pensei em testar o desempenho de todas as soluções.

O primeiro teste simplesmente testa a criação de uma lista de 10 elementos [1..10]usando os seguintes métodos:

  • classicArrayList : o código fornecido acima na minha pergunta (e essencialmente o mesmo que a resposta de adarshr).
  • eclipseCollections : o código fornecido na resposta de Donald abaixo usando o Eclipse Collections 8.0.
  • guavaRange : o código fornecido na resposta daveb abaixo. Tecnicamente, isso não cria um, List<Integer>mas sim um ContiguousSet<Integer>- mas, como é implementado Iterable<Integer>em ordem, funciona principalmente para meus propósitos.
  • intStreamRange : o código fornecido na resposta de Vladimir abaixo, que usa IntStream.rangeClosed()- que foi introduzido no Java 8.
  • streamIterate : o código fornecido na resposta do Catalin abaixo, que também usa a IntStreamfuncionalidade introduzida no Java 8.

Aqui estão os resultados em operações de quilo por segundo (números maiores são melhores), para todos os itens acima com listas de tamanho 10:

Taxa de transferência de criação de lista

... e novamente para listas de tamanho 10.000:

insira a descrição da imagem aqui

Esse último gráfico está correto - as soluções que não sejam Eclipse e Guava são muito lentas para obter uma única barra de pixels! As soluções rápidas são 10.000 a 20.000 vezes mais rápidas que as demais.

O que está acontecendo aqui, é claro, é que as soluções goiaba e eclipse realmente não materializam nenhum tipo de lista de 10.000 elementos - elas são simplesmente invólucros de tamanho fixo em torno do ponto inicial e final. Cada elemento é criado conforme necessário durante a iteração. Como na verdade não iteramos neste teste, o custo é adiado. Todas as outras soluções, na verdade, materializam a lista completa na memória e pagam um preço muito alto em uma referência apenas para criação.

Vamos fazer algo um pouco mais realista e também iterar sobre todos os números inteiros, somando-os. Portanto, no caso da IntStream.rangeClosedvariante, a referência se parece com:

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

Aqui, as imagens mudam bastante, embora as soluções não materializantes ainda sejam as mais rápidas. Aqui está o comprimento = 10:

Iteração <Integer> da lista (comprimento = 10)

... e comprimento = 10.000:

Iteração <Integer> da lista (comprimento = 10.000)

A longa iteração sobre muitos elementos equilibra bastante as coisas, mas o eclipse e a goiaba permanecem mais que o dobro da velocidade, mesmo no teste de 10.000 elementos.

Portanto, se você realmente deseja List<Integer>, coleções de eclipses parece ser a melhor escolha - mas é claro que se você usar fluxos de maneira mais nativa (por exemplo, esquecendo .boxed()e fazendo uma redução no domínio primitivo), provavelmente terminará mais rápido do que todos esses variantes.


1 Talvez, com exceção do tratamento de erros, por exemplo, se end< begin, ou se o tamanho exceder alguns limites de implementação ou JVM (por exemplo, matrizes maiores que 2^31-1.


Respostas:


184

Com o Java 8, é tão simples que nem precisa mais de um método separado:

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

2
Adicionei resultados de desempenho para esta resposta acima com o rótulo intStreamRange .
BeeOnRope

Requer API 24+
gcantoni 20/06

28

Bem, esse liner pode se qualificar (usa Guava Ranges )

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

Isso não cria um List<Integer>, mas ContiguousSetoferece a mesma funcionalidade, em particular a implementação, Iterable<Integer>que permite a foreachimplementação da mesma maneira queList<Integer> .

Nas versões mais antigas (em algum lugar anterior ao Guava 14), você poderia usar isso:

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

Ambos produzem:

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

7
Eu não usaria asList()lá, a menos que você realmente precisasse de List... o ContiguousSetproduzido por asSeté leve (ele só precisa do intervalo e do domínio), mas asList()criará uma lista que realmente armazena todos os elementos na memória (atualmente).
colind

1
Acordado. O OP estava pedindo uma lista ou matriz que, caso contrário eu teria deixado-o para fora
daveb

1
Eu acredito que para o 18.0, Rangeexiste , mas não, Rangese eles acabaram com o asSetmétodo. Na minha versão mais antiga, asSetestá obsoleta e parece que eles a removeram. Aparentemente, as faixas devem ser usadas apenas para coleções contíguas e elas foram aplicadas, embora eu adore esta solução.
Demongolem 13/05

A API agora exige código semelhante a este: ContiguousSet.create (Range.closed (1, count), DiscreteDomain.integers ()
Ben

Adicionei resultados de desempenho para esta resposta acima com o rótulo guavaRange .
BeeOnRope

11

A seguinte versão do Java 8 de uma linha gerará [1, 2, 3 ... 10]. O primeiro argumento de iterateé o primeiro n da sequência e o primeiro argumento de limité o último número.

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

Adicionei resultados de desempenho para esta resposta acima com o rótulo streamIterate .
BeeOnRope

1
Como ponto de esclarecimento, o limite arg não é o último número, é o número de Inteiros na lista.
neilireson

7

Você pode usar a Intervalclasse das Coleções Eclipse .

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

A Intervalclasse é preguiçosa, portanto, não armazena todos os valores.

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

Seu método poderá ser implementado da seguinte maneira:

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

Se você deseja evitar o encaixotamento de ints como números inteiros, mas ainda assim desejar uma estrutura de lista como resultado, poderá usá IntList-lo IntIntervalno Eclipse Collections.

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

IntListtem os métodos sum(), min(), minIfEmpty(), max(), maxIfEmpty(), average()e median()disponível na interface.

Atualização para maior clareza: 27/11/2017

An Intervalé um List<Integer>, mas é preguiçoso e imutável. É extremamente útil para gerar dados de teste, especialmente se você lida muito com coleções. Se você quiser, pode facilmente copiar um intervalo de um List, Setou Bagcomo se segue:

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

An IntIntervalé um ImmutableIntListque se estende IntList. Também possui métodos de conversão.

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

An Intervale an IntIntervalnão têm o mesmo equalscontrato.

Atualização para o Eclipse Collections 9.0

Agora você pode criar coleções primitivas a partir de fluxos primitivos. Existem métodos withAlle ofAlldependendo da sua preferência. Se você está curioso, explico por que temos os dois aqui . Esses métodos existem para listas, conjuntos, malas e pilhas int / longa / dupla mutáveis ​​e imutáveis.

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: Eu sou um confirmador das Coleções Eclipse


Adicionei resultados de desempenho para esta resposta acima com o rótulo eclipseCollections .
BeeOnRope

Arrumado. Atualizei minha resposta com uma versão primitiva adicional que deve evitar qualquer boxe.
Donald Raab

6

Este é o mais curto que pude obter usando o 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
Você pode raspar mais um par de personagens, alterando esse ciclo para for(int i = begin; i <= end; ret.add(i++));:)
vaughandroid

Não tenho muita certeza de que mover a ret.add(i)peça para o incremento do loop for o torne "mais curto". Eu acho que por essa lógica, se eu escrevi tudo em uma linha que seria mais curto :)
BeeOnRope

@BeeOnRope Sim, definitivamente não é o mais curto, mas mais curto em duas linhas, com certeza :) Como eu disse, este é o mais próximo que podemos chegar de encurtá-lo no Core Java.
Adarshr 20/04

Adicionei resultados de desempenho para esta resposta acima com o rótulo classicArrayList .
BeeOnRope

3

Você poderia usar intervalos de goiaba

Você pode obter um SortedSetusando

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

0

Este é o mais curto que pude encontrar.

Versão da lista

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

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

    return ret;
}

Versão da 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;
}

-2

Este pode funcionar para você ....

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

Usar o AtomicInteger é muito pesado para recursos, cerca de dez vezes mais lento no meu teste. Mas é seguro para multithread. fim <início não verificado
cl-r

1
O uso do AtomicInteger não faz sentido dentro de um método. Todas as frases em uma chamada de método são executadas seqüencialmente pelo encadeamento que chamou o método, para que você não obtenha nada do AtomicInteger, a não ser as chamadas getAndIncrement () mais lentas e irritantes.
Igor Rodriguez
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.