Bagaimana saya bisa menghasilkan daftar atau larik bilangan bulat berurutan di Java?


130

Apakah ada cara yang singkat dan menarik untuk menghasilkan a List<Integer>, atau mungkin an Integer[]atau int[], dengan nilai berurutan dari beberapa startnilai ke endnilai?

Artinya, sesuatu yang lebih pendek dari, tetapi setara dengan 1 berikut ini:

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

Penggunaan jambu biji boleh saja.

Memperbarui:

Analisis Kinerja

Karena pertanyaan ini telah menerima beberapa jawaban bagus, baik menggunakan Java 8 asli dan pustaka pihak ketiga, saya pikir saya akan menguji kinerja semua solusi.

Tes pertama hanya menguji pembuatan daftar 10 elemen [1..10]menggunakan metode berikut:

  • classicArrayList : kode yang diberikan di atas dalam pertanyaan saya (dan pada dasarnya sama dengan jawaban adarshr).
  • eclipseCollections : kode yang diberikan dalam jawaban Donald di bawah ini menggunakan Eclipse Collections 8.0.
  • guavaRange : kode yang diberikan dalam jawaban daveb di bawah ini. Secara teknis, ini tidak membuat List<Integer>melainkan a ContiguousSet<Integer>- tetapi karena diterapkan Iterable<Integer>secara berurutan, sebagian besar berfungsi untuk tujuan saya.
  • intStreamRange : kode yang diberikan dalam jawaban Vladimir di bawah ini, yang menggunakan IntStream.rangeClosed()- yang diperkenalkan di Java 8.
  • streamIterate : kode yang diberikan dalam jawaban Catalin di bawah ini yang juga menggunakan IntStreamfungsionalitas yang diperkenalkan di Java 8.

Berikut adalah hasil dalam kilo-operasi per detik (angka yang lebih tinggi lebih baik), untuk semua di atas dengan daftar ukuran 10:

Buat daftar throughput pembuatan

... dan sekali lagi untuk daftar ukuran 10.000:

masukkan deskripsi gambar di sini

Bagan terakhir itu benar - solusi selain Eclipse dan Guava terlalu lambat bahkan untuk mendapatkan satu batang piksel! Solusi cepatnya 10.000 hingga 20.000 kali lebih cepat daripada yang lain.

Apa yang terjadi di sini, tentu saja, adalah bahwa solusi jambu biji dan gerhana tidak benar-benar mewujudkan daftar 10.000 elemen apa pun - mereka hanya pembungkus berukuran tetap di sekitar titik awal dan akhir. Setiap elemen dibuat sesuai kebutuhan selama iterasi. Karena kami tidak benar-benar mengulang dalam pengujian ini, biayanya ditangguhkan. Semua solusi lain benar-benar mewujudkan daftar lengkap dalam memori dan membayar mahal dalam tolok ukur khusus kreasi.

Mari kita lakukan sesuatu yang sedikit lebih realistis dan juga mengulangi semua bilangan bulat, menjumlahkannya. Jadi dalam kasus IntStream.rangeClosedvarian, tolok ukurnya terlihat seperti:

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

Di sini, gambar banyak berubah, meskipun solusi yang tidak terwujud masih yang tercepat. Berikut panjangnya = 10:

Daftar <Integer> Iterasi (panjang = 10)

... dan panjang = 10.000:

Daftar <Integer> Iterasi (panjang = 10.000)

Iterasi panjang pada banyak elemen meratakan banyak hal, tetapi gerhana dan jambu biji tetap lebih dari dua kali lebih cepat bahkan pada uji 10.000 elemen.

Jadi jika Anda benar - benar menginginkan List<Integer>, koleksi gerhana tampaknya pilihan terbaik - tetapi tentu saja jika Anda menggunakan aliran dengan cara yang lebih asli (misalnya, melupakan .boxed()dan melakukan pengurangan dalam domain primitif) Anda mungkin akan berakhir lebih cepat daripada semua ini. varian.


1 Mungkin dengan pengecualian penanganan kesalahan, misalnya jika end< begin, atau jika ukurannya melebihi beberapa implementasi atau batas JVM (misalnya, array lebih besar dari 2^31-1.


Jawaban:


185

Dengan Java 8 itu sangat sederhana sehingga tidak memerlukan metode terpisah lagi:

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

2
Saya menambahkan hasil kinerja untuk jawaban ini di atas dengan label intStreamRange .
BeeOnRope

Membutuhkan API 24+
gcantoni

28

Nah, liner yang satu ini mungkin memenuhi syarat (menggunakan Guava Ranges )

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

Ini tidak membuat List<Integer>, tetapi ContiguousSetmenawarkan banyak fungsionalitas yang sama, khususnya penerapan Iterable<Integer>yang memungkinkan foreachimplementasi dengan cara yang sama seperti List<Integer>.

Di versi lama (sebelum Guava 14) Anda bisa menggunakan ini:

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

Keduanya menghasilkan:

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

7
Saya tidak akan menggunakannya di asList()sana kecuali Anda benar-benar membutuhkan List... yang ContiguousSetdiproduksi oleh asSetringan (hanya memerlukan jangkauan dan domain), tetapi asList()akan membuat daftar yang benar-benar menyimpan semua elemen dalam memori (saat ini).
ColinD

1
Sepakat. OP meminta Daftar atau larik, jika tidak, saya akan meninggalkannya
daveb

1
Saya percaya untuk 18.0, Rangeada tetapi tidak Rangesdan mereka telah menghapus asSetmetode tersebut. Di versi lama saya, asSetsudah usang dan tampaknya mereka telah menghapusnya. Rentang tampaknya hanya untuk digunakan untuk koleksi yang berdekatan dan mereka telah memberlakukannya meskipun saya sangat menyukai solusi ini.
demongolem

API sekarang membutuhkan kode yang mirip dengan ini: ContiguousSet.create (Range.closed (1, count), DiscreteDomain.integers ()
Ben

Saya menambahkan hasil kinerja untuk jawaban ini di atas dengan label guavaRange .
BeeOnRope

11

Versi Java 8 satu baris berikut akan menghasilkan [1, 2, 3 ... 10]. Argumen pertama iterateadalah nr pertama dalam barisan, dan argumen pertama limitadalah bilangan terakhir.

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

Saya menambahkan hasil kinerja untuk jawaban ini di atas dengan label streamIterate .
BeeOnRope

1
Sebagai poin klarifikasi, argumen limit bukanlah angka terakhir, ini adalah jumlah Integer dalam daftar.
neilires

7

Anda dapat menggunakan Intervalkelas dari Koleksi Eclipse .

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

The Intervalkelas malas, sehingga tidak menyimpan semua nilai-nilai.

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

Metode Anda akan dapat diterapkan sebagai berikut:

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

Jika Anda ingin menghindari tinju int sebagai Integer, tetapi tetap menginginkan struktur daftar sebagai hasilnya, Anda dapat menggunakan IntListwith IntIntervalfrom Eclipse Collections.

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

IntListmemiliki metode sum(), min(), minIfEmpty(), max(), maxIfEmpty(), average()dan median()tersedia pada antarmuka.

Pembaruan untuk kejelasan: 11/27/2017

An Intervaladalah a List<Integer>, tetapi malas dan tidak dapat diubah. Ini sangat berguna untuk menghasilkan data pengujian, terutama jika Anda berurusan dengan banyak koleksi. Jika mau, Anda dapat dengan mudah menyalin interval ke List, Setatau Bagsebagai berikut:

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

An IntIntervaladalah ImmutableIntListyang meluas IntList. Ini juga memiliki metode konverter.

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

An Intervaldan an IntIntervaltidak memiliki equalskontrak yang sama .

Pembaruan untuk Koleksi Eclipse 9.0

Sekarang Anda dapat membuat koleksi primitif dari aliran primitif. Ada withAlldan ofAllmetode tergantung pada preferensi Anda. Jika Anda penasaran, saya jelaskan mengapa kita memiliki keduanya di sini . Metode ini ada untuk Int / Long / Double List, Sets, Bags dan Stacks yang dapat berubah dan tidak berubah.

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

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

Catatan: Saya seorang pelaku untuk Koleksi Eclipse


Saya menambahkan hasil kinerja untuk jawaban ini di atas dengan label eclipseCollections .
BeeOnRope

Rapi. Saya memperbarui jawaban saya dengan versi primitif tambahan yang seharusnya menghindari tinju apa pun.
Donald Raab

6

Ini adalah waktu terpendek yang saya dapat menggunakan 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
Anda dapat memangkas beberapa karakter lagi dengan mengubah putaran itu menjadi for(int i = begin; i <= end; ret.add(i++));:)
vaughandroid

Saya tidak begitu yakin bahwa memindahkan ret.add(i)bagian ke penambahan loop for membuat ini "lebih pendek". Saya kira dengan logika itu jika saya menulis semuanya dalam satu baris, itu akan menjadi lebih pendek :)
BeeOnRope

@BeeOnRope Ya, jelas bukan yang terpendek, tapi pasti lebih pendek dengan dua baris :) Seperti yang saya katakan, ini adalah cara terdekat untuk mempersingkatnya di Core Java.
adarshr

Saya menambahkan hasil kinerja untuk jawaban ini di atas dengan label classicArrayList .
BeeOnRope

3

Anda bisa menggunakan Guava Ranges

Anda bisa mendapatkan SortedSetdengan menggunakan

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

0

Ini adalah waktu terpendek yang bisa saya temukan.

Versi daftar

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 Array

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

Yang ini mungkin berhasil untuk Anda ....

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

Menggunakan AtomicInteger sangat berat untuk sumber daya, sekitar sepuluh kali lebih lambat dalam pengujian saya. Tetapi aman untuk multithread. akhir <mulai tidak diverifikasi
cl-r

1
Penggunaan AtomicInteger tidak masuk akal di dalam metode. Semua kalimat dalam panggilan metode dijalankan secara berurutan oleh utas yang memanggil metode tersebut, jadi Anda tidak mendapatkan apa pun dari AtomicInteger kecuali panggilan getAndIncrement () yang melambat dan mengganggu.
Igor Rodriguez
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.