티스토리 뷰

 

 

Scanner Class

장점

 nextInt, nextLong 등의 편리한 메소드를 제공하기 때문에 입력을 받기 편하다. System.out.print 메소드와 같이 사용하면 짧고 가독성도 쓸만한 코드를 금방 짤 수 있다.

 

문제점

 사용하는 건 편한데, 너무 느리다. 왜 이렇게 느린지 궁금해서 intellij를 이용해 내부 클래스를 한 번 살펴봤다.

 

 

 Pattern 클래스와 Matcher 클래스가 보인다. 벌써부터 단순 입력만을 위해 쓰기에는 복잡하지 않을까 하는 느낌이 온다.

 

 

 클래스 코드 시작 라인이 304인데, 내부에서 사용할 변수 선언이 519 라인까지이다. 변수 선언에만 200 라인을 사용하고 총 라인 수는 2600 라인이다. BufferedReader 클래스의 총 라인 수가 500 ~ 600 라인 정도 되는 것에 비하면 라인 수가 상당하다. Scanner 클래스를 사용하는 것만으로도 시간을 꽤 잡아먹었는데, 다 이유가 있었다.

 

 

 Scanner 클래스를 사용하면서 정말 많이 사용하게 되는 메소드인데, 내부 코드가 복잡하다. 내부적으로 캐쉬를 사용하는지 캐싱과 관련된 메소드도 포함되어 있고 matcher의 group 메소드도 사용하고 integer token을 처리하기 위한 메소드도 사용하고 있다. 여러 메소드를 거친 뒤에야 Integer 클래스의 parseInt 메소드를 사용해 정수를 반환한다.

 

 알고리즘 문제에서 주어지는 입력은 포맷이 일정한 형식을 잘 갖추고 있어서 토큰을 받고 타입에 따라 형변환만 적절히 잘해주면 문제가 없다. Scanner 클래스의 여러 메소드들은 알고리즘 문제 풀이에 사용하기엔 쓸모없는 기능을 너무 많이 담고 있어 무겁다.

 

 

BufferedReader, BufferedWriter, StringTokenizer 조합

장점

 Scanner 클래스와는 비교할 수 없는 그 속도가 장점이다. 입출력이 조금만 많아져도 이 클래스들을 사용해야 문제를 풀 수 있다. 사실상 이것 외엔 선택지가 없다.

 

문제점

 새 라인을 입력 받을 때마다 StringTokenier에 넣어줘야 한다. 그리고 token을 변수에 할당할 때마다 알맞게 파싱해줘야 한다. 그리고 타이핑 할 때 자동완성의 덕을 그렇게 크게 못 본다. 자동완성을 기다리는 시간이나 내가 치는 시간이나 큰 차이가 없다.

 

 또, 입출력 할 때 사용하는 코드 길이도 길다. 이게 생각보다 눈을 어지럽게 한다. 코드 복잡도가 조금 올라간 건데 이게 의외로 영향을 꽤 준다. 자바 코딩 실력이 꽤 오른 지금도 문제 풀이엔 C++이 훨씬 편하다. C++ 사용할 때는 뭔가 머리에 공간이 조금 더 남는 느낌이다.

 

Java code
C++ code

 

 

그래서 만드는 입출력 함수

어떻게 만들까?

 처음엔 여러 가지로 많이 생각했는데, 적당히 빠르게 구현할 수 있도록 타협했다. 코드가 복잡하면 나중에 다른 환경에서 알고리즘 문제를 풀 때 바로 구현해서 사용하기 힘들다. 메소드는 Scanner를 참조하고 내부 구현만 BufferedReader, BufferedWriter, StringTokenizer로 적당히 구현하면 될 것 같다. 만들어 놓은 뒤에 Live Templates에 등록하면 코드도 금방 불러와서 사용할 수 있다.

 

구현

static class FastReader {
    private final BufferedReader br;
    private StringTokenizer st;

    public FastReader() {
        br = new BufferedReader(new InputStreamReader(System.in));
    }

    String next() {
        if (st != null && st.hasMoreTokens()) {
            return st.nextToken();
        }

        try {
            st = new StringTokenizer(br.readLine());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return st.nextToken();
    }

    String nextLine() {
        if (st != null && st.hasMoreTokens()) {
            return st.nextToken("\n");
        }

        String str = "";
        try {
            str = br.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return str;
    }

    int nextInt() {
        return Integer.parseInt(next());
    }

    long nextLong() {
        return Long.parseLong(next());
    }

    double nextDouble() {
        return Double.parseDouble(next());
    }
}

static class FastWriter {
    private final BufferedWriter bw;

    public FastWriter() {
        bw = new BufferedWriter(new OutputStreamWriter(System.out));
    }

    public void print(Object object) {
        try {
            bw.append(object.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void println(Object object) {
        try {
            bw.append(object.toString()).append("\n");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void close() {
        try {
            bw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

 이전에는 main 함수 내에서 BufferedReader, Writer를 사용해서 Throw IOException을 main 함수에 달아줘야 했다. 이게 거슬려서 클래스 메소드 내에서 try-catch로 exception을 다 처리하도록 했다.

 

테스트

 항상 쓰던 메소드를 클래스로 래핑한 느낌이라 따로 빡세게 테스트는 안 하고 atcoder 문제 하나 풀어서 테스트했다. 잘 작동한다. 풀이 코드도 이전보다 훨씬 깔끔해져서 사용하기 편하다. 입력을 처리하는 부분에 힘 쓰는 게 귀찮았는데, 그게 시원하게 해결됐다.

 

public class ABC289_B {
    static class FastReader { ... }
    static class FastWriter { ... }

    public static void main(String[] args) {
        FastReader in = new FastReader();
        FastWriter out = new FastWriter();

        int n = in.nextInt();
        int m = in.nextInt();

        int[] arr = new int[m];
        for (int i = 0; i < m; i++) {
            arr[i] = in.nextInt();
        }

        boolean[] re = new boolean[n + 1];
        for (int x : arr) {
            re[x] = true;
        }

        for (int i = 1, j = 1; i <= n; i = ++j) {
            while (re[j]) j++;
            for (int k = j; k >= i; k--) {
                out.print(k + " ");
            }
        }

        out.close();
    }
}

 

Live Templates

 FastReader, FastWriter 클래스와 main 함수 내에 세팅까지 다 복사해서 fiom 키워드로 등록했다. 그리고 클래스 내에서 사용되는 라이브러리도 ifiom으로 등록했다. 미리 import 안 하면 FastReader와 FastWriter 구현하는 데 필요한 것들을 하나하나 import 하도록 intellij가 추천하는데, 이게 불편하다. 처음부터 import를 다 하면 불편함을 피할 수 있다.

 

기본 세팅

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함