컴퓨터 언어

Java 코딩 테스트 문법 및 주의 사항

웅대 2025. 6. 13. 12:54
728x90

개요

코딩 테스트에서 자주 나오는 Java 문법들과 주의 사항들에 대해 정리하였습니다.

 

1. BufferedReader와 BufferedWriter를 활용한 입출력

왜 BufferedReader/BufferedWriter를 사용해야 할까?

입력에는 Scanner, 출력에는 System.out을 사용하는 것이 일반적인 입출력 방식입니다.

 

먼저 System.out을 여러 번 사용하게 될 경우 그 때마다 system call이 발생하게 됩니다.

System.out.println("Hello"); // 시스템 콜 발생
System.out.println("World"); // 시스템 콜 발생

 

그에 비해 BufferedWriter의 경우 버퍼에 문자열들을 보관해두었다가 한 번에 출력하기 때문에 시스템 콜 횟수를 줄일 수 있습니다.

 

I/O 작업이 cpu 작업에 비해 많은 시간이 걸린다는 것을 생각하면 여러 데이터를 출력할 때 매번 System.out을 활용하는 것보다 BufferedWriter를 사용하여 메모리에 보관해두었다가 출력하는 것이 시간을 단축할 수 있습니다.

 

물론 코딩 테스트에서 BufferedWriter가 아닌 System.out을 사용했다고 해서 시간 초과 문제가 발생하는 경우는 매우 드물겁니다.

 

보조적인 시간 단축 수단에 해당하기 때문에 최적의 알고리즘을 적용하는 것이 가장 중요합니다.

BufferedReader 사용법

코딩 테스트에서는 공백으로 구분되어 데이터가 주어지는 경우가 있습니다.

 

이 데이터를 활용하려면 StringTokenizer를 사용해야 합니다.

 

 

 

BufferedWriter 사용법

import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
import java.io.IOException;

public class OutputExample {
    public static void main(String[] args) throws IOException {
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        
        // 문자열 출력
        bw.write("Hello World");
        bw.newLine(); // 줄바꿈
        
        // 숫자 출력
        int result = 42;
        bw.write(String.valueOf(result));
        bw.newLine();
        
        bw.flush(); // 버퍼 비우기 (필수!)
        bw.close();
    }
}

 

  • BufferedReader는 readLine()만 제공하므로 StringTokenizer나 split()과 함께 사용
  • BufferedWriter는 반드시 flush()나 close() 호출
  • IOException 처리 필요 (throws IOException 추가)

2. 배열 선언 시 원시 타입 vs 참조 타입 주의사항

원시 타입 배열의 기본값

원시 타입 배열을 선언하면 각 타입의 기본값으로 자동 초기화됩니다.

public class PrimitiveArrayExample {
    public static void main(String[] args) {
        int[] intArray = new int[5];        // 모든 원소가 0으로 초기화
        boolean[] boolArray = new boolean[5]; // 모든 원소가 false로 초기화
        double[] doubleArray = new double[5]; // 모든 원소가 0.0으로 초기화
        
        System.out.println(intArray[0]);    // 0 출력
        System.out.println(boolArray[0]);   // false 출력
    }
}

참조 타입 배열 주의 사항

참조 타입 배열은 모든 원소가 null로 초기화됩니다.

 

즉, 따로 각 배열의 원소를 초기화 하지 않고 접근한다면 NullPointerException이 발생할 수 있습니다!

public class ReferenceArrayExample {
    public static void main(String[] args) {
        String[] stringArray = new String[5];
        Integer[] integerArray = new Integer[5];
        int[][] twoDimArray = new int[3][]; // 1차원 배열 참조들이 null!
        
        // ❌ NullPointerException 발생!
        // System.out.println(stringArray[0].length()); 
        // System.out.println(integerArray[0] + 1);
        // twoDimArray[0][0] = 1;
        
        // 2차원 배열 올바른 초기화
        for (int i = 0; i < twoDimArray.length; i++) {
            twoDimArray[i] = new int[4]; // 각 행을 별도로 초기화
        }
        
        // 또는 한 번에 초기화
        int[][] properArray = new int[3][4]; // 모든 원소가 0으로 초기화됨
    }
}

 

3. Comparable과 Comparator로 정렬 기준 설정

Comparable 인터페이스

클래스 자체에 정렬 기준을 정의할 수 있습니다.

class Student implements Comparable<Student> {
    String name;
    int score;
    
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    
    @Override
    public int compareTo(Student other) {
        // 점수 기준 내림차순 정렬
        return Integer.compare(other.score, this.score);
        
        // 여러 조건으로 정렬하는 경우
        // if (this.score != other.score) {
        //     return Integer.compare(other.score, this.score); // 점수 내림차순
        // }
        // return this.name.compareTo(other.name); // 이름 오름차순
    }
    
}

// 사용 예시
List<Student> students = Arrays.asList(
    new Student("Alice", 85),
    new Student("Bob", 92),
    new Student("Charlie", 78)
);

Collections.sort(students); // compareTo 메서드 사용
System.out.println(students); // [Bob(92), Alice(85), Charlie(78)]

Comparator 인터페이스

정렬 기준을 별도로 정의하거나, 여러 가지 정렬 방식을 제공할 때 사용합니다.

import java.util.Arrays;
import java.util.Comparator;

public class ComparatorExample {
    public static void main(String[] args) {
        int[][] points = {{1, 3}, {2, 1}, {3, 2}, {1, 1}};
        
        // 1. 람다 표현식 사용
        Arrays.sort(points, (a, b) -> {
            if (a[0] != b[0]) {
                return Integer.compare(a[0], b[0]); // x좌표 오름차순
            }
            return Integer.compare(a[1], b[1]); // y좌표 오름차순
        });
        
        // 2. 익명 클래스로 Comparator 객체 직접 생성
        Arrays.sort(points, new Comparator<int[]>() {
            @Override
            public int compare(int[] a, int[] b) {
                if (a[0] != b[0]) {
                    return Integer.compare(a[0], b[0]); // x좌표 오름차순
                }
                return Integer.compare(a[1], b[1]); // y좌표 오름차순
            }
        });
        
        // 3. Comparator 메서드 체이닝
        Arrays.sort(points, Comparator
            .comparingInt((int[] a) -> a[0])  // x좌표 기준
            .thenComparingInt(a -> a[1]));    // y좌표 기준
        
        // 4. 내림차순 정렬
        Arrays.sort(points, (a, b) -> Integer.compare(b[0], a[0]));
        
        // 5. 문자열 배열 정렬 (길이 기준, 길이 같으면 사전순)
        String[] words = {"apple", "pie", "washington", "book"};
        Arrays.sort(words, (a, b) -> {
            if (a.length() != b.length()) {
                return Integer.compare(a.length(), b.length());
            }
            return a.compareTo(b);
        });
        
        System.out.println(Arrays.deepToString(points));
        System.out.println(Arrays.toString(words));
    }
}

4. 문자열 변경 메모리 주의 사항 : String vs StringBuilder

String 연산의 메모리 비효율성

Java에서 String은 불변(immutable) 객체입니다. 따라서 문자열을 반복적으로 합치는 작업은 매번 새로운 String 객체를 생성하여 메모리 초과가 발생할 수 있습니다.

public class StringPerformanceExample {
    public static void main(String[] args) {
        // ❌ 비효율적인 방법
        String result = "";
        for (int i = 0; i < 10000; i++) {
            result += "a"; // 매번 새로운 String 객체 생성
        }
        // 시간 복잡도: O(n²) - 매우 느림!
        
        // ✅ 효율적인 방법
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 10000; i++) {
            sb.append("a"); // 내부 배열에 문자 추가
        }
        String efficient = sb.toString();
        // 시간 복잡도: O(n) - 빠름!
    }
}

StringBuilder 사용법

import java.util.Arrays;

public class StringBuilderExample {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        
        // 1. 기본 사용법
        sb.append("Hello");
        sb.append(" ");
        sb.append("World");
        System.out.println(sb.toString()); // "Hello World"
        
        // 2. 메서드 체이닝
        sb.clear(); // 초기화
        String result = sb.append("Java")
                         .append(" is")
                         .append(" awesome")
                         .toString();
        
        // 3. 다양한 타입 추가
        sb.clear();
        sb.append("Number: ").append(42);
        sb.append(", Boolean: ").append(true);
        sb.append(", Character: ").append('A');
        
        // 4. 문자열 삽입과 삭제
        sb.clear();
        sb.append("Hello World");
        sb.insert(5, " Beautiful"); // "Hello Beautiful World"
        sb.delete(5, 15); // "Hello World" (5번째부터 15번째 전까지 삭제)
        sb.reverse(); // "dlroW olleH"
        
        // 5. 배열 요소를 문자열로 변환
        int[] arr = {1, 2, 3, 4, 5};
        sb.clear();
        for (int i = 0; i < arr.length; i++) {
            sb.append(arr[i]);
            if (i < arr.length - 1) {
                sb.append(", ");
            }
        }
        System.out.println(sb.toString()); // "1, 2, 3, 4, 5"
    }
}

 

5. 배열과 List의 차이점과 순회 방법

배열 vs List 비교

  배열 (Array) List (ArrayList)
크기 고정 크기 동적 크기
타입 원시 타입 + 참조 타입 참조 타입만 (Wrapper 클래스 사용)

배열 활용법

public class ArrayExample {
    public static void main(String[] args) {
        // 1. 배열 선언과 초기화
        int[] arr1 = new int[5];                    // 크기 5, 모든 원소 0
        int[] arr2 = {1, 2, 3, 4, 5};              // 초기값 지정
        int[] arr3 = new int[]{1, 2, 3, 4, 5};     // 명시적 초기화
        
        // 2. 배열 순회 방법들
        int[] numbers = {10, 20, 30, 40, 50};
        
        // 기본 for문 (인덱스 필요한 경우)
        for (int i = 0; i < numbers.length; i++) {
            System.out.println("Index " + i + ": " + numbers[i]);
        }
        
        // 향상된 for문 (Enhanced for loop)
        for (int num : numbers) {
            System.out.println(num);
        }
        
    }
}

List 활용법

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class ListExample {
    public static void main(String[] args) {
        // 1. List 선언과 초기화
        List<Integer> list1 = new ArrayList<>();
        List<Integer> list2 = Arrays.asList(1, 2, 3, 4, 5);
        List<Integer> list3 = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
        
        // 2. List 조작
        list1.add(10);
        list1.add(20);
        list1.add(1, 15); // 인덱스 1에 15 삽입
        list1.remove(0);  // 인덱스 0 제거
        list1.remove(Integer.valueOf(20)); // 값 20 제거
        
        // 3. List 순회 방법들
        List<String> fruits = Arrays.asList("apple", "banana", "cherry");
        
        // 기본 for문
        for (int i = 0; i < fruits.size(); i++) {
            System.out.println("Index " + i + ": " + fruits.get(i));
        }
        
        // 향상된 for문
        for (String fruit : fruits) {
            System.out.println(fruit);
        }
        
        
        // 인덱스와 함께 순회하고 싶은 경우
        for (int i = 0; i < fruits.size(); i++) {
            System.out.println(i + ": " + fruits.get(i));
        }
    }
}

배열과 List 변환

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        // 1. 배열 → List
        int[] arr = {1, 2, 3, 4, 5};
        
        List<Integer> list1 = new ArrayList<>();
        for (int num : arr) {
            list1.add(num);
        }
        
        String[] strArr = {"a", "b", "c"};
        List<String> strList = Arrays.asList(strArr);
        
        // 2. List → 배열
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        
        // Integer 배열로 변환
        Integer[] integerArr = numbers.toArray(new Integer[numbers.size()]);        
    }
}

 

6. 정수 overflow 주의

코테를 하다보면 java의 정수를 많이 사용하게 됩니다.

 

java int의 범위는 -2,147,483,648부터 2,147,483,647이기 때문에 이 범위를 넘어가면 정답을 맞추지 못 할 수 있습니다.

 

만약 이 범위가 넘어가는 값을 저장해야 한다면 int가 아닌 long을 활용해야 합니다.

 

java long의 범위는 -9,223,372,036,854,775,808부터 9,223,372,036,854,775,807로 int보다 더 넓은 범위의 정수를 저장할 수 있습니다.

 

한 번 15억 정수 두 개를 더해서 long에 저장해봅시다.

 

public class Main {
    public static void main(String[] args) {
        int a = 1500000000;
        int b = 1500000000;
        long c = a+b;
        System.out.println(c);
        // overflow 발생 : -1294967296
    }
}

 

overflow가 발생해서 이상한 값이 저장됩니다.

 

이 현상을 해결하려면 더할 때 적어도 하나의 변수를 long으로 변환해야 합니다.

public class Main {
    public static void main(String[] args) {
        int a = 1500000000;
        int b = 1500000000;
        long c = (long)a+b;
        System.out.println(c);
        // 정상 실행 : 3000000000
    }
}

 

728x90