Java 8 에는 문자 코드를 나타내는 s ( ) String.chars()
스트림을 리턴 하는 새로운 메소드 가 있습니다. 많은 사람들이 대신 스트림을 기대할 것입니다. 이런 식으로 API를 디자인하려는 동기는 무엇입니까?int
IntStream
char
Java 8 에는 문자 코드를 나타내는 s ( ) String.chars()
스트림을 리턴 하는 새로운 메소드 가 있습니다. 많은 사람들이 대신 스트림을 기대할 것입니다. 이런 식으로 API를 디자인하려는 동기는 무엇입니까?int
IntStream
char
답변:
다른 사람들이 이미 언급했듯이, 그 뒤에 디자인 결정은 방법과 클래스의 폭발을 막는 것이 었습니다.
여전히 개인적으로 나는 이것이 매우 나쁜 결정이라고 생각하며, 그들이 CharStream
대신하고 합리적인 다른 방법 을 만들고 싶지 chars()
않다고 생각해야합니다.
Stream<Character> chars()
, 이는 일련의 상자 문자를 제공하며 약간의 성능 저하가 있습니다.IntStream unboxedChars()
성능 코드에 사용될 것입니다.그러나 현재 이러한 방식으로 수행 되는 이유 에 중점을 두는 대신 이 답변은 Java 8에서 얻은 API로 수행하는 방법을 보여주는 데 중점을 두어야한다고 생각합니다.
Java 7에서는 다음과 같이했습니다.
for (int i = 0; i < hello.length(); i++) {
System.out.println(hello.charAt(i));
}
Java 8에서 합리적인 방법은 다음과 같습니다.
hello.chars()
.mapToObj(i -> (char)i)
.forEach(System.out::println);
여기에서을 가져 IntStream
와서 lambda 통해 객체에 매핑 i -> (char)i
하면 자동으로 상자에 상자를 넣은 Stream<Character>
다음 원하는 것을 수행 할 수 있으며 여전히 메서드 참조를 플러스로 사용합니다.
주의 당신이 비록 있어야 할 mapToObj
당신이 잊고 사용하는 경우, map
다음, 아무것도 불평하지 않습니다,하지만 당신은 여전히 끝낼 것입니다 IntStream
, 당신이 그것을 대신 문자를 나타내는 문자열의 정수 값을 인쇄 왜 궁금 중단 될 수 있습니다.
Java 8의 다른 추악한 대안 :
에 남아서 IntStream
궁극적으로 인쇄하려고하면 더 이상 메소드 참조를 인쇄에 사용할 수 없습니다.
hello.chars()
.forEach(i -> System.out.println((char)i));
또한 자신의 메서드에 대한 메서드 참조를 사용하면 더 이상 작동하지 않습니다! 다음을 고려하세요:
private void print(char c) {
System.out.println(c);
}
그리고
hello.chars()
.forEach(this::print);
변환 손실이있을 수 있으므로 컴파일 오류가 발생합니다.
결론:
API는 때문에 추가하고자하는하지의이 방법으로 설계되었습니다 CharStream
, 제가 개인적으로 방법은 반환해야한다고 생각 Stream<Character>
하고, 해결 방법은 현재 사용하는 것입니다 mapToObj(i -> (char)i)
온 IntStream
그들과 함께 제대로 작동 할 수 있도록.
codePoints()
대신에 사용 하는 chars()
것이므로 이미 모든 int
코드 char
메소드 java.lang.Character
를 비롯하여 코드 포인트를 추가로 수용하는 많은 라이브러리 함수를 찾을 수 있습니다 StringBuilder.appendCodePoint
.이 지원은 이후에 존재합니다 jdk1.5
.
String
또는 로 대리 쌍으로 표시됩니다 char[]
. 대부분의 char
처리 코드가 대리 쌍을 잘못 처리 한다고 생각 합니다.
void print(int ch) { System.out.println((char)ch); }
다음 메소드 참조를 사용할 수 있습니다.
Stream<Character>
거부 된 이유 는 내 답변을 참조하십시오 .
Skiwi 의 답변은 이미 많은 주요 사항을 다루었습니다. 조금 더 배경을 채울 것입니다.
모든 API의 디자인은 일련의 트레이드 오프입니다. Java에서 어려운 문제 중 하나는 오래 전에 만들어진 디자인 결정을 다루는 것입니다.
기본 요소는 1.0 이후 Java로 사용되었습니다. 프리미티브는 객체가 아니기 때문에 Java를 "불완전한"객체 지향 언어로 만듭니다. 프리미티브의 추가는 객체 지향 순도를 희생하면서 성능을 향상시키기위한 실용적인 결정이었다고 생각합니다.
이것은 거의 20 년이 지난 지금도 여전히 우리와 함께하고있는 절충안입니다. Java 5에 추가 된 오토 박싱 기능은 대부분 박싱 및 언 박싱 메소드 호출로 소스 코드를 복잡하게 만들 필요가 없었지만 여전히 오버 헤드가 있습니다. 많은 경우 눈에 띄지 않습니다. 그러나 내부 루프 내에서 권투 또는 언 박싱을 수행하는 경우 상당한 CPU 및 가비지 콜렉션 오버 헤드가 발생할 수 있음을 알 수 있습니다.
Streams API를 디자인 할 때 프리미티브를 지원해야한다는 것이 분명했습니다. boxing / unboxing 오버 헤드는 병렬 처리의 성능 이점을 없애줍니다. 우리는 API에 많은 양의 혼란을 가중 시켰기 때문에 모든 프리미티브 를 지원하고 싶지 않았습니다 . (실제로 ShortStream
?를 사용하는 것을 볼 수 있습니까 ?) "모두"또는 "없음"은 디자인하기에 편안한 장소이지만 수용 할 수있는 것은 아닙니다. 그래서 우리는 "일부"의 합리적인 가치를 찾아야했습니다. 우리는 원시적 전문으로 결국 int
, long
하고 double
. (개인적으로 나는 int
제외했을 것입니다. 그러나 그것은 단지 나입니다.)
들어 CharSequence.chars()
우리가 돌아 고려 Stream<Character>
(초기 프로토 타입이 구현했을 수 있습니다)하지만 때문에 권투 오버 헤드 거부되었습니다. 문자열에 char
프리미티브 값이 있다고 가정하면 호출자가 값에 대해 약간의 처리를하고 문자열로 다시 상자를 풀 때 무조건 복싱을 부과하는 것은 실수로 보입니다.
우리는 또한 CharStream
원시 전문화를 고려 했지만 API에 추가 할 대량의 양에 비해 사용 범위가 상당히 좁은 것 같습니다. 그것을 추가하는 것이 가치가 없었습니다.
이것이 발신자에게 부과되는 처벌 은로 표시된 값을 IntStream
포함 하고 캐스팅이 적절한 장소에서 수행되어야한다는 것을 알아야한다는 것입니다. 오버로드 된 API 호출이 있고 동작이 현저하게 다르기 때문에 이는 혼동을 일으 킵니다 . 호출이 또한 값을 반환 하지만 포함 된 값이 상당히 다르기 때문에 추가적인 혼란이 발생할 수 있습니다.char
ints
PrintStream.print(char)
PrintStream.print(int)
codePoints()
IntStream
따라서 이것은 여러 대안 중에서 실용적으로 선택하는 것으로 요약됩니다.
우리는 기본 전문화를 제공 할 수 없어 단순하고 우아하며 일관된 API를 만들 수 있지만 고성능 및 GC 오버 헤드가 발생합니다.
API를 어지럽히고 JDK 개발자에게 유지 보수 부담을 부과하는 비용으로 완전한 기본 전문화 세트를 제공 할 수 있습니다. 또는
우리는 프리미티브 전문화의 부분 집합을 제공하여 상당히 좁은 범위의 유스 케이스 (char processing)에서 호출자에게 상대적으로 적은 부담을주는 적당한 크기의 고성능 API를 제공 할 수 있습니다.
우리는 마지막 것을 선택했습니다.
chars()
, 반환이있는 한 가지 Stream<Character>
다른 존재 (작은 성능 저하와 함께) IntStream
,이 또한 고려되었다? 사람들이 Stream<Character>
편의상 성능 저하에 비해 그 가치가 있다고 생각한다면 사람들은 어쨌든 그것을 매핑 할 가능성이 큽니다 .
chars()
char 값을 반환하는 메소드가 이미있는 경우 IntStream
동일한 값을 얻지 만 상자 형식의 다른 API 호출을 많이 추가하지는 않습니다. 호출자는 많은 어려움없이 값을 박스에 넣을 수 있습니다. 이 경우 (아마 드문 경우)에는이 작업을 수행하지 않아도되지만 API에 혼란을 추가하는 것이 더 편리합니다.
chars()
귀환 IntStream
은 큰 문제가되지 않는다는 데 동의한다 . 그러나 다시 변환하는 내장 된 방법이 좋은 것입니다 IntStream
받는 사람을 String
. 그것은 할 수 .reduce(StringBuilder::new, (sb, c) -> sb.append((char)c), StringBuilder::append).toString()
있지만 정말 길다.
collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString()
. 실제로 짧지는 않지만 코드 포인트를 사용하면 (char)
캐스트를 피하고 메소드 참조를 사용할 수 있습니다. 또한 대리자를 제대로 처리합니다.
IntStream
에는을 사용하는 collect()
메소드 가 없습니다 Collector
. collect()
이전 의견에서 언급했듯이 3 개의 인수 방법 만 있습니다.
CharStream
존재하지 않는 경우 추가하는 데 문제점이 있습니까?