Java에서 낮은 수준의 문자열 비교에 대한 이야기

10565 단어 JVMJava
이 기사정적 정보 LT 대회 Advent Calendar 2019 셋째 날 보도.

입문


이것은 낮은 수준의 이야기이다
Java는 당분간 보통과 같이 쉽게 읽을 수 있다.
문자열을 비교한 적이 있다면 먼저 읽을 수 있습니다.

복습(?)


Java에서 문자열 비교를 할 때는 "=="로 비교할 수 없습니다.
String#equals 방법을 사용하세요.
public class Test {
    public static void main(String[] args) {
        String a = "HelloWorld";
        String b = "Hello";
        b += "World";

        System.out.println(a == b);        // false
        System.out.println(a.equals(b));   // true
    }
}

잠시만요. "==" 도 진짜가 돼요.

public class Test {
    public static void main(String[] args) {
        String a = "HelloWorld";
        String b = "HelloWorld";

        System.out.println(a == b);       // true
        System.out.println(a.equals(b));  // true
    }
}
왜?

"==" 구조


JVM


"=="흔히 주소 위치의 비교라고 불리우니 자세히 살펴보자.
Java에서 "=="는 일반적으로 컴파일할 때 JVM의 명령어로 변환됩니다. 예를 들어 "if_acmpne"또는 "if_acmpeq"입니다.
JVM은 Java 파일을 컴파일할 때 class 파일이 많이 생성되죠?이 class 파일을 해석하고 실행하는 것은 JVM(Java Virtual Machine)입니다.
class 파일은 많은 사람들이 읽을 수 없는 형식이지만 JVM(컴퓨터)에게는 읽기 쉬워졌습니다.
JVM은 여러 개의 간단한 명령을 조합하여 복잡한 프로그램을 실현하기 때문에 JVM에서는 하나의 명령에 불과하지만 대부분의 경우 JVM은 이를 여러 명령으로 변환합니다.

JVM 명령 및 작업 스택


그렇다면'if_acmpne'과'if_acmpeq'는 어떤 명령입니까? 작업 창고에서 두 개를 꺼내서 일치하는지 조사하고 지정한 프로그램의 줄로 이동하는 명령입니다.
창고를 세어...?
JVM은 스택머신이라고 불리며 레지스터를 사용하지 않고 스택이라는 기기를 사용하여 다양한 연산을 수행합니다.
※ 창고...위에 놓인 값어치 중 순서대로 꺼내는 장치(포커의 산패 같은 물건)
작업 스택은 작업 영역과 유사합니다.
사칙 연산 등이 적당하기 때문에 자주 사용된다.
(반폴란드 기법 등)
예를 들어 다음과 같은 메커니즘을 통해 5+12를 계산할 수 있다.

그러나 Java의 작업 스택에는 최대 32bit의 요소가 하나만 있습니다.
※ 32bit=2^32의 수량을 처리할 수 있다는 뜻입니다.
자바로 문자를 표시할 때 작업 스택의 요소 1 개 (= 32bit) 가 소모됩니다.
문자열은 가변 크기입니다.때로는 한 글자를 처리하고, 때로는 이 문장처럼 수백 글자를 처리한다.즉, 조작수 창고 1개 요소 (=32bit) 는 어쨌든 모든 문자를 처리할 수 없다는 것이다.
따라서 문자열을 다른 메모리에 저장하고 이 메모리의 주소 (컴퓨터의 주소) 를 작업수 창고에 저장합니다.이렇게 하면 원소 하나 (=32bit) 를 작업하면 충분하다.
※ 다른 메모리와 이 메모리를 사용하는 작업수 창고의 이미지(0x는 특별한 의미가 없음)

if_acmpeq / if_acmpne


위에서 설명한 바와 같이'if_acmpeq'와'if_acmpeq'는 작업수 창고에서 두 개를 꺼냅니다. 만약 이 두 개가'if_acmpeq'라면,'if_acmpene'에서 같지 않을 때 다른 지정한 줄로 이동합니다.(점프는 흔히 볼 수 있는 GOTO 명령 같은 것들)

즉, "if_acmpeq"를 사용하면 작업수 창고의 주소 수치가 같으면 점프 명령이 발생합니다.
이 페이지의 두 번째 소스 코드 ("잠시만요") 에서 같은 주소에 "Hello World"가 저장되어 있기 때문에true로 표시됩니다.

정말로...


같은 곳에 저장되는 건가요?


맞습니다.
그럼 이거 어때요?
적당히 명명된 것을 용서해 주십시오
public class Test {
    public static void main(String[] args) {
        String a = "HelloWorld";
        method1(a);
    }

    public static void method1(String c) {
        String k = "HelloWorld";
        System.out.println(k == c);
    }
}
정답은'진짜'입니다.

클래스 상수 영역


Java 클래스 파일에는 상수 영역이 있습니다.
이 상수 영역은 컴파일할 때 문자열 등을 주로 저장합니다.마술 번호 또는 마술 문자열 (?)이러한 파일은 상수 영역에 저장되며 JVM이 클래스 파일을 읽을 때 메모리로 읽히고 사용됩니다.
예를 들면
System.out.println("Hello Ja! Ja!");
"Hello Ja! Ja!"같아요.등이 상수 영역에 저장되어 문자열을 읽습니다.
또한 자바의 컴파일러는 똑똑하기 때문에 같은 문자열이 두 번 나타나도 이전에 한 번 이상 사용하면 같은 상량 구역에서 읽을 수 있다.
즉, 아까 코드에서 방법이 다르더라도 같은 상수 영역의'Hello World'문자열을 참조하기 때문에 주소가 같다는 것이다.
아까 클래스 파일의 상수 영역은 바이너리로 보면'Hello World'가 잘 저장되어 있어요.

이 페이지의 원본 코드는 일부러 저장 영역을'Hello'와'World'두 부분으로 나누어 문자열을 쓴다.
Java의 컴파일러는 그 정도까지는 계산하지 못할 것 같습니다.C의 컴파일러 등도 이곳에 자주 온다.

이퀄스는요?


String 클래스에서 덮어쓰는 것은 내용을 잘 비교하는 코드이다
인상 코드는 다음과 같다.원문은 엄마가 아니다.
@Override
public boolean equals(String str) {
    if(this.length() != str.length()) {
        return false;
    }
    for(int i = 0; i < str.length(); i++) {
        if(this.charAt(i) != str.charAt(i)) {    //charAtで任意のn文字目をとりだせる
            return false;
        }
    }
    return true;
}
선형으로 앞부터 순서대로 문자를 비교한다.
한 글자는 32bit에 수용할 수 있기 때문에 문제없다.

총결산


문자열을 비교할 때 equals 방법을 사용하세요.
"=="는 진짜일 때 같은 곳에 저장된다.음~음.
내일 행사 달력 보도도 잘 부탁드립니다.
정적 정보 LT 대회 Advent Calendar 2019

좋은 웹페이지 즐겨찾기