도도한 개발자

[Java] #15-2. String - nullCheck, 비교/검색 본문

Backend/Java

[Java] #15-2. String - nullCheck, 비교/검색

Kiara Kim 2022. 3. 18. 18:00

* 객체의 null 체크

 

모든 객체를 처리할 때엔 널 체크를 반드시 해야 한다. 객체가 널이라는 것은 객체가 아무런 초기화가 되어 있지 않으며, 클래스에 선언되어 있는 어떤 메소드도 사용할 수 없다는 것을 의미한다. 그리고 널인 객체의 메소드를 호출하는 순간 예외를 발생시킨다. 널체크 하는 소스를 보자.

 

public class StringNull {

	public static void main(String[] args) {
		StringNull sample = new StringNull();
		sample.nullCheck(null);
	}
	public boolean nullCheck(String text) {
		int textLength = text.length();
		System.out.println(textLength);
		if(text == null) return true;
		else return false;
	}
}

 

컴파일은 정상적으로 작동되지만 객체가 널인지 아닌지는 실행시에만 확인할 수 있다. 결과는 다음과 같다.

 

Exception in thread "main" java.lang.NullPointerException
	at chapter15.StringNull.nullCheck(StringNull.java:11)
	at chapter15.StringNull.main(StringNull.java:7)

 

이는, null인 객체의 메소드에 접근하면 NullPointerException이 발생한다. 그럼 다른 메소드를 보자.

 

public boolean nullCheck2(String text) {
	if(text == null) {
		return true;
	} else {
		int textLength = text.length();
		System.out.println(textLength);
		return false;
	}
}

 

이 메소드를 main()에서 호출하고 컴파일 및 실행하면 true가 리턴된다. null 체크는 아무리 강조해도 지나치지 않는다. 메소드의 매개 변수로 넘어오는 객체가 널이 될 확률이 조금이라도 있다면 반드시 한 번씩 확인하는 습관을 갖고 있어야 한다.

 

 

* String 비교 · 검색 메소드

 

String 클래스는 문자열을 나타낸다. 문자열 내 특정 위치를 찾거나 값을 비교하는 작업을 빈번하다. String 클래스 객체의 내용을 비교하고 검색하는 메소드를 알아보자.

 

** 문자열의 길이를 확인하는 메소드

 

리턴 타입 메소드 이름 및 매개 변수 설명
int length() 문자열의 길이를 리턴한다.

 

배열도 객체이긴 하지만 메소드는 없는 특수한 객체이다. 배열의 크기를 확인할 땐 괄호가 없는 length를 사용하지만, 그 이외의 모든 클래스는 메소드를 호출해야 하며 String 객체 길이를 확인하기 위해선 length() 라는 메소드를 사용해야 한다.

 

** 문자열이 비어 있는지 확인하는 메소드

 

문자열의 길이가 0인지를 확인하는 것보다 이 메소드를 사용하는 것이 간단한다.

 

리턴 타입 메소드 이름 및 매개 변수 설명
boolean inEmpty() 문자열이 비어 있는지 확인. 비어있으면 true를 리턴한다.

 

text가 공백 하나로 되어 있는 문자열이라도 메소드는 false를 리턴한다.

 

** 문자열이 같은지 비교하는 메소드

 

String 클래스에서 제공하는 문자열이 같은지 비교하는 메소드들은 매우 많다.

 

리턴 타입 메소드 이름 및 매개 변수
boolean equals(Object anObject)
boolean equalsIgnoreCase(String anotherStr)
int compareTo(String anotherStr)
int compareToIgnoreCase(String str)
boolean contentEquals(CharSequence cs)
boolean contentEquals(StringBuffer sb)

 

먼저 equals() 메소드에 대해 알아보자.

 

public class compareStr {

	public static void main(String[] args) {
		compareStr sample = new compareStr();
		sample.checkCompare();
	}
	
	public void checkCompare() {
		String text = "Check value";
		String text2 = "Check value";
		
		if(text == text2) {
			System.out.println("text == text2 result is same");
		} else {
			System.out.println("text == text2 result is different");
		}
		
		if(text.equals("Check value")) {
			System.out.println("text.equals(text2) result is same");
		}
	}
}

 

실행 결과는 다음과 같다.

 

text == text2 result is same
text.equals(text2) result is same

 

String 클래스도 기본적으로 == 비교가 아닌 equals() 메소드를 사용해서 비교 해야만 한다. 그런데 결과가 이렇게 나오는 이유는 자바에 Constant Pool 이라는 것이 존재하기 때문이다. 자바에서는 객체들을 재사용하기 위해 Constant Pool 이라는 것이 만들어져 있고, String의 경우 동일한 값을 갖는 객체가 있으면 이미 만든 객체를 재사용한다. 따라서 text와text2는 같은 객체다. 

 

이번엔 equalsIgnoreCase() 메소드의 사용법을 확인해보자.

 

String text3 = "check value";
if(text.equalsIgnoreCase(text3)) {
	System.out.println("text.equalsIgnoreCase(text3) result is same");
}

 

결과는

 

text.equalsIgnoreCase(text3) result is same

 

text와 text3는 첫 글자가 대문자로 시작하는지 소문자로 시작하는지의 차이만 존재한다. 이렇게 대소문자만 다른 문자열을 비교하는 equalsIgnoreCase() 메소드를 이용해 비교한 결과 대소문자를 구분하지 않고 두 개의 값이 같은지 다른지 확인한 것을 볼 수 있다.

 

** 특정 조건에 맞는 문자열이 있는지를 확인하는 메소드

 

리턴 타입 메소드 이름 및 매개 변수
boolean startsWith(String prefix)
boolean startsWith(String prefix, int offset)
boolean endsWith(String suffix)
boolean contains(CharSequence s)
boolean matches(String regex)
boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len)
boolean regionMatches(int toffset, String other, int ooffset, int len)

 

이 중 가장 많이 사용하는 것이 startsWith() 메소드다. 이름 그대로 매개 변수로 넘겨준 값으로 시작하는지를 확인한다.

 

public class StrCheck {

	public static void main(String[] args) {
		StrCheck sample = new StrCheck();
		
		String addresses[] = new String[] {
			"서울시 송파구 방이동",
			"경기도 용인시 수지구 동천동",
			"서울시 송파구 잠실 롯데타워"
		};
		sample.checkAddr(addresses);
	}
	
	public void checkAddr(String[] addr) {
		int startCnt = 0, endCnt = 0;
		String startTxt = "서울시";
		String endTxt = "동";
		for(String address : addr) {
			if(address.startsWith(startTxt)) {
				startCnt++;
			}
			if(address.endsWith(endTxt)) {
				endCnt++;
			}
		}
		System.out.println("Starts with " + startTxt + " count is " + startCnt);
		System.out.println("Ends with " + endTxt + " count is " + endCnt);
	}
}

 

결과는 다음과 같다.

 

Starts with 서울시 count is 2
Ends with 동 count is 2

 

이렇게 startsWith() 메소드와 endsWith() 메소드를 사용하면 원하는 값이 해당 문자열 앞 뒤에 있는지 쉽게 확인할 수 있다. 그러면 중간에 있는 값은 어떻게 확인할까?

 

이때 사용하는 것이 contains() 메소드다. 이 메소드는 매개 변수로 넘어온 값이 문자열에 존재하는지를 확인한다. 소스를 보자.

 

public void constainsAddr(String[] addr) {
	int containCnt = 0;
	String containTxt = "방이";
	for(String address : addr) {
		if(address.contains(containTxt)) {
			containCnt++;
		}
	}
	System.out.println("Contains " + containTxt + " count is " + containCnt);
}

 

결과는 예상대로 나왔다.

 

Contains 방이 count is 1

 

그런데 살면서 이렇게 간단한 데이터만 있는 것은 아니다. regionMatches()라는 메소드는 문자열 중에서 특정 영역이 매개 변수로 넘어온 문자열과 동일한지를 확인하는데 사용된다. 위의 테이블을 보면 하나는 대소문자 구분 여부를 지정할 수 있고 하나는 그런 지정 자체가 안된다. regionMatches() 메소드의 매개 변수로 넘어오는 값이 어떤 값을 뜻하는지 알아보자.

 

매개 변수 의미
ignoreCase true일 경우 대소문자 구분을 하지 않고, 값을 비교한다.
toffset 비교 대상 문자열의 확인 시작 위치를 지정한다.
other 존재하는지를 확인할 문자열을 의미한다.
ooffset other 객체의 확인 시작 위치를 지정한다.
len 비교한 char의 개수를 지정한다.

 

public void checkMatch() {
	String text = "This is a text";
	String compare1 = "is";
	String compare2 = "this";
	System.out.println(text.regionMatches(2, compare1, 0, 1));
	System.out.println(text.regionMatches(5, compare1, 0, 2));
	System.out.println(text.regionMatches(true, 0, compare2, 0, 4));
}

 

코드를 보면 text 값이 있고, 비교할 compare1과 compare2가 있다. 이 메소드의 수행 결과가 제대로 나오는지 확인하려면 각 char의 위치의 index가 어디있는지 알아야한다.

T h i s   i s   a   t e x t
0 1 2 3 4 5 6 7 8 9 10 11 12 13

 

text 문장에 "is"라는 문자열이 시작되는 위치를 확인해보자. 처음 "is"가 나오는 것은 2번째 위치와 5번째 위치다. 첫 번째 regionMatchew() 메소드를 보면 첫 번째 매개 변수인 2라는 위치 값은 맞았다. 비교하려고 하는 compare1의 값이 "is"인데, 세 번째 매개 변수가 0이고 네 번째 매개 변수가 1이기 때문에 비교하려는 것은"i"인지 아닌지만 확인하면 되게 때문에 첫 번째 출력무의 결과는 true이다.

 

 

그럼 이만.