source

Java 8에서 String.chars()가 int의 스트림인 이유는 무엇입니까?

gigabyte 2022. 8. 29. 22:16
반응형

Java 8에서 String.chars()가 int의 스트림인 이유는 무엇입니까?

Java 8에서는 스트림을 반환하는 새로운 방법이 있습니다.ints (IntStream문자 코드를 나타냅니다.많은 분들이 기대하실 것 같아요.char대신 여기에 있습니다.API를 이렇게 설계한 동기는 무엇입니까?

다른 사람들이 이미 언급했듯이, 이 배경의 설계 결정은 방법과 클래스의 폭증을 막기 위한 것이었다.

여전히, 개인적으로, 나는 이것이 매우 나쁜 결정이었다고 생각한다, 그리고 그들이 원하지 않는 것을 고려할 때,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람다를 통해 물체에 매핑합니다.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그들과 제대로 일할 수 있는 기회입니다.

스키위로부터의 답변은 이미 많은 주요 사항을 포함했다.배경화면을 조금 더 채워볼게요.

API 설계는 일련의 트레이드오프입니다.Java에서 어려운 문제 중 하나는 오래 전에 내려진 디자인 결정을 처리하는 것입니다.

프리미티브는 1.0부터 Java에 있습니다.원형이 개체가 아니기 때문에 Java를 "불순한" 객체 지향 언어로 만듭니다.원형을 추가한 것은 객체 지향의 순수성을 희생하면서 성능을 향상시키려는 실용적인 결정이었다고 생각합니다.

이것은 20년이 지난 오늘날에도 여전히 우리가 살고 있는 트레이드오프입니다.Java 5에 추가된 자동 박싱 기능을 통해 소스 코드를 박싱 및 언박싱 메서드 호출로 복잡하게 만들 필요가 거의 없지만 오버헤드는 그대로입니다.눈에 띄지 않는 경우가 많습니다.그러나 내부 루프 내에서 박스화 또는 박스화 해제를 수행하면 CPU 및 가비지 수집 오버헤드가 크게 발생할 수 있습니다.

Streams API를 설계할 때 기본 요소를 지원해야 했습니다.박싱/언박싱 오버헤드로 인해 병렬 처리로 인한 성능상의 이점이 사라집니다.단, API에 큰 혼란이 생기기 때문에 모든 기본 요소를 지원하고 싶지는 않았습니다.(실제로 API의 용도를 알 수 있습니까?ShortStream?) "모두" 또는 "없음"은 설계에 적합한 장소이지만 둘 다 허용되지 않습니다.그래서 우리는 "일부"라는 합리적인 값을 찾아야 했다.우리는 결국 에 대한 원시적인 전문화를 하게 되었다.int,long,그리고.double(개인적으로는 생략하고int하지만 그건 저뿐입니다.)

위해서CharSequence.chars()우리는 복귀를 고려했다Stream<Character>(초기 프로토타입이 이를 구현했을 수 있음) 그러나 박스 오버헤드 때문에 거부되었습니다.스트링에 있는 것을 고려하면char발신자가 값을 조금만 처리하고 스트링으로 바로 되돌리려고 할 때 무조건 박스를 부과하는 것은 잘못된 것 같습니다.

또,CharStream원시적인 전문화는 API에 추가할 부피의 양에 비해 그 용도는 상당히 좁아 보입니다.그것을 추가할 가치가 없어 보였다.

이것에 의해 발신자에게 부과되는 벌칙은, 발신자는, 다음의 점에 주의할 필요가 있는 것입니다.IntStream포함하다char로서 나타나는 가치관ints캐스팅은 적절한 장소에서 해야 합니다.이는 오버로드된 API 호출이 있기 때문에 이중으로 혼란스럽다.PrintStream.print(char)그리고.PrintStream.print(int)눈에 띄게 다른 행동을 보입니다또 다른 혼란의 원인은 아마도 다음과 같습니다.codePoints()call도 반환한다.IntStream하지만 그 안에 담긴 가치들은 상당히 다릅니다.

즉, 다음과 같은 몇 가지 대안 중에서 실용적으로 선택하는 것입니다.

  1. 기본적인 전문화를 제공할 수 없기 때문에 단순하고 우아하며 일관된 API를 만들 수 있지만 높은 퍼포먼스와 GC 오버헤드가 필요합니다.

  2. API를 엉망으로 만들고 JDK 개발자에게 유지보수를 부담시키는 대가를 치르고 완전한 원시 전문화 세트를 제공할 수 있습니다.

  3. 원시적인 전문화의 서브셋을 제공하여 적당한 크기의 고성능 API를 제공하여 상당히 좁은 사용 사례(char processing)에서 발신자에게 비교적 적은 부담을 줄 수 있습니다.

마지막 거 골랐어요.

언급URL : https://stackoverflow.com/questions/22435833/why-is-string-chars-a-stream-of-ints-in-java-8

반응형