ฉันจะสร้างรายการหรืออาร์เรย์ของจำนวนเต็มตามลำดับใน Java ได้อย่างไร


130

มีวิธีที่สั้นและไพเราะในการสร้าง a List<Integer>หรืออาจเป็น Integer[]หรือint[]ด้วยค่าลำดับจากค่าบางstartค่าเป็นendค่าหนึ่ง?

นั่นคือสิ่งที่สั้นกว่า แต่เทียบเท่ากับ1สิ่งต่อไปนี้:

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

ฝรั่งใช้ก็ใช้ได้

ปรับปรุง:

การวิเคราะห์ประสิทธิภาพ

เนื่องจากคำถามนี้ได้รับคำตอบที่ดีหลายประการทั้งการใช้ Java 8 ดั้งเดิมและไลบรารีของบุคคลที่สามฉันจึงคิดว่าจะทดสอบประสิทธิภาพของโซลูชันทั้งหมด

การทดสอบครั้งแรกเป็นการทดสอบการสร้างรายการองค์ประกอบ 10 รายการ[1..10]โดยใช้วิธีการต่อไปนี้:

  • classicArrayList : รหัสที่ให้ไว้ข้างต้นในคำถามของฉัน (และโดยพื้นฐานแล้วเหมือนกับคำตอบของ adarshr)
  • eclipseCollections : รหัสที่ระบุในคำตอบของ Donaldด้านล่างโดยใช้ Eclipse Collections 8.0
  • guavaRange : รหัสที่ระบุในคำตอบของ davebด้านล่าง ในทางเทคนิคสิ่งนี้ไม่ได้สร้างList<Integer>แต่เป็นContiguousSet<Integer>- แต่เนื่องจากมันใช้งานตามIterable<Integer>ลำดับส่วนใหญ่จึงใช้ได้กับวัตถุประสงค์ของฉัน
  • intStreamRange : รหัสที่ระบุในคำตอบของ Vladimirด้านล่างซึ่งใช้IntStream.rangeClosed()- ซึ่งเปิดตัวใน Java 8
  • streamIterate : รหัสที่ระบุในคำตอบของ Catalinด้านล่างซึ่งใช้IntStreamฟังก์ชันการทำงานที่แนะนำใน Java 8

นี่คือผลลัพธ์ในการดำเนินการกิโลวัตต์ต่อวินาที (ตัวเลขที่สูงกว่าจะดีกว่า) สำหรับรายการทั้งหมดข้างต้นพร้อมรายการขนาด 10:

ปริมาณงานการสร้างรายการ

... และอีกครั้งสำหรับรายการขนาด 10,000:

ใส่คำอธิบายภาพที่นี่

แผนภูมิสุดท้ายนั้นถูกต้อง - โซลูชันอื่นที่ไม่ใช่ Eclipse และ Guava นั้นช้าเกินไปที่จะได้รับแถบพิกเซลเดียว! โซลูชันที่รวดเร็วนั้นเร็วกว่าที่เหลือ10,000 ถึง 20,000 เท่า

สิ่งที่เกิดขึ้นที่นี่แน่นอนว่าโซลูชัน guava และ eclipse ไม่ได้สร้างรายการองค์ประกอบ 10,000 ชนิดใด ๆ - พวกมันเป็นเพียงเครื่องห่อขนาดคงที่รอบจุดเริ่มต้นและจุดสิ้นสุด แต่ละองค์ประกอบถูกสร้างขึ้นตามความจำเป็นในระหว่างการทำซ้ำ เนื่องจากเราไม่ได้ทำซ้ำในการทดสอบนี้ค่าใช้จ่ายจึงรอการตัดบัญชี โซลูชันอื่น ๆ ทั้งหมดทำให้รายการทั้งหมดเป็นจริงในหน่วยความจำและจ่ายราคาหนักในเกณฑ์มาตรฐานการสร้างเท่านั้น

มาทำบางสิ่งที่เป็นจริงมากขึ้นและทำซ้ำบนจำนวนเต็มทั้งหมดรวมเข้าด้วยกัน ดังนั้นในกรณีของIntStream.rangeClosedตัวแปรเกณฑ์มาตรฐานจะมีลักษณะดังนี้:

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

ที่นี่รูปภาพเปลี่ยนไปมากแม้ว่าโซลูชันที่ไม่เป็นรูปธรรมจะยังคงเร็วที่สุด นี่คือความยาว = 10:

รายการ <Integer> การวนซ้ำ (ความยาว = 10)

... และความยาว = 10,000:

รายการ <Integer> การวนซ้ำ (ความยาว = 10,000)

การวนซ้ำที่ยาวนานในหลาย ๆ องค์ประกอบทำให้หลาย ๆ อย่างมีมากขึ้น แต่คราสและฝรั่งยังคงเร็วกว่าสองเท่าแม้ในการทดสอบ 10,000 องค์ประกอบ

ดังนั้นหากคุณต้องการจริงๆList<Integer>คอลเลกชัน eclipse ดูเหมือนจะเป็นทางเลือกที่ดีที่สุด - แต่แน่นอนว่าถ้าคุณใช้สตรีมในแบบเนทีฟมากขึ้น (เช่นลืม.boxed()และลดโดเมนดั้งเดิม) คุณอาจจะจบลงเร็วกว่าสิ่งเหล่านี้ทั้งหมด สายพันธุ์


1บางทีอาจมีข้อยกเว้นในการจัดการข้อผิดพลาดเช่นถ้าend< beginหรือขนาดเกินขีด จำกัด ของการนำไปใช้งานหรือ JVM บางส่วน (เช่นอาร์เรย์ที่ใหญ่กว่า2^31-1.


สำหรับ apache commons, stackoverflow.com/a/5744861/560302
MoveFast

คำตอบ:


185

ด้วย Java 8 นั้นง่ายมากดังนั้นจึงไม่จำเป็นต้องใช้วิธีแยกอีกต่อไป:

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

2
ฉันเพิ่มผลการปฏิบัติงานสำหรับคำตอบข้างต้นนี้มีป้ายintStreamRange
BeeOnRope

ต้องใช้ API
24+

28

ซับนี้อาจมีคุณสมบัติ (ใช้Guava Ranges )

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

นี้ไม่ได้สร้างList<Integer>แต่ContiguousSetข้อเสนอมากฟังก์ชันการทำงานเดียวกันโดยเฉพาะอย่างยิ่งการดำเนินการIterable<Integer>ซึ่งจะช่วยให้การดำเนินการในลักษณะเดียวกับที่foreachList<Integer>

ในเวอร์ชันเก่า (ก่อน Guava 14) คุณสามารถใช้สิ่งนี้:

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

ทั้งสองผลิต:

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

7
ฉันจะไม่ใช้ที่asList()นั่นเว้นแต่คุณจะต้องการList... ที่ContiguousSetผลิตโดยasSetมีน้ำหนักเบา (เพียงแค่ต้องการช่วงและโดเมน) แต่asList()จะสร้างรายการที่เก็บองค์ประกอบทั้งหมดไว้ในหน่วยความจำ (ปัจจุบัน)
ColinD

1
ตกลง OP กำลังขอ List หรืออาร์เรย์ไม่เช่นนั้นฉันจะทิ้งมันไว้
daveb

1
ฉันเชื่อว่าสำหรับ 18.0 นั้นRangeมีอยู่จริง แต่ไม่ใช่Rangesและพวกเขาได้เลิกใช้asSetวิธีนี้แล้ว ในเวอร์ชันเก่าของฉันasSetเลิกใช้งานแล้วและดูเหมือนว่าพวกเขาได้ลบออกไปแล้ว เห็นได้ชัดว่าช่วงจะใช้สำหรับคอลเลกชันที่อยู่ติดกันเท่านั้นและพวกเขาได้บังคับใช้แม้ว่าฉันจะชอบโซลูชันนี้
demongolem

ตอนนี้ API ต้องใช้รหัสที่คล้ายกันนี้: ContiguousSet.create (Range.closed (1, count), DiscreteDomain.integers ()
Ben

ฉันเพิ่มผลการปฏิบัติงานสำหรับคำตอบข้างต้นนี้มีป้ายguavaRange
BeeOnRope

11

Java 8 เวอร์ชันซับเดียวต่อไปนี้จะสร้าง [1, 2, 3 ... 10] อาร์กิวเมนต์แรกของiterateคือ nr แรกในลำดับและอาร์กิวเมนต์แรกlimitคือจำนวนสุดท้าย

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

ฉันเพิ่มผลการปฏิบัติงานสำหรับคำตอบข้างต้นนี้มีป้ายstreamIterate
BeeOnRope

1
เพื่อเป็นการชี้แจงขีด จำกัด อาร์กิวเมนต์ไม่ใช่ตัวเลขสุดท้าย แต่เป็นจำนวนจำนวนเต็มในรายการ
neilireson

7

คุณสามารถใช้Intervalระดับจากคราสคอลเลกชัน

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

Intervalชั้นเป็นคนขี้เกียจดังนั้นจะไม่จัดเก็บค่าทั้งหมด

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

วิธีการของคุณสามารถนำไปใช้งานได้ดังนี้:

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

หากคุณต้องการที่จะหลีกเลี่ยง ints มวยเป็นจำนวนเต็ม แต่ยังคงเช่นโครงสร้างรายการเป็นผลแล้วคุณสามารถใช้IntListกับIntIntervalจากคราสคอลเลกชัน

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

IntListมีวิธีการsum(), min(), minIfEmpty(), max(), maxIfEmpty(), average()และmedian()ที่มีอยู่บนอินเตอร์เฟซ

ปรับปรุงเพื่อความชัดเจน: 27/11/2017

Intervalเป็นList<Integer>แต่มันเป็นขี้เกียจและไม่เปลี่ยนรูป เป็นประโยชน์อย่างยิ่งสำหรับการสร้างข้อมูลการทดสอบโดยเฉพาะอย่างยิ่งหากคุณจัดการกับคอลเล็กชันจำนวนมาก ถ้าคุณต้องการคุณสามารถคัดลอกช่วงเวลาไปList, SetหรือBagดังนี้

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

IntIntervalเป็นซึ่งทอดตัวImmutableIntList IntListนอกจากนี้ยังมีวิธีการแปลง

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

IntervalและIntIntervalไม่ได้มีเดียวกันequalsสัญญา

อัพเดตสำหรับEclipse Collections 9.0

ตอนนี้คุณสามารถสร้างคอลเลกชันดั้งเดิมจากสตรีมดั้งเดิมได้แล้ว มีwithAllและofAllวิธีการขึ้นอยู่กับความชอบของคุณ หากคุณมีความอยากรู้อยากเห็นผมอธิบายว่าทำไมเรามีทั้งที่นี่ วิธีการเหล่านี้มีอยู่สำหรับ Int / Long / Double Lists, Sets, Bags และ Stacks ที่เปลี่ยนแปลงได้และไม่เปลี่ยนรูป

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

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

หมายเหตุ: ฉันเป็นผู้กำหนดค่า Eclipse Collections


ฉันเพิ่มผลการปฏิบัติงานสำหรับคำตอบข้างต้นนี้มีป้ายeclipseCollections
BeeOnRope

เรียบร้อย ฉันอัปเดตคำตอบของฉันด้วยเวอร์ชันดั้งเดิมเพิ่มเติมที่ควรหลีกเลี่ยงการชกมวย
Donald Raab

6

นี่เป็นวิธีที่สั้นที่สุดที่ฉันจะได้รับจาก 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
คุณสามารถกำจัดตัวละครได้อีกสองสามตัวโดยเปลี่ยนลูปนั้นเป็นfor(int i = begin; i <= end; ret.add(i++));:)
vaughandroid

ฉันไม่แน่ใจจริงๆว่าการย้ายret.add(i)ส่วนไปยังการเพิ่มสำหรับลูปทำให้ "สั้นลง" ฉันเดาตามตรรกะนั้นถ้าฉันเขียนมันทั้งหมดในบรรทัดเดียวมันจะสั้นลง :)
BeeOnRope

@BeeOnRope ใช่ไม่ใช่สั้นที่สุด แต่สั้นกว่าสองบรรทัดแน่นอน :) อย่างที่บอกนี่เป็นสิ่งที่ใกล้เคียงที่สุดที่เราสามารถย่อลงใน Core Java ได้
adarshr

ฉันเพิ่มผลการปฏิบัติงานสำหรับคำตอบข้างต้นนี้มีป้ายclassicArrayList
BeeOnRope

3

คุณสามารถใช้Guava Ranges

คุณสามารถรับได้SortedSetโดยใช้ไฟล์

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

0

นี่คือสิ่งที่สั้นที่สุดที่ฉันหาได้

รายการเวอร์ชัน

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

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

    return ret;
}

เวอร์ชันอาร์เรย์

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

อันนี้อาจเหมาะกับคุณ ....

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 นั้นหนักมากสำหรับ ressources ซึ่งช้ากว่าในการทดสอบของฉันประมาณสิบเท่า แต่ปลอดภัยสำหรับมัลติเธรด สิ้นสุด <เริ่มไม่ได้รับการยืนยัน
cl-r

1
การใช้ AtomicInteger ไม่สมเหตุสมผลภายในวิธีการ ประโยคทั้งหมดในการเรียกใช้เมธอดจะถูกรันตามลำดับโดยเธรดที่เรียกว่าเมธอดดังนั้นคุณจึงไม่ได้รับอะไรเลยจากการเรียก AtomicInteger แต่การเรียก getAndIncrement () ที่ช้าลงและน่ารำคาญ
Igor Rodriguez
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.