Blog
Anki
Java

PriorityQueue, Comparator를 사용하는 방법

    1. 아래 코드의 ? 부분에 Comparator를 사용한 PriorityQueue 선언 방법을 적어주세요.
public class Solution {
    public static void main(String[] args) {
        // 2번째 값을 기준으로 정렬해야 함
        int[][] map = new int[][]{{1, 2}, {2, 1}, {3, 2}, {2, 4}};
        PriorityQueue<int[]> pq = ?;
        for (int[] m : map) {
            pq.add(m);
        }
    }
}
    1. 아래 코드의 ? 부분에 Comparator를 사용한 PriorityQueue 선언 방법을 적어주세요.
public class Solution {
    private static class Node {
        int from, to, cost;
 
        public Node(int from, int to, int cost) {
            this.from = from;
            this.to = to;
            this.cost = cost;
        }
    }
 
    public static void main(String[] args) {
        PriorityQueue<Node> pq = ? ;
    }
}
답 보기
    1. 아래 코드의 ? 부분에 Comparator를 사용한 PriorityQueue 선언 방법을 적어주세요.
public class Solution {
    public static void main(String[] args) {
        // 2번째 값을 기준으로 정렬해야 함
        int[][] map = new int[][]{{1, 2}, {2, 1}, {3, 2}, {2, 4}};
        // comparingDouble, comparingLong 도 존재
        PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(o -> o[1]));
        for (int[] m : map) {
            pq.add(m);
        }
    }
}
    1. 아래 코드의 ? 부분에 Comparator를 사용한 PriorityQueue 선언 방법을 적어주세요.
public class Solution {
    private static class Node {
        int from, to, cost;
 
        public Node(int from, int to, int cost) {
            this.from = from;
            this.to = to;
            this.cost = cost;
        }
    }
 
    public static void main(String[] args) {
        PriorityQueue<Node> pq = new PriorityQueue<>(Comparator.comparing(node -> node.cost));
    }
}

Java는 CallByValue인가요, CallByReference인가요?

답 보기

Java는 항상 CallByValue로 동작.
Reference Type (String, Array, List, Object 등)의 경우 객체의 참조값(주소값)을 복사해서 전달 → 하지만 이건 "주소값을 복사한 것"이지, call by reference가 아님!
즉, 메서드 내에서 객체의 필드를 변경하면 원본 객체에도 영향이 있지만, 참조 자체를 변경해도 원본엔 영향이 없음
(이게 call by reference가 아닌 이유)

  • 👇 아래 코드 changeName 함수와 changeName2 함수의 차이를 보면 이해가 쉬움
class User {
    Long id;
    String name;
 
    public User(
        final Long id,
        final String name
    ) {
        this.id = id;
        this.name = name;
    }
}
 
public class Main {
    public static void main(String[] args) {
        User user = new User(1L, "홍길동");
        changeName(user, "장길동");
        System.out.println(user.name); // 홍길동
        changeName2(user, "장길동");
        System.out.println(user.name); // 장길동
    }
 
    public static void changeName(User user, String name) {
        System.out.println("원래 이름 : " + user.name); // 홍길동
        user = new User(user.id, name);
        System.out.println("바뀐 이름 : " + user.name); // 장길동
    }
    public static void changeName2(User user, String name) {
        System.out.println("원래 이름 : " + user.name); // 홍길동
        user.name = name;
        System.out.println("바뀐 이름 : " + user.name); // 장길동
    }
}
dp[i][j]={1if i=0 or j=0 (배열의 가장자리)min(dp[i1][j],dp[i][j1],dp[i1][j1])+1if matrix[i][j]=10if matrix[i][j]=0dp[i][j] = \begin{cases} 1 & \text{if } i = 0 \text{ or } j = 0 \text{ (배열의 가장자리)} \\ \min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1 & \text{if } matrix[i][j] = 1 \\ 0 & \text{if } matrix[i][j] = 0 \end{cases}

AOP 프레임워크에 대해서 설명해주세요.

꼬리 질문
  • Spring AOP와 AspectJ의 차이점은 무엇인가요?
  • JDK Dynamic Proxy와 CGLib Proxy의 차이점은 무엇인가요?
  • Runtime Weaving과 Compile-time Weaving의 장단점은?
  • Spring AOP에서 프록시 생성 방식은 어떻게 결정되나요?
  • AspectJ를 사용하는 이유는 무엇인가요?
답변 보기
  • AOP는 횡단 관심사(Cross-cutting Concerns)를 분리하여 코드의 모듈성을 높이는 프로그래밍 패러다임입니다 📋
  • 주요 프레임워크로는 Spring AOP와 AspectJ가 있으며, 각각 다른 접근 방식을 사용합니다

Spring AOP 특징

  • Spring IoC 컨테이너와 통합된 간단한 AOP 구현체 🌱
  • Spring Bean에서만 동작 (컨테이너 관리 객체만 적용 가능)
  • 프록시 기반 Runtime Weaving 방식 사용
  • JDK Dynamic Proxy 또는 CGLib Proxy로 구현
  • 런타임 오버헤드 존재 (프록시 객체 생성 및 메서드 호출 비용)

AspectJ 특징

  • 완전한 AOP 솔루션을 제공하는 독립적인 프레임워크 ⚡

  • 모든 자바 객체에 적용 가능 (Spring Bean이 아니어도 됨)

  • Compile-time, Load-time, Runtime Weaving 모두 지원

  • 컴파일 시점에 바이트코드에 직접 삽입되어 런타임 성능 영향 없음

  • 설정과 학습이 복잡하지만 더 강력한 기능 제공

  • 꼬리질문: Spring AOP와 AspectJ의 차이점은 무엇인가요?

    • 적용 범위: Spring AOP는 Spring Bean만, AspectJ는 모든 자바 객체
    • 위빙 시점: Spring AOP는 런타임, AspectJ는 컴파일타임/로드타임
    • 성능: Spring AOP는 프록시 오버헤드, AspectJ는 직접 바이트코드 삽입으로 빠름
    • 복잡도: Spring AOP는 간단하고 직관적, AspectJ는 복잡하지만 강력
    • 기능: AspectJ가 더 다양한 JoinPoint와 고급 기능 제공
  • 꼬리질문: JDK Dynamic Proxy와 CGLib Proxy의 차이점은 무엇인가요?

    • JDK Dynamic Proxy 🎭
      • 인터페이스 기반 프록시 생성 (인터페이스 구현 클래스만 가능)
      • 자바 표준 라이브러리 사용 (별도 의존성 불필요)
      • 리플렉션 기반으로 상대적으로 느림
      • 메모리 사용량 적음
    • CGLib Proxy 🔧
      • 클래스 기반 프록시 생성 (상속을 통한 구현)
      • 서드파티 라이브러리 필요 (spring-core에 포함)
      • 바이트코드 조작으로 상대적으로 빠름
      • final 클래스나 메서드는 프록시 불가
  • 꼬리질문: Runtime Weaving과 Compile-time Weaving의 장단점은?

    • Runtime Weaving (Spring AOP)
      • 장점: 설정 간단, 동적 변경 가능, IDE 디버깅 용이
      • 단점: 성능 오버헤드, 프록시 제약사항 존재
    • Compile-time Weaving (AspectJ) ⚙️
      • 장점: 런타임 성능 우수, 모든 JoinPoint 지원, 강력한 기능
      • 단점: 컴파일 과정 복잡, 디버깅 어려움, 유연성 부족
  • 꼬리질문: Spring AOP에서 프록시 생성 방식은 어떻게 결정되나요?

    • 대상 클래스가 인터페이스를 구현하면 → JDK Dynamic Proxy 사용 🎯
    • 대상 클래스가 인터페이스를 구현하지 않으면 → CGLib Proxy 사용
    • @EnableAspectJAutoProxy(proxyTargetClass = true) 설정으로 강제로 CGLib 사용 가능
    • Spring Boot 2.0부터는 기본적으로 CGLib 사용
  • 꼬리질문: AspectJ를 사용하는 이유는 무엇인가요?

    • 성능 최적화: 컴파일타임 위빙으로 런타임 오버헤드 제거 🚀
    • 완전한 AOP: 필드 접근, 생성자 호출 등 모든 JoinPoint 지원
    • Spring 독립성: Spring 없이도 순수 자바 애플리케이션에서 사용 가능
    • 고급 기능: declare parents, declare soft 등 고급 AOP 기능 제공