728x90

https://www.acmicpc.net/problem/31562

 

오늘은 백준 에서 31562 전주 듣고 노래 맞히기 에 대해서 진행해보도록하겠습니다.

 


문제 :

윤수와 정환은 「전주 듣고 노래 맞히기」라는 게임을 할 예정이다. 「전주 듣고 노래 맞히기」는 주어진 노래의 전주를 듣고 먼저 제목을 맞히는 사람이 점수를 얻어 최종적으로 점수가 더 많은 사람이 이기는 게임이다. 절대 음감을 가진 윤수는 노래의 첫 네 음만 듣고도 어떤 노래든 바로 맞힐 수 있다. 따라서, 정환은 윤수를 이기기 위해 첫 세 음만으로 노래를 맞히게 해주는 프로그램을 만들려고 한다. 우선 정환이 알고 있는 노래 제목, 음이름 등을 데이터로 만든 뒤 프로그램을 구현하기 시작했다. 예를 들어, 다음은 TwinkleStar(반짝반짝 작은 별)의 악보 중 일부이다.

 

위 악보를 박자와 관계없이 음이름으로 표현하면 CCGGAAG가 된다.

윤수를 이기기 위해서는 이 프로그램이 첫 세 음인 CCG만으로 노래 제목인 TwinkleStar를 출력할 수 있어야 한다. 또한, 세상의 모든 노래를 아는 윤수와 다르게 정환은 음을 아는 노래가 N개뿐이다. 그래서 프로그램에 N개의 노래의 정보를 저장해 놓을 것이다. 만약 저장된 노래 중 입력한 첫 세 음으로 시작하는 노래가 여러 개 있어 무슨 노래인지 정확히 알 수 없는 경우 ?를 출력하고, 입력한 첫 세 음에 맞는 저장된 노래가 없을 경우 !를 출력한다.

정환을 도와서 첫 세 음만으로 본인이 음을 아는 노래를 맞히는 프로그램을 완성하자. 이 프로그램은 대문자와 소문자를 구분한다.


입력 :

첫 번째 줄에 정환이 음을 아는 노래의 개수 N, 정환이 맞히기를 시도할 노래의 개수 M이 공백으로 구분되어 주어진다.

두 번째 줄부터 N개의 줄에 걸쳐 노래 제목의 길이 T, 영어 대소문자로 이루어진 문자열 노래 제목 S, 해당 노래에서 처음 등장하는 일곱 개의 음이름 a1,a2,a3,a4,a5,a6,a7이 공백으로 구분되어 주어진다.

 N+2번째 줄부터 M개의 줄에 걸쳐 정환이 맞히기를 시도할 노래의 첫 세 음의 음이름 b1,b2,b3가 공백으로 구분되어 주어진다.

주어지는 음이름은 각각 C, D, E, F, G, A, B 중 하나이다. 같은 제목이 두 번 이상 주어지지 않는다.

 

출력 : 

정환이 맞히기를 시도할 각 노래에 대하여 프로그램에 저장된 노래와 첫 세 음이 동일한 노래가 하나만 있다면 해당 노래의 제목을, 두 개 이상이면 ?을, 없다면 !을 한 줄에 하나씩 출력한다.

 

예제 :

4 4
11 TwinkleStar C C G G A A G
8 Marigold E D E F E E D
23 DoYouWannaBuildASnowMan C C C G C E D
12 Cprogramming C C C C C C C
E D E
C G G
C C C
C C G

 

JAVA  

public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        String[] NM = br.readLine().split(" ");

        int N = Integer.parseInt(NM[0]); // 음을 아는 노래의 개수 N
        int M = Integer.parseInt(NM[1]); // 맞출 노래의 개수 M

        String[][] musicInfo = new String[N][]; // [제목 길이, 제목, 음이름]이 담긴 이차원 배열 선언

        for(int i = 0; i < N; i++) {
            musicInfo[i] = br.readLine().split(" "); // 음악 정보 초기화
        }

        for(int j = 0; j < M; j++) {
            int cnt = 0;
            String code = br.readLine().replaceAll(" ", ""); 
            ArrayList<String> list = new ArrayList<>(); 
            for(int i = 0; i < N; i++) {
                // code와 가장 맨앞부터 3개의 음이름 같으면 list에 저장
                if((musicInfo[i][2] + musicInfo[i][3] + musicInfo[i][4]).equals(code)) {
                    list.add(musicInfo[i][1]);
                }
            }

            int listSize = list.size();

            if(listSize == 1) { // size 1이면 제목 출력
                System.out.println(list.get(0));
            }
            else if(listSize > 1) { // size 1보다 크면 ? 출력
                System.out.println("?");
            }
            else { // size 0이면 ! 출력
                System.out.println("!");
            }
        }
    }

 

JAVA 2

public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringBuilder sb = new StringBuilder();

        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken()), m = Integer.parseInt(st.nextToken());

        Map<String, String> map = new HashMap<>();
        while (n-- > 0) {
            String[] arr = br.readLine().split(" ", 3);
            String key = arr[2].substring(0, 5);

            map.put(key, map.containsKey(key) ? "?\n" : arr[1] + "\n");
        }

        while (m-- > 0) sb.append(map.getOrDefault(br.readLine(), "!\n"));

        bw.write(sb.toString());
        bw.flush();
    }

 

둘의 장단점 : 

첫 번째 코드 (JAVA)

장점:
명확한 흐름: 입력을 받고 처리하는 과정이 명확하게 나누어져 있어 이해하기 쉽습니다.
직관적인 비교: 코드가 음악 정보를 직접 비교하여 일치 여부를 판단하므로, 쿼리의 결과를 이해하기 쉽게 출력합니다.

단점:
비효율성: 모든 쿼리에 대해 음악 정보를 매번 순회하여 비교하므로, 시간이 오래 걸릴 수 있습니다. 쿼리 수가 많아지면 성능이 저하될 수 있습니다.
중복 출력 처리: 중복된 제목이 있을 때, 명시적으로 "?"를 출력하는 로직이 필요하여 코드가 다소 복잡해질 수 있습니다.


두 번째 코드 (JAVA2)

장점:
해시맵 사용: 음악 코드와 제목을 해시맵에 저장하여 O(1) 시간 복잡도로 쿼리를 처리할 수 있습니다. 이로 인해 성능이 크게 향상됩니다.
중복 처리 간편화: 해시맵에 키가 이미 존재할 경우 "?"로 처리하므로, 코드가 간결해지고 중복 제목 처리가 쉽게 이루어집니다.
효율적인 입출력: BufferedWriter와 StringBuilder를 사용하여 입출력을 효율적으로 처리합니다.

단점:
코드 이해의 어려움: 해시맵 사용과 관련된 부분이 초보자에게는 직관적이지 않을 수 있습니다. 코드의 흐름이 첫 번째 코드에 비해 다소 복잡하게 느껴질 수 있습니다.
고정된 키 길이: 코드의 처음 5글자만 사용하므로, 만약 코드가 5글자 이하로는 구분할 수 없는 경우가 생길 수 있습니다.


결론
성능을 중시한다면 두 번째 코드(Main)가 더 좋습니다. 해시맵을 사용하여 쿼리 처리 속도가 빠르고, 중복 처리가 간편하여 전체적인 효율성이 높습니다.
코드의 직관성과 간단함을 중시한다면 첫 번째 코드(bak_31562)가 더 나을 수 있습니다. 읽기 쉬운 구조로 인해 이해하기 쉽습니다.
따라서 사용자의 요구에 따라 선택할 수 있습니다. 성능이 더 중요한 경우에는 두 번째 코드를 추천합니다.
728x90
728x90

객체지향 프로그래밍 시작하기


Static 과 메모리의 관계를 알아보도록 해보자

이 글의 목표는 static 키워드와 메모리 관계에 있어서 이해를 돕고자 또한, JVM 에 사용하는 메모리 모델을 통해 자바 프로그래임이 어떤식으로 동작하는지에 대한 원리를 학습하는것을 목표로 합니다. 


1. static 과 메모리의 관계

2. static 멤버 들의 접근 방법

3. private 생성자와 static과의 관계

4. JVM 이 상요하는 메모리 영역

5. class, object, instance의 상호관계

 

1. static 과 메모리의 관계

메인(시작) 클래스는 왜 객체 생성(new)없이 실행이 되나요?

public class StaticTest {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

메인(시작) 클래스가 동작 되는 방식을 이해 해야 됩니다. 

1. JVM이 실행할 클래스를 찾는다. 찾았다면...->

2. static 키워드가 붙어있는 멤버들을 정해진 메모리(static-zone)위치에 한번 자동으로 로딩한다.

   -> static멤버들은 클래스를 사용하는 시점에서 딱 한번 메모리에 로딩된다는 점이 중요합니다.

   -> 여기서는 main() 메서드가 static 이기 때문에 메모리에 자동으로 로딩한다.

3. JVM 이 static-zone에서 main()메서드를 호출한다.

4. 호출된 메서드를 Call Stack Fame Area(Stack Area)에 push(기계어코드를 넣고) 한 뒤 동작을 시작하면됩니다. 

 

* Call Stack Frame Area 

- 메서드가 호출되면 호출된 기계어코드가 push 되고 실행되는 메모리 공간

- 현재 프로그램이 실행되고 있는 상태를 파악할수 있다.

- LIFO(Last-In-First-Out) 구조이다.

* PC 는 현재 수행중인 프로그램 위치 입니다. 

* 프로그램 종료

- stack에 아무것도 없으면 프로그램이 종료됨.

* 지역변수는 메서드와 동일한 생명주기(life-cycle)을 가진다.

 

4. JVM 이 상요하는 메모리 영역

Method Area 에는 static-zone 과 non-static-zone으로 나뉘는데

메서드의 바이트코드가 할당되는 공간이 메소드영역이고 

static 멤버들은 static-zone으로 할당됩니다.

Heap 영역은 실제 객체가 생성되는 메모리 공간(new 연산자)

GC(gabage collector)에 의해서 메모리가 수집됩니다.

 

Thread -Statck Area

(Call stack Frame Area)

PC register, Native Method Area 

는 메서드가 호출되면 메서드의 기계어코드를 할당받고(Native Method Area)메서드가 실행되는 메모리 공간(Call Stack Frame Area)

(지역변수, 매개변수들이 만들어지는 공간)

PC(Program Counter)에 의해서 현재 실행중인 프로그램의 위치가 관리된다.

LIFO 구조로 운영되는 메모리 공간(메서드의 호출 순서를 알 수 있다.)

 

Runtime Constant Pool

(Literal Pool)

상수 값 할당이 되는 메모리 공간 문자열 중 문자열 상수가 할당되는 메모리 공간

 

728x90
728x90

오늘은 JPA N+1의 문제 해결에 대해서 글을 공유해드리려고 합니다. 

* Java 백엔드 개발자를 위한 데이터베이스 쿼리 최적화에 적합한 내용입니다.

개발자가 직면하는 가장 일반적인 성능 병목 현상 중 하나가 N+1입니다.
애플리케이션이 단 한 번의 쿼리로 동일한 결과를 얻을 수 있는데도 N+1번의 데이터베이스 쿼리를 수행할 때 발생합니다.
과도한 데이터베이스 Hit는 느린 응답 시간, 높은 서버 부하, 열약한 사용자 경험으로 이어질 수 있습니다. 
 
원인을 함께 파악해 보고 개발자가 이러한 문제를 어떻게 완화할지에 대해서 다양한 전략과 기법에 대해서 적어보겠습니다.

앞서, N+1 문제가 무엇인지 알아보도록 하겠습니다.

N+1 문제란 무엇인가?

애플리케이션이 개체목록(예시: 제품, 사용자 또는 게시물 목록)을 가져온 다음 목록의 각 객체애 대해 추가 데이터베이스 쿼리를 수행하여 관련 데이터를 검색할 때 발생을 하게 됩니다.
예를 들어 작성자 정보와 함께 블로그 게시물 목록을 표시하는 Java 애플리케이션을 생각해 보자.
애플리케이션이 글 목록을 가져온 다음 각 글의 작성자 정보를 검색하기 위해 별도의 쿼리를 수행하면 N+1 쿼리가 발생하며, 여기서 N은 글의 수입니다. 이러한 비효율적인 쿼리 패턴은 많은 수의 데이터베이스 HIT로 빠르게 이어질 수 있습니다.
 

N+1 문제 원인

1. 지연 로딩(Lazy Loading) : 
수많은 Java ORM 프레임워크들, 가령 Hibernate에서 기본적으로 지연 로딩을 사용합니다.
지연 로딩이란 관련된 데이터를 가져올때만  데이터베이스에 가져오는 것을 의미합니다. 개발자가  컬렉션의 각 항목에 대한 관련 데이터에 접근할 때 N+1 쿼리를 발생할 수 있습니다.  
 
2.비효율적인 쿼리(Inefficient Queries)
개발자는 관련 데이터를 반복해서 가져오는 코드를 작성하여 최적화된 단일 쿼리로 충분할 수 있는 데이터베이스 쿼리를 여러 번 수행하게 될 수 있습니다.
 
3. 일괄 가져오기 부족 (Lack of Batch Fetching)
일부 ORM 프레임워크에서 제공하는 기술인 일괄 가져오기를 사용하면 단일 쿼리에서 여러 개체에 대한 관련 데이터를 검색할 수 있습니다.
개발자들은 이 기능을 간과하거나 오용하는 경우가 많습니다.
 

N+1 문제를 완화하기 위한 전략들

N+1 문제를 해결하고 과도한 데이터베이스 히트를 최소화하기 위해 Java 백엔드 엔지니어는 다양한 전략과 모범 사례를 활용할 수 있습니다

 

1. 에저 로딩 

에지 로딩을 사용하면 관련 데이터를 미리 불러와 추가 쿼리의 필요성을 줄일 수 있습니다. 대부분의 ORM 프레임워크는 관련 데이터를 로드할 시기와 방법을 지정하는 메커니즘을 제공하므로 데이터베이스 쿼리를 최적화할 수 있습니다.

2. 일괄 가져오기

ORM 프레임워크에서 제공하는 일괄 가져오기 기능을 사용하여 관련 데이터를 하나씩 가져오지 않고 일괄적으로 검색하세요. 이렇게 하면 데이터베이스 쿼리 횟수를 크게 줄일 수 있습니다. 

3. DTO 투영

데이터베이스에서 필요한 데이터만 가져오려면 DTO(데이터 전송 객체) 투영을 사용하는 것이 좋습니다. 이 접근 방식을 사용하면 검색되는 데이터의 양을 최소화하여 쿼리 속도를 높일 수 있습니다.

4. 캐싱

캐싱 메커니즘을 구현하여 자주 액세스하는 데이터를 메모리에 저장하세요. 캐싱은 반복적인 데이터베이스 쿼리의 필요성을 줄여 응답 시간을 개선하고 데이터베이스 부하를 줄이는 데 도움이 될 수 있습니다.

5. 페이지 매김 및 필터링

페이지 매김 및 필터링을 구현하여 단일 쿼리에서 검색되는 레코드 수를 제한하세요. 이는 대규모 데이터 세트를 다룰 때 특히 유용할 수 있습니다.

6. 쿼리 최적화

데이터베이스 쿼리를 정기적으로 검토하고 최적화하세요. 쿼리 실행 계획을 분석하고 데이터베이스 프로파일링 도구를 사용해 성능 병목 현상을 파악하고 해결하세요.
 

마치며

N+1 문제와 과도한 데이터베이스 히트는 Java 백엔드 엔지니어가 직면하는 일반적인 성능 문제입니다. 개발자는 N+1 문제의 원인을 이해하고 일괄 로딩, 배치 가져오기, DTO 예측, 캐싱, 페이지 매김 및 쿼리 최적화와 같은 효과적인 전략을 채택함으로써 애플리케이션의 성능을 크게 향상해 원활한 사용자 경험을 보장하고 서버 부하를 줄일 수 있습니다. N+1 문제를 해결한다는 것은 단순히 데이터베이스 쿼리를 최적화하는 것만이 아니라 사용자에게 효율적이고 확장 가능한 백엔드 솔루션을 제공하는 것입니다.
 

728x90

'DB' 카테고리의 다른 글

Database Lock 이란?  (1) 2024.06.18
PL/SQL 이란  (0) 2022.01.10
데이터베이스 모델링 -1  (0) 2022.01.07
SQL 사용자 권한  (0) 2022.01.05
SQUENCE INDEX(순차 적으로 증가하는 값)  (0) 2022.01.05
728x90

자바 8은 데이터베이스 질의 언어에서 표현식을 처리하는 것처럼 병렬 연산을 지원하는 스트림이라는 새로운 API 를 제공한다. 데이터베이스 질의 언어에서 고수준 언어로 원하는 동작을 표현하면, 구현(자바에서는 스트림 라이브러리가 이 역할을 수행) 에서 최적의 저수준 실행 방법을 선택하는 방식으로 동작한다. 즉, 스트림을 이용하면 에러를 자주 일이키며 멀티코어 CPU 를 이용하는 것보다 비용이 훨씬 비싼 키워드 synchronized를 사용하지 않아도 된다. 

더보기

멀티코어 CPU의 각 코어는 별도의 캐시(빠른 메모리) 를 포함하고 있다. 락을 사용하면 이러한 캐시가 동기화되어야 하므로 속도가 느린 캐시 일관성 프로토콜 인터코어 통신이 이루어진다.

 

조금 다른 관점에서 보면 결국 자바8에 추가된 스트림 API 덕분에 다른 두 가지 기능, 즉 메서드에 코드를 전달하는 간결 기법(메서드 참조와 람다) 과 인터페이스의 디폴트 메서드가 존재 할 수 있음을 알 수 있다. 

 

하지만 스트림 API 때문에 메서드에 코드를 전달하는 기법이 생겼다고 추리하는 것은 메서드에 코드를 전달하는 기법의 활용성을 제한할 수 있는 위험한 생각이다. 

메서드에 코드를 전달하는 기법을 이용하면 새롭고 간결한 방식으로 동작 파라미터화를 구현할 수 있다. 

 

예시 ) 약간만 다른 두 메서드가 있다고 가정하자. 이때 두 메서드를 그대로 유지하는 것보다는 인수를 이용해서 다른 동작을 하도록 하나의 메서드로 합치는 것이 바람직할 수 있다(그러면 복사 및 붙여넣기를 하는 기법에 비해 프로그램이 짧고 간결해지며, 불필요한 에러도 줄일 수 있다.) 조금 경험이 있는 프로그래머라면 자바 8 이전 상황에서는 익명 클래스를 이용해서 동작 파라미터화를 구현할 수 있다고 생각 할 것이다. 

 

메서드에 코드를 전달(뿐만 아니라 결과를 반환하고 다른 자료구조로 전달할 수도 있음) 하는 자바 8 기법은 함수형 프로그래밍에서 위력을 발휘한다. 코드를 전달하거나 조합해서 자바의 강력한 프로그래밍 도구로 활용할 수 있다는 것을 이 책 전반에서 확인할 수 있다. 

 

1.1 절 : 자바가 멀티코어 병렬설(기존의 자바에서 부족했던 특성) 을 더 쉽게 이용할 수 있도록 진화하는 과정과 관련 개념을 설명한다. 

1.2절  : 자바 8에서 제공하는 코드를 메서드로 전달하는 기법이 어떻게 강력한 새로운 프로그래밍 도구가 될 수 있는지 설명한다. 

1.3절 : 스트림 API (병렬형 데이터를 표현하고 이들 데이터를 병렬로 처리할 수 있음을 유연하게 보여주는)가 어째서 강력하고 새로운 프로그래밍 도구인지 설명한다.

1.4절 : 디폴트 메서드라는 새로운 자바 8의 기능을 인터페이스, 라이브러리의 간결셩 유지 및 재컴파일을 줄이는 데 어떻게 활용할 수 있는지 설명한다. 

1.5절 : JVM을 구성하는 자바 및 기타 언어에서 함수형 프로그래밍이라는 존재가 어떤 영향을 미치는지 제시한다. 

 


스트림 처리

스트림이란 한번에  한 개씩 만들어지는 연속적인 데이터 항목들의 모임이다. 이론적으로 프로그램은 입력 스트림에서 데이터를 한 개씩 읽어 들이며 마찬가지로 출력 스트림으로 데이터를 한 개씩 기록한다. 즉, 어떤 프로그램의 출력 스트림은 다른 프로그램의 입력 스트림이 될 수 있다. 

728x90
728x90

안녕하세요, 늘 부족하고 배움을 갈구하는  2년차 백엔드 개발자 던킨 입니다. 

오늘 Modern Java in Action 책을 사고 읽으면서 좋은 내용들이 많아 리뷰를 하려고합니다.

많은 개발자 분들이 이미 이 책에 관련해서 리뷰와 함께 많은 리소스를 공유 해주었다고 해도 과언이 아니지만, 저 또한 이 책에 푹 빠져있는 독자로써

또한, 제 블로그를 찾아오시는분들을 위해 짧게 나마 잘 정리 해서 공유드리고 싶어 리뷰를 시작했습니다.  

 

 


'함수형 프로그래밍은 뭔가요?'

- 함수형 프로그래밍은 프로그래밍 기법을 지칭한다. 함수형 프로그래밍에서는 함수를 값으로 취급한다.

자바 8의 놀라운 점은 함수형 프로그래밍의 여러 장점을 친숙한 자바 문법으로 접목했다는 것이다. 훌룡한 자바 8의 설계 덕분에 함수형 프로그래밍을 자바 8에 새로 추가된 디자인 패턴 처럼 사용할 수 있으며 짧은 시간에 더 명확하고 간결한 코드를 구현할 수 있다. 프로그래밍 무기창고에 더  넓은 영역을 커버하는 무기가 추가되었다고 생각하면됩니다.

 

자바 8에 새롭게 추가된 핵심 기능뿐만 아니라 디폴트 메서드, 새로운 Optional 클래스, CompletableFuture, 새로운 날짜와 시간 API 등 유용한 기능도 설명한다.

 

자바 9에서는 새로운 모듈 시스템, Flow API 를 통한 리액티브 프로그래밍 지원 및 다양한 개선 기능이 추가되었다.

 

 

이 책은 크게 '기초', '함수형 데이터 처리', '스트림과 람다를 이용한 효과적 프로그래밍', '매일 자바와 함께', '개선된 자바 동시성', '함수형 프로그래밍과 자바의 미래' 여섯가지 내용으로 구성되었습니다. 

 

천천히 업로드 하면서 올릴 예정입니다.

아마 많은 SI 회사들은 아직 자바 8을 많이 사용한다고 알고있습니다.

대부분의 서비스 회사들은 자바 11과 스프링부트를 사용한다고 들었습니다(?? 맞는 정보인거죠?)

 

저는 금융권 관련해서 솔루션회사에서 업무를 진행하고 있으며 자바 8을 사용중에 있습니다.

저 같은 아직 신입은 이 책이 더더욱 중요하게 다가왔습니다. 혹시나 내가 이 책으로 인해서 회사에서 저의 코드가 조금 더 가치가 생길 수 있다 라는 희망을 품으면서 이 책을 읽고 있습니다.

 

 

728x90

+ Recent posts