티스토리 뷰
length() 메서드부터 이제 파악하기 시작한다.
보다가 느낀 게 있는데 JDK 8 버전의 코드와 JDK11 의 코드가 다르다는 점이다.
가령 regionMatch의 경우에는 8의 경우 그저 문자열 지역을 비교하지만 11에서는 latin1 인지와 UTF8의 여부를 체크하여 비교하는 로직이 존재한다.
메서드 중 ignoreCase 가 들어가는 경우 toLowerCase(char.toUppserCase()) 의 경우를 비교하는데 이는 uppderCase의 경우 아래와 같은 경고가 있어서 그 사유를 알 수 있다.
// Unfortunately, conversion to uppercase does not work properly
// for the Georgian alphabet, which has strange rules about case
// conversion. So we need to make one last check before
// exiting.
이게 JDK 11에선 StringUTF16 와 같은 클래스 내부로 이동되었다.. 음 JDK 8과 12로 살펴보도록 하자. 이미 JDK16 까지 나왔지만 사용하는 내용이 8이기 때문에 우선 파악한다.
눈에 보이는 차이가 있다면 11부터 각 메서드에 isLatin1() 와 같은 체크 메서드가 슬슬 등장하며 StringUTF16, StringLatin1 클래스가 분화되며 이를 사용해 로직 분기가 이뤄지는 것이 확인된다. Java 8 에서는 크게 확인되지 않는다.
length()
isEmpty()
char charAt(int index) // 이게 JDK8 에선 단순하게 value[index] 를 리턴하는데, 확인한 16에서는
if (isLatin1()) { |
return StringLatin1.charAt(value, index); |
} else { |
return StringUTF16.charAt(value, index); |
} |
로 바뀌고 StringLatin1은 기존 로직이 그대로, StringUTF16 charAt 메서드에는 아래의 연산이 추가된다.
쉬프트 연산을 이용하면서 비트 연산을 취한다. latin1 쪽은 단일 바이트, UTF16은 2바이트이므로 연산이 UTF16에선 보통 1 단위 인덱스가 쉬프트된 뒤에(1쉬프트는 값의 2배임) 수행된다. 따라서 수행된 연산의 첫 인덱스 위치를 반환하기 위하여 << 1 쉬프트로 값을 1/2로 변경하는 것으로 보인다.
int codePointAt(int index)
int codePointCount(int beginIndex, int endIndex)
int offsetByCodePoints(int index, int codePointOffset)
getChars(char dst[], int dstBegin)
void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin)
getBytes(int srcBegin, int srcEnd, byte dst[], int dstBegin) //Deprecated
byte[] getBytes(String charsetName)
byte[] getBytes(Charset charset)
byte[] getBytes()
boolean equals(Object anObject) // JDK16 에서는 return StringLatin1.equals(value, aString.value);로 최종 변환 로직이 이전되었다.
그러나 JDK11에서는 isLatin1() 을 통해 StringUTF8의 equals를 대신 호출한다. 왜 더 높은 버전에서 사라졌을까.
boolean contentEquals(StringBuffer sb)
boolean equalsIgnoreCase(String anotherString)
int compareTo(String anotherString) // JDK 11, JDK16 에서 Latin1 UTF16 구분을 따로 하도록 구현된다.
int compareToIgnoreCase(String str) // JDK 11, JDK16 에서 Latin1 UTF16 구분을 따로 하도록 구현된다. 이는 내부 클래스 CaseInsensitiveComparator 의 구현이 대체됨으로써 구현된다.
boolean regionMatches(int toffset, String other, int ooffset, int len) // JDK 11, JDK16 에서 Latin1 UTF16 구분을 따로 하도록 구현된다.
boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) // JDK 11, JDK16 에서 Latin1 UTF16 구분을 따로 하도록 구현된다.
boolean startsWith(String prefix, int toffset) // JDK 11, JDK16 에서 Latin1 UTF16 구분을 따로 하도록 구현된다.
boolean startsWith(String prefix) // JDK 11, JDK16 에서 Latin1 UTF16 구분을 따로 하도록 구현된다.
boolean endsWith(String suffix) // JDK 11, JDK16 에서 Latin1 UTF16 구분을 따로 하도록 구현된다.
int hashCode() // JDK8 에서 h = 31 * h + val[i]; 로 해시를 계산하지만 11 에서는 Latin1, UTF16을 구분한다. JDK16에서는 추가로 hashIsZero를 hash가 0 인경우 true로 셋하는 추가 로직이 있다.
int indexOf(int ch)
int indexOf(int ch, int fromIndex) // JDK 11, JDK16 에서 Latin1 UTF16 구분을 따로 하도록 구현된다. JDK8에선 String 코드에 private 메서드로 로직이 같은 클래스 내에 존재한다.
int lastIndexOf(int ch)
int lastIndexOf(int ch, int fromIndex) // JDK 11, JDK16 에서 Latin1 UTF16 구분을 따로 하도록 구현된다. JDK8에선 String 코드에 private 메서드로 로직이 같은 클래스 내에 존재한다.
int indexOf(String str)
int indexOf(String str, int fromIndex)
int indexOf(char[] source, int sourceOffset, int sourceCount, String target, int fromIndex)
-> static int indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex) // JDK 11, JDK16 에서 Latin1 UTF16 구분을 따로 하도록 구현된다. JDK8에선 구분이 없다. 또한 UTF8 코드에서도 indexOfLatin1Unsafe 와 같은 메서드명을 통해 별다른 supplementary 체크는 안 하는 것으로 보인다.
// JDK 11, JDK16 에서 Latin1 UTF16 구분을 따로 하도록 구현된다. JDK8에선 String 코드에 private 메서드로 로직이 같은 클래스 내에 존재한다.
int lastIndexOf(String str)
int lastIndexOf(String str, int fromIndex)
lastIndexOf(char[] source, int sourceOffset, int sourceCount, String target, int fromIndex)
-> static int lastIndexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex) // JDK 11, JDK16 에서 Latin1 UTF16 구분을 따로 하도록 구현된다. JDK8에선 구분이 없다. 또한 UTF8 코드에서도 indexOfLatin1Unsafe 와 같은 메서드명을 통해 별다른 supplementary 체크는 안 하는 것으로 보인다. 메시지를 거꾸로 비교하네.. label 을 사용해서 continue를 사용한 흔적도 보인다. 오랜만이네..
String substring(int beginIndex) // 아 이것도 구분을 따로 하는데.. JVM에서 값을 주입해 주는 COMPACK_STRINGS의 값에 따라 UTF16 코드에서도 Latin1 문자열을 리턴하도록 로직이 들어있기도 한 상황이다. 이러면 들어온 문자열의 값이.. 압축 가능한지 여부를 두고 판단해서 Latin1 로도 충분하면 이로 변환하는 것으로 보인다(용량 절약).
String substring(int beginIndex, int endIndex) // 얘도 마찬가지.
subSequence(int beginIndex, int endIndex) // 리턴값은 다르나 위 substring을 내부에서 그대로 사용한다.
String concat(String str) //JDK8, JDK11, JDK16 의 로직이 다르다. 8에서는 그저 value 라는 char[] 에 값을 복사한다. JDK11 에서는 두 문자열의 coder(latin1 or UTF16)이 같으면 복사를 수행한다. 이 때 Arrays.copyOf 대신 System.arraycopy를 대신 사용한다. 다르면 UTF16의 문자열을 생성한다. JDK16에서는 StringConcatHelper.simpleConcat 메서드를 사용하는데 이 내부에서 UTF16, Latin1 관련 로직이 분리되어 있다.
아 머리아파.
'Java' 카테고리의 다른 글
String Class 를 열어보다2(일부 메서드). (0) | 2021.04.02 |
---|---|
String Class 를 열어보다(시작)-3. 생성자..? (0) | 2021.03.21 |
String Class 를 열어보다(시작). (0) | 2021.03.18 |
com.amazonaws.services.s3.AmazonS3ClientBuilder의 standard() 메서드의 호출 위치. (0) | 2020.11.09 |
Java ConditionalOnClass 어노테이션 (0) | 2020.11.05 |