Java'da sıralı tamsayılar listesi veya dizisini nasıl oluşturabilirim?


130

Bir değerden bir değere sıralı değerlerle List<Integer>bir Integer[]veya belki bir veya oluşturmanın kısa ve tatlı bir yolu var mı ?int[]startend

Yani, aşağıdakilerden daha kısa, ancak aşağıdakilerden 1'e eşdeğer bir şey :

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

Guava kullanımı gayet iyi.

Güncelleme:

Performans analizi

Bu soru hem yerel Java 8 hem de üçüncü taraf kitaplıkları kullanarak birçok iyi yanıt aldığından, tüm çözümlerin performansını test edeceğimi düşündüm.

İlk test [1..10], aşağıdaki yöntemleri kullanarak 10 öğeden oluşan bir liste oluşturmayı test eder:

  • classicArrayList : sorumda yukarıda verilen kod (ve esasen adarshr'ın cevabı ile aynı).
  • eclipseCollections : Donald'ın Eclipse Collections 8.0'ı kullanarak aşağıdaki yanıtında verilen kod .
  • guavaRange : aşağıdaki daveb'in cevabında verilen kod . Teknik olarak, bu bir yaratmaz, List<Integer>aksine bir ContiguousSet<Integer>- ama Iterable<Integer>sırayla uyguladığından , çoğunlukla benim amaçlarım için çalışır.
  • intStreamRange : Vladimir'in aşağıdaki cevabında verilen kod IntStream.rangeClosed(), Java 8'de tanıtılan.
  • streamIterate : Catalin'in aşağıdaki yanıtında verilen IntStreamve Java 8'de tanıtılan işlevselliği de kullanan kod .

Aşağıda, 10 büyüklüğünde listelerle yukarıdakilerin tümü için saniye başına kilo işlemlerinin sonuçları (daha yüksek sayılar daha iyidir) verilmiştir:

Liste oluşturma verimi

... ve yine 10.000 büyüklüğündeki listeler için:

görüntü açıklamasını buraya girin

Bu son grafik doğrudur - Eclipse ve Guava dışındaki çözümler, tek bir piksel çubuğu bile alamayacak kadar yavaştır! Hızlı çözümler diğerlerinden 10.000 ila 20.000 kat daha hızlıdır.

Elbette burada olan şey, guava ve tutulma çözümlerinin gerçekte herhangi bir 10.000 öğe listesini gerçekleştirmemesidir - bunlar başlangıç ​​ve bitiş noktaları etrafında sabit boyutlu sarmalayıcılardır. Her öğe, yineleme sırasında gerektiği gibi oluşturulur. Bu testte aslında yineleme yapmadığımız için, maliyet ertelendi. Diğer tüm çözümler aslında tam listeyi bellekte somutlaştırır ve yalnızca yaratım için bir kıyaslamada ağır bir bedel öder.

Biraz daha gerçekçi bir şey yapalım ve ayrıca tüm tam sayıları tekrarlayıp toplayalım. Dolayısıyla, IntStream.rangeClosedvaryant durumunda , kıyaslama şöyle görünür:

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

Burada, somutlaşmayan çözümler hala en hızlı olmasına rağmen, resimler çok değişiyor. İşte uzunluk = 10:

Liste <Tamsayı> Yinelemesi (uzunluk = 10)

... ve uzunluk = 10.000:

Liste <Tamsayı> Yinelemesi (uzunluk = 10.000)

Pek çok öğe üzerindeki uzun yineleme, işleri çok fazla eşitler, ancak tutulma ve guava, 10.000 öğe testinde bile iki katından daha hızlı kalır.

Öyleyse, gerçekten bir List<Integer>tutulma koleksiyonları istiyorsanız en iyi seçenek gibi görünüyor - ancak tabii ki akışları daha doğal bir şekilde kullanırsanız (örneğin, unutmak .boxed()ve ilkel alanda bir azalma yapmak) muhtemelen tüm bunlardan daha hızlı sonuçlanacaksınız. varyantları.


1 Belki hata işleme haricinde, örn. end< beginİse veya boyut bazı uygulama veya JVM sınırlarını aşarsa (örn., 2^31-1.


Yanıtlar:


185

Java 8 ile o kadar basit ki artık ayrı bir yönteme bile ihtiyaç duymuyor:

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

2
Yukarıdaki yanıt için performans sonuçlarını intStreamRange etiketiyle ekledim .
BeeOnRope

API 24+ gerektirir
gcantoni

28

Bu tek astar uygun olabilir ( Guava Serilerini kullanır )

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

Bu bir yaratmaz List<Integer>, ancak ContiguousSetaynı işlevselliği sunar, özellikle de aynı şekilde uygulamaya Iterable<Integer>izin veren foreachuygulama List<Integer>.

Eski sürümlerde (Guava 14'ten önceki bir yerde) bunu kullanabilirsiniz:

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

Her ikisi de üretir:

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

7
asList()Gerçekten ihtiyacınız olmadıkça orayı kullanmazdım List... ContiguousSettarafından üretilen asSethafiftir (sadece aralığa ve etki alanına ihtiyaç duyar), ancak asList()tüm öğeleri bellekte (şu anda) depolayan bir liste oluşturur.
ColinD

1
Kabul. OP bir Liste veya dizi
istiyordu

1
18.0'a inanıyorum, Rangevar ama yok Rangesve asSetyöntemi ortadan kaldırdılar. Eski asSetsürümümde kullanımdan kaldırıldı ve kaldırılmış gibi görünüyor. Görünüşe göre aralıklar yalnızca bitişik koleksiyonlar için kullanılacak ve bu çözümü sevmeme rağmen bunu uyguladılar.
demongolem

API artık buna benzer bir kod gerektiriyor: ContiguousSet.create (Range.closed (1, count), DiscreteDomain.integers ()
Ben

Bu cevap için performans sonuçlarını guavaRange etiketiyle ekledim .
BeeOnRope

11

Aşağıdaki tek satırlık Java 8 sürümü [1, 2, 3 ... 10] oluşturacaktır. İlk argüman iteratedizideki ilk nr ve ilk argüman limitson sayıdır.

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

Yukarıdaki yanıt için performans sonuçlarını streamIterate etiketiyle ekledim .
BeeOnRope

1
Açıklığa kavuşturmak için, limit arg, son sayı değil, listedeki Tamsayıların sayısıdır.
neilireson

7

Eclipse Collections'danInterval sınıfı kullanabilirsiniz .

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

IntervalSınıf tembel, bu nedenle tüm değerleri saklamaz.

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

Yönteminiz aşağıdaki şekilde uygulanabilecektir:

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

Eğer Tamsayılar olarak boks ints önlemek istiyoruz, ama yine de sonuç olarak bir liste yapısı gibi, o zaman kullanabilirsiniz istiyorsanız IntListile IntIntervalEclipse Koleksiyonları'ndan.

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

IntListsahip yöntemleri sum(), min(), minIfEmpty(), max(), maxIfEmpty(), average()ve median()arabirim üzerinde kullanılabilir.

Netlik için güncelleme: 11/27/2017

Bir Intervalbir olduğunu List<Integer>, ancak tembel ve sabittir. Özellikle koleksiyonlarla çok uğraşıyorsanız, test verileri oluşturmak için son derece kullanışlıdır. İsterseniz bir için bir aralık kolaylıkla kopyalayabilirsiniz List, Setya Bagaşağıdaki gibi:

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

An IntInterval, ImmutableIntListgenişleyen bir şeydir IntList. Ayrıca dönüştürücü yöntemleri de vardır.

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

An Intervalve bir IntIntervalaynı equalssözleşmeye sahip değildir .

Eclipse Koleksiyonları 9.0 Güncellemesi

Artık ilkel akışlardan ilkel koleksiyonlar oluşturabilirsiniz. Tercihinize bağlı olarak withAllve ofAllyöntemler vardır . Merak ediyorsanız, neden ikimizin de burada olduğunu açıklarım . Bu yöntemler, değiştirilebilir ve değişmez Int / Long / Double Listeler, Setler, Çantalar ve Yığınlar için mevcuttur.

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

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

Not: Eclipse Koleksiyonlarının sorumlusuyum


Bu cevap için performans sonuçlarını eclipseCollections etiketiyle ekledim .
BeeOnRope

Temiz. Cevabımı herhangi bir kutudan kaçınması gereken ek bir ilkel sürümle güncelledim.
Donald Raab

6

Bu, Core Java'yı kullanarak alabildiğim en kısa süre.

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
Bu döngüyü değiştirerek birkaç karakteri daha tıraş edebilirsiniz for(int i = begin; i <= end; ret.add(i++));:)
vaughandroid

ret.add(i)Parçayı for döngüsü artışına taşımanın bunu "kısaltacağından" emin değilim . Sanırım bu mantığa göre hepsini tek bir satıra
yazarsam

@BeeOnRope Evet, kesinlikle en kısa değil, ama kesinlikle iki satır daha kısa :) Dediğim gibi Core Java'da kısaltmaya en yakın olanı bu.
adarshr

Bu cevap için yukarıda klasikArrayList etiketiyle performans sonuçlarını ekledim .
BeeOnRope

3

Guava Serilerini kullanabilirsiniz

SortedSetKullanarak bir alabilirsiniz

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

0

Bu bulabildiğim en kısası.

Liste sürümü

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

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

    return ret;
}

Dizi Sürümü

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

Bu sizin için işe yarayabilir ...

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

AtomicInteger'ı kullanmak kaynaklar için çok ağır, testimde yaklaşık on kat daha yavaş. Ancak çoklu iş parçacığı için güvenlidir. bitiş < doğrulanmadı
cl-r

1
AtomicInteger kullanımı, bir yöntemin içinde anlam ifade etmez. Bir yöntem çağrısındaki tüm cümleler, yöntemi çağıran iş parçacığı tarafından sırayla çalıştırılır, böylece AtomicInteger'dan yavaşlama ve sinir bozucu getAndIncrement () çağrılarından başka hiçbir şey elde edemezsiniz.
Igor Rodriguez
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.