Java에서 순차 정수 목록 또는 배열을 생성하려면 어떻게해야합니까?


130

를 생성하는 짧고 달콤한 방법이 있나요 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 및 타사 라이브러리를 사용하여 몇 가지 좋은 답변을 받았으므로 모든 솔루션의 성능을 테스트 할 것이라고 생각했습니다.

첫 번째 테스트 [1..10]는 다음 방법을 사용하여 10 개의 요소 목록을 만드는 것을 테스트 합니다.

  • classicArrayList : 내 질문에서 위에 주어진 코드 (그리고 본질적으로 adarshr의 대답과 동일).
  • eclipseCollections : Eclipse Collections 8.0을 사용하여 아래 Donald의 답변에 제공된 코드 .
  • guavaRange : 아래 daveb 의 답변에 제공된 코드 . 기술적으로 이것은 a를 생성하는 List<Integer>것이 아니라 순서대로 ContiguousSet<Integer>구현 Iterable<Integer>하기 때문에 대부분 내 목적에 적합합니다.
  • intStreamRange : 아래 Vladimir의 답변에 제공된 코드 IntStream.rangeClosed()로 Java 8에서 도입 된-를 사용합니다 .
  • streamIterate : Java 8에 도입 된 기능 을 사용하는 아래 Catalin의 답변에 제공된 코드 IntStream입니다.

다음은 목록 크기가 10 인 위의 모든 항목에 대한 초당 킬로 작업 (숫자가 클수록 좋음)의 결과입니다.

목록 생성 처리량

... 그리고 다시 한번 크기가 10,000 인 목록 :

여기에 이미지 설명 입력

마지막 차트는 정확합니다. Eclipse 및 Guava 이외의 솔루션은 너무 느려 단일 픽셀 막대를 얻을 수도 없습니다! 빠른 솔루션은 나머지 솔루션보다 10,000 ~ 20,000 배 빠릅니다.

물론 여기서 진행되는 것은 구아바와 이클립스 솔루션이 실제로 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입니다.

List <Integer> 반복 (길이 = 10)

... 및 길이 = 10,000 :

List <Integer> 반복 (길이 = 10,000)

많은 요소에 대한 긴 반복은 많은 것을 고르게하지만 일식과 구아바는 10,000 개의 요소 테스트에서도 두 배 이상 빠르게 유지됩니다.

따라서 정말로List<Integer> , eclipse 컬렉션을 원한다면 이클립스 컬렉션이 최선의 선택 인 것처럼 보이지만 물론 더 네이티브 방식으로 스트림을 사용한다면 (예 : .boxed()원시 도메인을 잊어 버리고 축소하는 것) 아마이 모든 것보다 더 빨리 끝날 것입니다. 변형.


1 오류 처리 (예 : end< begin인 경우) 또는 크기가 일부 구현 또는 JVM 제한을 초과하는 경우 (예 : 2^31-1.


아파치 평민, 들어 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 이상 필요
— gcantoni

28

글쎄,이 라이너는 자격이 될 수 있습니다 ( 구아바 범위 사용 )

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

이것은을 만들지 않습니다 List<Integer>,하지만 ContiguousSet구현 특히, 거의 같은 기능을 제공 Iterable<Integer>할 수있는 foreach같은 방법으로 구현 List<Integer>.

구아바 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 2012

1
동의합니다. 영업 그렇지 않으면 내가 그것을 밖으로 떠난 것, 목록 또는 배열 생각을 요청했다
— 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
설명의 요점으로 limit arg는 마지막 숫자가 아니라 목록의 정수 개수입니다.
— neilireson

7

Eclipse Collections 의 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);
}

int를 Integers로 사용하는 것을 피하고 싶지만 결과적으로 목록 구조를 원하는 경우 Eclipse Collections에서 IntListwith IntInterval를 사용할 수 있습니다 .

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

IntList이 방법 sum(), min(), minIfEmpty(), max(), maxIfEmpty(), average()및 median()인터페이스에 사용할 수 있습니다.

명확성을위한 업데이트 : 2017 년 11 월 27 일

An 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();

An Interval과 an IntInterval은 동일한 equals계약을 가지고 있지 않습니다 .

Eclipse Collections 9.0 업데이트

이제 기본 스트림에서 기본 컬렉션을 만들 수 있습니다. 있다 withAll및 ofAll환경 설정에 따라 방법. 궁금한 점이 있으면 여기에 둘 다있는 이유를 설명 합니다 . 이러한 메소드는 변경 가능하고 변경 불가능한 Int / Long / Double 목록, 세트, ​​백 및 스택에 존재합니다.

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 컬렉션 커미터입니다.


이 답변에 대한 성능 결과를 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)부분을 ​​for 루프 증분으로 이동 하면 이것이 "더 짧아지는" 지 확실하지 않습니다 . 나는 :)이 짧은 것 한 줄에 모두 쓴 경우 그 논리에 의해 추측
— 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를 사용하는 것은 리소스에 매우 무겁고 테스트에서 약 10 배 느립니다. 그러나 다중 스레드의 경우 안전합니다. end < 확인되지 않은 시작
— cl-r

1
AtomicInteger의 사용은 메서드 내에서 의미가 없습니다. 메서드 호출의 모든 문장은 메서드를 호출 한 스레드에 의해 순차적으로 실행되므로 AtomicInteger에서 아무것도 얻지 못하지만 속도가 느려지고 성가신 getAndIncrement () 호출이 발생합니다.
— Igor Rodriguez
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.