코테 문제 풀 때 알고리즘이나 자료구조도 공부해야 하지만, 코드를 작성하는 방법도 중요하다.
클린 코드를 작성하기 위해서는 아래 세 가지 습관을 들여야 한다.
하루 아침에 코드 쓰는 습관이 바뀌지는 않겠지만 매번 코드를 작성할 때마다 이러한 습관을 염두에 두고 작성하다 보면 점차 클린 코드 작성 능력이 향상될 것이라고 기대한다.
1. 조기반환 (early return)
조기 리턴(early return)은 조건이 충족되면 함수나 메서드에서 바로 결과를 반환하는 프로그래밍 기법이다. 이렇게 하면 불필요한 계산을 피하고 코드의 가독성을 높일 수 있다.
예를 들어, totalPrice 함수에서 가격이 100을 초과하는 경우 바로 할인을 적용하고 결과를 반환하는 코드를 짤 때, 조기 리턴을 하지 않으면 할인 로직을 처리한 후에만 결과를 반환하기 때문에 추가적인 코드가 필요하다.
아래는 totalPrice 메서드에서 조기 리턴을 활용하는 예시 코드이다.
public static void main(String[] args) {
System.out.println(totalPrice(4, 50));
}
static int totalPrice(int quantity, int price) {
int total = quantity * price;
if (total > 100) {
return (int)(total * 0.9); // 가격이 100을 초과하면 할인 적용 후 바로 반환
}
return total; // 그 외의 경우, 총액을 그대로 반환
}
- total에 quantity * price 를 대입한다.
- total의 값이 100보다 큰 경우, total에 0.9를 곱하고 반환한다. 이렇게 하면 메서드를 조기에 종료할 수 있으므로 이후 예외에 대한 처리를 하지 않아도 된다.
2. 보호 구문 (guard clauses)
보호 구문은 본격적인 로직을 실행하기 전에 입력값의 유효성을 검사하는 예외 처리 기법이다. 원하는 코드를 쓰기 전에 조건문을 사용해서 먼저 초기 입력값이 유효한지 확인하고, 유효하지 않으면 즉시 메서드를 종료하거나 기본값을 반환하도록 미리 예외를 처리할 수 있다.
보호구문을 활용하면 예측 가능한 오류를 사전에 처리하고, 잘못된 입력에 대해 안전한 기본값을 반환함으로써 프로그램의 안정성을 높일 수 있다.
import java.util.List;
static double calculateAverage(List<Integer> numbers) {
if (numbers == null || numbers.isEmpty()) { // null 이거나 빈 리스트이면 0 반환
return 0;
}
int total = numbers.stream().mapToInt(i -> i).sum(); // stream 사용하여 합산
return (double) total / numbers.size(); // 평균 계산 후 반환
}
코드를 보면 null 이거나 데이터가 없을 때 바로 예외처리를 해서 메서드가 종료된다.
그리고 실제로 작성하는 로직은 예외처리 뒤에 온다.
3. 제네릭(generic)
제네릭은 빌드 레벨에서 타입을 체크해서 타입 안정성을 제공하고, 타입 체크과 형변환을 생략 할 수 있게 해 주기 때문에 코드가 간결해 진다.
// 1. 일반 List 사용 (타입 안전하지 않음)
List<Object> list = new ArrayList<>(); // List의 타입을 Object로 설정
list.add(10);
list.add("abc");
// 런타임 오류 발생 (강제로 타입 캐스팅)
// String을 int로 캐스팅하면 ClassCastException 발생
int sum1 = (int) list.get(0) + (int) list.get(1);
// 2. 제네릭을 사용한 List (타입 안전함)
// 제네릭을 사용하여 Integer만 저장 가능
List<Integer> genericList = new ArrayList<>();
genericList.add(10);
// genericList.add("abc"); // 컴파일 오류(문법 - 빌드 오류), Integer만 허용됨
// sum2 계산에서 타입이 맞지 않음
int sum2 = genericList.get(0) + genericList.get(0); // 오류 없이 정상적으로 계산
코드를 보면 1에서는 런타임 오류가 발생하고 2에서는 빌드 오류가 발생한다. List를 정의할 때 <Integer> 과 같이 타입을 강제하는 것을 제네릭이라고 한다. 테네릭은 타입에 맞지 않은 데이터를 추가하려고 할 때 문법 오류를 발생시키기 때문에 개발자의 실수를 미리 방지 해 준다.
그래서 1처럼 작성하면 코드를 실제로 실행 해 봐야지만 오류가 발생한다는 것을 알 수 있지만, 2는 빌드 자체가 안 되기 때문에 런타임 버그를 방지할 수 있다. 또 데이터에 접근해서 사용하려고 할 때 형변환을 할 필요가 없기 때문에 코드가 간결해진다. 코테에서는 여러 타입의 데이터를 하나의 컬렉션에 넣어야 하는 경우는 거의 없기 때문에 제네릭으로 타입을 강제해서 실수를 방지하는 것이 좋다.
4. 해시맵
자바의 해시맵(hashmap)은 키(key)와 값(value) 쌍을 저장하는 해시테이블로 구현되어 있다. 키를 사용해서 값을 검색하는 자료구조라고 생각하면 편하다.
해시맵 초기화
키는 문자열, 값은 32비트 정수형을 저장하는 해시맵 선언
HashMap<String, Integer> map = new HashMap<>();
해시맵 데이터 삽입 & 전체 출력
// 해시맵 값 삽입
map.put("apple", 1);
map.put("banana", 2);
map.put("orange", 3);
// 해시맵 값 출력
System.out.println(map); // {banana=2, orange=3, apple=1}

해시맵 데이터 검색
해시맵에 "apple" 문자열과 일치하는 키가 있는지 확인하고, 일치하는 키를 찾으면 키-값을 출력한다.
String key = "apple";
if (map.containsKey(key)) { // containsKey로 수정
int value = map.get(key); // 오타 수정: 'key'로 변경
System.out.println(key + ": " + value); // apple: 1
} else {
System.out.println(key + "는 해시맵에 존재하지 않습니다.");
}

해시맵 데이터 수정
해시맵에서 키 "banana"를 검색해서 해당 키의 값을 4로 바꾸는 코드
해시맵 내부는 해시 테이블로 구성되어 있기 대문에 해시 테이블에서 키를 찾거나 하지 않아도 된다.
map.put("banana", 4 );
System.out.println(map); // {banana=4, orange=3, apple=1}

해시맵 삭제
키 "orange"를 찾아 해시맵에서 삭제하기
map.remove("orange");
System.out.println(map); // {banana=4, apple=1}

참고로 자바에는 해시맵과 유사하지만 '키-값' 쌍이 아니라 '키'만 저장하는 해시 셋도 있다.
언제 HashSet을 사용하고, 언제 HashMap을 사용할까?
HashSet
중복을 제거하거나, 존재 여부만 체크하고 싶을 때 사용하고 순서가 중요하지 않다.
예: 특정 숫자가 배열에 포함되어 있는지 확인할 때
HashMap
키와 값의 관계를 저장하고 싶을 때 사용한다.
예: 학생 이름과 성적, 상품 ID와 가격을 저장할 때
5. 문자열 (String)
중요한 점은 문자열을 추가하고 삭제하는 동작을 할 때, 문자열은 immutable 객체이므로 기존 객체를 수정하는 것이 아니라 새로운 객체를 반환한다는 사실이다.
String string = "he"
string += "llo"
System.out.println(string) // "hello"

문자열 수정
그렇다면 문자열을 수정하고 싶을 때는 어떻게 해야 하는가? 그럴 때는 replace() 메서드를 사용한다. replace() 메서드는 첫 번째 인자로 찾을 문자열을, 두 번째 인자로 변경할 문자열을 넣어서 사용한다.
예를 들어서 replace(A, B)는 메서드의 검색 대상 문자 A를 모두 찾아서 일괄 B로 변환한다.
String string = "Hello";
string = string.replace("l", ""); // "l" 모두 삭제
System.out.println(string); // "Heo"
6. StringBuffer와 StringBuilder
위에서 언급한 것처럼 자바에서 String 객체는 값을 변경할 수 없는 Immutable 객체이다. '값을 변경할 수 없다'는 것이 어떤 의미인지 성능 측면에서 알아보기 위해 아래 예시 코드를 보자.
String s = "abc"
System.out.println(System.identityHashCode(s)); // 1808253012
s += "def";
System.out.println(System.identityHashCode(s)); // 589431969
System.out.println(s) // abcdef
`System.identityHashCode()` 메서드는 객체를 특정하는 식별값을 반환한다. String s의 abc에 def를 이어 붙였을 뿐인데 identityHashCode() 메서드의 출력값이 달라진다. 이건 "abc"값만 가지고 있던 s와 "abcdef" 값을 가지고 있는 s가 서로 다른 객체임을 의미한다. 즉, String 객체의 값을 변경하는 새로운 String 객체를 만들고 값을 복사하는 작업이 수행됨을 의미한다. 따라서 s+="def"를 수행 할 때 내부에서는 아래와 같은 연산이 수행된다.
- 새로운 String s 객체를 생성
- s가 가진 "abc" 값을 하나씩 복사
- "abc" 뒤에 "def" 저장
그 결과 s += "def" 코드 한 줄 에서 총 6번의 내부 연산("abc"값 3개 복사, "def"값 3개 저장)이 수행된다. 시간 복잡도로 따지면 문자열의 길이를 N이라고 했을 때 O(N)이다.
그래서 아래처럼 N = 100,000인 코드를 수행하면 컴퓨터에서 6초가 걸린다.
public class Main {
public static void main(String[] args) {
long start = System.currentTimeMillis();
String s = "";
for (int i = 1; i <= 100000; i++) {
s += i; // String을 계속 수정 (비효율적이지만 의도적으로 유지)
}
long end = System.currentTimeMillis();
System.out.println((end - start) / 1000.0 + "초"); // 괄호 수정 완료
}
}
이런 문제를 해결하기 위해 등장한 것이 StringBuilder 클래스와 StringBuffer 클래스이다. 두 클래스는 mutable 하므로 값을 변경할 때 시간복잡도 관점에서 훨씬 더 효율적이다. 다음 코드는 위 코드와 비교했을 때 s 객체에 저장한 문자열은 동일하다. 하지만 실행 시간은 전혀 다르다. 고작 0.005초가 소요된다.
public class Main {
public static void main(String[] args) {
long start = System.currentTimeMillis();
StringBuilder sb = new StringBuilder(); // String 대신 StringBuilder 사용
for (int i = 1; i <= 100000; i++) {
sb.append(i); // 문자열 추가
}
long end = System.currentTimeMillis();
System.out.println((end - start) / 1000.0 + "초"); // 괄호 수정 완료
}
}
따라서 String 값을 변경하는 연산이 많을 때에는 효율이 높은 StringBuilder 클래스나 StringBuffer 클래스를 사용해야 한다. 두 클래스의 차이는 멀티스레드 환경에서 Thread-Safe 여부로 나누어진다. 하지만 일반적으로 대부분의 코딩 테스트에서는 다수의 스레드를 형성할 필요가 없기 때문에, Thread-Safe가 없는 StringBuilder 클래스가 속도 측면에서 약간 미세하지만 더 빠르므로 권장된다.
int 와 Integer의 차이 ▼
int 타입
- Primitive type
- 메모리에서 직접 값을 저장하며, 성능이 빠르다.
- null 값을 가질 수 없다.
- 기본 값은 0이다.
Integer 타입
- Reference type (참조 타입)
- Integer는 int를 감싸는 클래스이다.
- 객체로 처리되며, int와 달리 메서드를 호출할 수 있다.
- null 값을 가질 수 있다.
- Integer는 int와 상호 변환할 수 있는 방법을 제공한다. (auto-boxing과 unboxing)
int primitiveInt = 10; // Primitive type
Integer wrapperInt = 10; // Reference type (auto-boxing)
Integer nullInteger = null; // Reference type can be null
// Unboxing (Integer -> int)
int unboxedInt = wrapperInt; // 자동으로 unboxing 발생
성능적인 면에서 int는 primitive type이므로 연산이 빠르고, 메모리 사용도 적다. 하지만 유연성은 Integer는 객체로서 null을 허용하고, 다양한 메서드 및 클래스를 활용할 수 있기 때문에 Integer이 더 유연하다. 예를 들어, 컬렉션(List, Set 등)에서 Integer를 사용할 수 있기 때문이다.
리마인드
- 자바의 데이터 타입에는 primitive types와 reference types가 있다. primitive types의 연산 속도가 reference types보다 더 빠르기 때문에 가능하다면 primitive types을 사용하는 것이 좋다.
- 자바의 컬렉션 프레임워크의 리스트, 해시맵은 코테에서 자주 사용하기 때문에 꼭 알아두어야 한다.
- 자바에서 String 클래스의 객체는 Immutable 객체이다. 한번 선언하면 값을 변경할 수 없기 때문에 + 연산 등을오 값을 변경할 경우 효율이 좋지 않다. String의 + 연산이 자주 발생하는 경우 StringBuilder을 사용하는 것이 좋다.
- 조기반환, 보호구문, 제네릭 기법을 사용하면 코드의 가독성과 효율성을 높일 수 있다.
참고 자료 & 포스팅 작성에 도움을 주신 분
1. chatGPT 에 궁금한 점은 물어 보며 작성
2. 김희성, 프로그래머스 코딩테스트, 프로그래머스, 2023
'코딩테스트 > 알고리즘' 카테고리의 다른 글
[자료구조] 그림으로 쉽게 이해하는 자바의 Queue (큐) (8) | 2025.02.21 |
---|---|
[코딩테스트/알고리즘 맛집] 자바 스택(Stack) 문제 유형과 코드 정리 (12) | 2025.02.19 |
[알고리즘] 그림과 문제로 알아보는 이진트리 탐색 (이분 탐색, Binary Search) (12) | 2025.02.15 |
[코딩테스트/알고리즘 맛집] 자바 정렬(Sort) 코딩테스트에서 자주 나오는 문제 유형과 풀이 팁 (30) | 2025.02.08 |
[코딩테스트/알고리즘 맛집] 그리디 알고리즘 (Greedy Algorithm) 으로 빠르고 효율적으로! (18) | 2025.02.05 |
코테 문제 풀 때 알고리즘이나 자료구조도 공부해야 하지만, 코드를 작성하는 방법도 중요하다.
클린 코드를 작성하기 위해서는 아래 세 가지 습관을 들여야 한다.
하루 아침에 코드 쓰는 습관이 바뀌지는 않겠지만 매번 코드를 작성할 때마다 이러한 습관을 염두에 두고 작성하다 보면 점차 클린 코드 작성 능력이 향상될 것이라고 기대한다.
1. 조기반환 (early return)
조기 리턴(early return)은 조건이 충족되면 함수나 메서드에서 바로 결과를 반환하는 프로그래밍 기법이다. 이렇게 하면 불필요한 계산을 피하고 코드의 가독성을 높일 수 있다.
예를 들어, totalPrice 함수에서 가격이 100을 초과하는 경우 바로 할인을 적용하고 결과를 반환하는 코드를 짤 때, 조기 리턴을 하지 않으면 할인 로직을 처리한 후에만 결과를 반환하기 때문에 추가적인 코드가 필요하다.
아래는 totalPrice 메서드에서 조기 리턴을 활용하는 예시 코드이다.
public static void main(String[] args) { System.out.println(totalPrice(4, 50)); } static int totalPrice(int quantity, int price) { int total = quantity * price; if (total > 100) { return (int)(total * 0.9); // 가격이 100을 초과하면 할인 적용 후 바로 반환 } return total; // 그 외의 경우, 총액을 그대로 반환 }
- total에 quantity * price 를 대입한다.
- total의 값이 100보다 큰 경우, total에 0.9를 곱하고 반환한다. 이렇게 하면 메서드를 조기에 종료할 수 있으므로 이후 예외에 대한 처리를 하지 않아도 된다.
2. 보호 구문 (guard clauses)
보호 구문은 본격적인 로직을 실행하기 전에 입력값의 유효성을 검사하는 예외 처리 기법이다. 원하는 코드를 쓰기 전에 조건문을 사용해서 먼저 초기 입력값이 유효한지 확인하고, 유효하지 않으면 즉시 메서드를 종료하거나 기본값을 반환하도록 미리 예외를 처리할 수 있다.
보호구문을 활용하면 예측 가능한 오류를 사전에 처리하고, 잘못된 입력에 대해 안전한 기본값을 반환함으로써 프로그램의 안정성을 높일 수 있다.
import java.util.List; static double calculateAverage(List<Integer> numbers) { if (numbers == null || numbers.isEmpty()) { // null 이거나 빈 리스트이면 0 반환 return 0; } int total = numbers.stream().mapToInt(i -> i).sum(); // stream 사용하여 합산 return (double) total / numbers.size(); // 평균 계산 후 반환 }
코드를 보면 null 이거나 데이터가 없을 때 바로 예외처리를 해서 메서드가 종료된다.
그리고 실제로 작성하는 로직은 예외처리 뒤에 온다.
3. 제네릭(generic)
제네릭은 빌드 레벨에서 타입을 체크해서 타입 안정성을 제공하고, 타입 체크과 형변환을 생략 할 수 있게 해 주기 때문에 코드가 간결해 진다.
// 1. 일반 List 사용 (타입 안전하지 않음) List<Object> list = new ArrayList<>(); // List의 타입을 Object로 설정 list.add(10); list.add("abc"); // 런타임 오류 발생 (강제로 타입 캐스팅) // String을 int로 캐스팅하면 ClassCastException 발생 int sum1 = (int) list.get(0) + (int) list.get(1); // 2. 제네릭을 사용한 List (타입 안전함) // 제네릭을 사용하여 Integer만 저장 가능 List<Integer> genericList = new ArrayList<>(); genericList.add(10); // genericList.add("abc"); // 컴파일 오류(문법 - 빌드 오류), Integer만 허용됨 // sum2 계산에서 타입이 맞지 않음 int sum2 = genericList.get(0) + genericList.get(0); // 오류 없이 정상적으로 계산
코드를 보면 1에서는 런타임 오류가 발생하고 2에서는 빌드 오류가 발생한다. List를 정의할 때 <Integer> 과 같이 타입을 강제하는 것을 제네릭이라고 한다. 테네릭은 타입에 맞지 않은 데이터를 추가하려고 할 때 문법 오류를 발생시키기 때문에 개발자의 실수를 미리 방지 해 준다.
그래서 1처럼 작성하면 코드를 실제로 실행 해 봐야지만 오류가 발생한다는 것을 알 수 있지만, 2는 빌드 자체가 안 되기 때문에 런타임 버그를 방지할 수 있다. 또 데이터에 접근해서 사용하려고 할 때 형변환을 할 필요가 없기 때문에 코드가 간결해진다. 코테에서는 여러 타입의 데이터를 하나의 컬렉션에 넣어야 하는 경우는 거의 없기 때문에 제네릭으로 타입을 강제해서 실수를 방지하는 것이 좋다.
4. 해시맵
자바의 해시맵(hashmap)은 키(key)와 값(value) 쌍을 저장하는 해시테이블로 구현되어 있다. 키를 사용해서 값을 검색하는 자료구조라고 생각하면 편하다.
해시맵 초기화
키는 문자열, 값은 32비트 정수형을 저장하는 해시맵 선언
HashMap<String, Integer> map = new HashMap<>();
해시맵 데이터 삽입 & 전체 출력
// 해시맵 값 삽입 map.put("apple", 1); map.put("banana", 2); map.put("orange", 3); // 해시맵 값 출력 System.out.println(map); // {banana=2, orange=3, apple=1}

해시맵 데이터 검색
해시맵에 "apple" 문자열과 일치하는 키가 있는지 확인하고, 일치하는 키를 찾으면 키-값을 출력한다.
String key = "apple"; if (map.containsKey(key)) { // containsKey로 수정 int value = map.get(key); // 오타 수정: 'key'로 변경 System.out.println(key + ": " + value); // apple: 1 } else { System.out.println(key + "는 해시맵에 존재하지 않습니다."); }

해시맵 데이터 수정
해시맵에서 키 "banana"를 검색해서 해당 키의 값을 4로 바꾸는 코드
해시맵 내부는 해시 테이블로 구성되어 있기 대문에 해시 테이블에서 키를 찾거나 하지 않아도 된다.
map.put("banana", 4 ); System.out.println(map); // {banana=4, orange=3, apple=1}

해시맵 삭제
키 "orange"를 찾아 해시맵에서 삭제하기
map.remove("orange"); System.out.println(map); // {banana=4, apple=1}

참고로 자바에는 해시맵과 유사하지만 '키-값' 쌍이 아니라 '키'만 저장하는 해시 셋도 있다.
언제 HashSet을 사용하고, 언제 HashMap을 사용할까?
HashSet
중복을 제거하거나, 존재 여부만 체크하고 싶을 때 사용하고 순서가 중요하지 않다.
예: 특정 숫자가 배열에 포함되어 있는지 확인할 때
HashMap
키와 값의 관계를 저장하고 싶을 때 사용한다.
예: 학생 이름과 성적, 상품 ID와 가격을 저장할 때
5. 문자열 (String)
중요한 점은 문자열을 추가하고 삭제하는 동작을 할 때, 문자열은 immutable 객체이므로 기존 객체를 수정하는 것이 아니라 새로운 객체를 반환한다는 사실이다.
String string = "he" string += "llo" System.out.println(string) // "hello"

문자열 수정
그렇다면 문자열을 수정하고 싶을 때는 어떻게 해야 하는가? 그럴 때는 replace() 메서드를 사용한다. replace() 메서드는 첫 번째 인자로 찾을 문자열을, 두 번째 인자로 변경할 문자열을 넣어서 사용한다.
예를 들어서 replace(A, B)는 메서드의 검색 대상 문자 A를 모두 찾아서 일괄 B로 변환한다.
String string = "Hello"; string = string.replace("l", ""); // "l" 모두 삭제 System.out.println(string); // "Heo"
6. StringBuffer와 StringBuilder
위에서 언급한 것처럼 자바에서 String 객체는 값을 변경할 수 없는 Immutable 객체이다. '값을 변경할 수 없다'는 것이 어떤 의미인지 성능 측면에서 알아보기 위해 아래 예시 코드를 보자.
String s = "abc" System.out.println(System.identityHashCode(s)); // 1808253012 s += "def"; System.out.println(System.identityHashCode(s)); // 589431969 System.out.println(s) // abcdef
System.identityHashCode()
메서드는 객체를 특정하는 식별값을 반환한다. String s의 abc에 def를 이어 붙였을 뿐인데 identityHashCode() 메서드의 출력값이 달라진다. 이건 "abc"값만 가지고 있던 s와 "abcdef" 값을 가지고 있는 s가 서로 다른 객체임을 의미한다. 즉, String 객체의 값을 변경하는 새로운 String 객체를 만들고 값을 복사하는 작업이 수행됨을 의미한다. 따라서 s+="def"를 수행 할 때 내부에서는 아래와 같은 연산이 수행된다.
- 새로운 String s 객체를 생성
- s가 가진 "abc" 값을 하나씩 복사
- "abc" 뒤에 "def" 저장
그 결과 s += "def" 코드 한 줄 에서 총 6번의 내부 연산("abc"값 3개 복사, "def"값 3개 저장)이 수행된다. 시간 복잡도로 따지면 문자열의 길이를 N이라고 했을 때 O(N)이다.
그래서 아래처럼 N = 100,000인 코드를 수행하면 컴퓨터에서 6초가 걸린다.
public class Main { public static void main(String[] args) { long start = System.currentTimeMillis(); String s = ""; for (int i = 1; i <= 100000; i++) { s += i; // String을 계속 수정 (비효율적이지만 의도적으로 유지) } long end = System.currentTimeMillis(); System.out.println((end - start) / 1000.0 + "초"); // 괄호 수정 완료 } }
이런 문제를 해결하기 위해 등장한 것이 StringBuilder 클래스와 StringBuffer 클래스이다. 두 클래스는 mutable 하므로 값을 변경할 때 시간복잡도 관점에서 훨씬 더 효율적이다. 다음 코드는 위 코드와 비교했을 때 s 객체에 저장한 문자열은 동일하다. 하지만 실행 시간은 전혀 다르다. 고작 0.005초가 소요된다.
public class Main { public static void main(String[] args) { long start = System.currentTimeMillis(); StringBuilder sb = new StringBuilder(); // String 대신 StringBuilder 사용 for (int i = 1; i <= 100000; i++) { sb.append(i); // 문자열 추가 } long end = System.currentTimeMillis(); System.out.println((end - start) / 1000.0 + "초"); // 괄호 수정 완료 } }
따라서 String 값을 변경하는 연산이 많을 때에는 효율이 높은 StringBuilder 클래스나 StringBuffer 클래스를 사용해야 한다. 두 클래스의 차이는 멀티스레드 환경에서 Thread-Safe 여부로 나누어진다. 하지만 일반적으로 대부분의 코딩 테스트에서는 다수의 스레드를 형성할 필요가 없기 때문에, Thread-Safe가 없는 StringBuilder 클래스가 속도 측면에서 약간 미세하지만 더 빠르므로 권장된다.
int 와 Integer의 차이 ▼
int 타입
- Primitive type
- 메모리에서 직접 값을 저장하며, 성능이 빠르다.
- null 값을 가질 수 없다.
- 기본 값은 0이다.
Integer 타입
- Reference type (참조 타입)
- Integer는 int를 감싸는 클래스이다.
- 객체로 처리되며, int와 달리 메서드를 호출할 수 있다.
- null 값을 가질 수 있다.
- Integer는 int와 상호 변환할 수 있는 방법을 제공한다. (auto-boxing과 unboxing)
int primitiveInt = 10; // Primitive type Integer wrapperInt = 10; // Reference type (auto-boxing) Integer nullInteger = null; // Reference type can be null // Unboxing (Integer -> int) int unboxedInt = wrapperInt; // 자동으로 unboxing 발생
성능적인 면에서 int는 primitive type이므로 연산이 빠르고, 메모리 사용도 적다. 하지만 유연성은 Integer는 객체로서 null을 허용하고, 다양한 메서드 및 클래스를 활용할 수 있기 때문에 Integer이 더 유연하다. 예를 들어, 컬렉션(List, Set 등)에서 Integer를 사용할 수 있기 때문이다.
리마인드
- 자바의 데이터 타입에는 primitive types와 reference types가 있다. primitive types의 연산 속도가 reference types보다 더 빠르기 때문에 가능하다면 primitive types을 사용하는 것이 좋다.
- 자바의 컬렉션 프레임워크의 리스트, 해시맵은 코테에서 자주 사용하기 때문에 꼭 알아두어야 한다.
- 자바에서 String 클래스의 객체는 Immutable 객체이다. 한번 선언하면 값을 변경할 수 없기 때문에 + 연산 등을오 값을 변경할 경우 효율이 좋지 않다. String의 + 연산이 자주 발생하는 경우 StringBuilder을 사용하는 것이 좋다.
- 조기반환, 보호구문, 제네릭 기법을 사용하면 코드의 가독성과 효율성을 높일 수 있다.
참고 자료 & 포스팅 작성에 도움을 주신 분
1. chatGPT 에 궁금한 점은 물어 보며 작성
2. 김희성, 프로그래머스 코딩테스트, 프로그래머스, 2023
'코딩테스트 > 알고리즘' 카테고리의 다른 글
[자료구조] 그림으로 쉽게 이해하는 자바의 Queue (큐) (8) | 2025.02.21 |
---|---|
[코딩테스트/알고리즘 맛집] 자바 스택(Stack) 문제 유형과 코드 정리 (12) | 2025.02.19 |
[알고리즘] 그림과 문제로 알아보는 이진트리 탐색 (이분 탐색, Binary Search) (12) | 2025.02.15 |
[코딩테스트/알고리즘 맛집] 자바 정렬(Sort) 코딩테스트에서 자주 나오는 문제 유형과 풀이 팁 (30) | 2025.02.08 |
[코딩테스트/알고리즘 맛집] 그리디 알고리즘 (Greedy Algorithm) 으로 빠르고 효율적으로! (18) | 2025.02.05 |