Java 8이 값이나 기능을 반복하는 좋은 방법을 제공합니까?
다른 많은 언어에서. Haskell, 값이나 함수를 여러 번 반복하는 것은 쉽습니다. 값 1의 8 개 사본 목록을 얻으려면 :
take 8 (repeat 1)
하지만 Java 8에서는 아직 찾지 못했습니다. Java 8의 JDK에 이러한 기능이 있습니까?
또는 다음과 같은 범위에 해당하는 것
[1..8]
Java의 장황한 진술에 대한 명백한 대체물처럼 보일 것입니다.
for (int i = 1; i <= 8; i++) {
System.out.println(i);
}
같은 것을 가지고
Range.from(1, 8).forEach(i -> System.out.println(i))
이 특정 예제는 실제로 훨씬 더 간결 해 보이지는 않지만 ...하지만 더 읽기 좋기를 바랍니다.
이 특정 예의 경우 다음을 수행 할 수 있습니다.
IntStream.rangeClosed(1, 8)
.forEach(System.out::println);
1과 다른 단계가 필요한 경우, 예를 들어 2 단계에 매핑 기능을 사용할 수 있습니다.
IntStream.rangeClosed(1, 8)
.map(i -> 2 * i - 1)
.forEach(System.out::println);
또는 사용자 지정 반복을 만들고 반복 크기를 제한합니다.
IntStream.iterate(1, i -> i + 2)
.limit(8)
.forEach(System.out::println);
다른 날에 내가 사용한 또 다른 기술이 있습니다.
Collections.nCopies(8, 1)
.stream()
.forEach(i -> System.out.println(i));
이 Collections.nCopies호출은 사용자가 제공하는 모든 값 의 List포함 n복사본을 만듭니다 . 이 경우 박스형 Integer값 1입니다. 물론 실제로 n요소가 있는 목록을 생성하지는 않습니다 . 값과 길이 만 포함하는 "가상화 된"목록을 만들고 get범위 내 호출 은 값을 반환합니다. 이 nCopies메서드는 Collections Framework가 JDK 1.2에서 도입 된 이후로 사용되었습니다. 물론 그 결과로부터 스트림을 생성하는 기능은 Java SE 8에 추가되었습니다.
큰 거래, 거의 동일한 줄 수에서 동일한 작업을 수행하는 또 다른 방법입니다.
그러나이 기술은 IntStream.generate및 IntStream.iterate접근 방식 보다 빠르며 놀랍게도 IntStream.range접근 방식 보다 빠릅니다 .
의 경우 iterate와 generate결과는 아마도 너무 놀라운 일이 아니다. 스트림 프레임 워크 (실제로 이러한 스트림에 대한 분할 자)는 람다가 매번 다른 값을 잠재적으로 생성하고 무제한의 결과를 생성한다는 가정을 기반으로 구축됩니다. 이것은 병렬 분할을 특히 어렵게 만듭니다. 이 iterate메서드는 또한 각 호출에 이전 호출의 결과가 필요하기 때문에 문제가 있습니다. 스트림 사용 그래서 generate및 iterate반복 상수를 생성하기위한 아주 잘하지 않는다.
의 상대적으로 낮은 성능 range은 놀랍습니다. 이것도 가상화되므로 요소가 실제로 모두 메모리에 존재하는 것은 아니며 크기가 미리 알려집니다. 이것은 빠르고 쉽게 병렬화 할 수있는 분할자를 만들 것입니다. 그러나 놀랍게도 잘되지 않았습니다. 아마도 그 이유는 range범위의 각 요소에 대한 값을 계산 한 다음 함수를 호출해야하기 때문일 것입니다. 하지만이 함수는 입력을 무시하고 상수를 반환하므로 인라인 처리되지 않고 종료되지 않은 것이 놀랍습니다.
이 Collections.nCopies기술은 값을 처리하기 위해 boxing / unboxing을 수행해야 List합니다.. 값이 매번 같기 때문에 기본적으로 한 번 상자에 넣어 모든 n복사본에서 해당 상자를 공유합니다 . 나는 boxing / unboxing이 고도로 최적화되고 내재화되어 있고 잘 인라인 될 수 있다고 생각합니다.
코드는 다음과 같습니다.
public static final int LIMIT = 500_000_000;
public static final long VALUE = 3L;
public long range() {
return
LongStream.range(0, LIMIT)
.parallel()
.map(i -> VALUE)
.map(i -> i % 73 % 13)
.sum();
}
public long ncopies() {
return
Collections.nCopies(LIMIT, VALUE)
.parallelStream()
.mapToLong(i -> i)
.map(i -> i % 73 % 13)
.sum();
}
다음은 JMH 결과입니다. (2.8GHz Core2Duo)
Benchmark Mode Samples Mean Mean error Units
c.s.q.SO18532488.ncopies thrpt 5 7.547 2.904 ops/s
c.s.q.SO18532488.range thrpt 5 0.317 0.064 ops/s
ncopies 버전에는 상당한 차이가 있지만 전반적으로 범위 버전보다 20 배 더 빠릅니다. (하지만 내가 뭔가 잘못했다고 믿고 싶어요.)
I'm surprised at how well the nCopies technique works. Internally it doesn't do very much special, with the stream of the virtualized list simply being implemented using IntStream.range! I had expected that it would be necessary to create a specialized spliterator to get this to go fast, but it already seems to be pretty good.
For completeness, and also because I couldn't help myself :)
Generating a limited sequence of constants is fairly close to what you would see in Haskell, only with Java level verboseness.
IntStream.generate(() -> 1)
.limit(8)
.forEach(System.out::println);
Once a repeat function is somewhere defined as
public static BiConsumer<Integer, Runnable> repeat = (n, f) -> {
for (int i = 1; i <= n; i++)
f.run();
};
You can use it now and then this way, e.g.:
repeat.accept(8, () -> System.out.println("Yes"));
To get and equivalent to Haskell's
take 8 (repeat 1)
You could write
StringBuilder s = new StringBuilder();
repeat.accept(8, () -> s.append("1"));
'IT박스' 카테고리의 다른 글
| Javascript는 연중 일을 계산합니다 (1-366). (0) | 2020.08.09 |
|---|---|
| MySQL 데이터베이스 이름 바꾸기 (0) | 2020.08.09 |
| JavaScript에서 regex \ S는 무엇을 의미합니까? (0) | 2020.08.09 |
| Java에서 기본 요소에 대한 참조에 의한 전달과 동등한 작업을 수행하는 방법 (0) | 2020.08.09 |
| SMTP를 사용하여 Python에서 메일 보내기 (0) | 2020.08.09 |