🔀 Java의 정렬 방법 총정리

- Comparable, Comparator부터 Collections.sort, Stream.sorted까지


✅ 자바에서 정렬이 필요한 이유

데이터를 다루는 대부분의 프로그램에서는 정렬이 핵심 작업입니다.
Java에서는 다양한 방식으로 정렬을 지원하며,
기본 타입부터 사용자 정의 객체까지 정렬이 가능합니다.


📚 자바의 대표적인 정렬 방법

정렬 방법 설명
Arrays.sort() 배열 정렬
Collections.sort() 리스트 정렬
List.sort() 자바 8 이후 리스트 정렬 메서드
Stream.sorted() 스트림에서 정렬
PriorityQueue 자동 정렬 큐
TreeSet, TreeMap 정렬이 보장되는 컬렉션

 

🧩 1. 기본 정렬: Arrays.sort() & Collections.sort()

📌 배열 정렬

int[] arr = {5, 2, 4, 1};
Arrays.sort(arr); // 오름차순 정렬
System.out.println(Arrays.toString(arr)); // [1, 2, 4, 5]

📌 리스트 정렬

List<String> names = Arrays.asList("Charlie", "Alice", "Bob");
Collections.sort(names); // 오름차순
System.out.println(names); // [Alice, Bob, Charlie]

 


🧠 2. 사용자 정의 정렬: Comparable & Comparator

📍 Comparable (내부 정렬 기준 정의)

class Person implements Comparable<Person> {
    String name;
    int age;

    public int compareTo(Person o) {
        return this.age - o.age; // 나이 오름차순
    }
}
  • Collections.sort() 또는 Arrays.sort()에서 자동 사용됨

📍 Comparator (외부 정렬 기준 지정)

List<Person> list = ...
list.sort(Comparator.comparingInt(p -> p.age));

또는

list.sort((p1, p2) -> p2.age - p1.age); // 나이 내림차순

✔ 정렬 기준을 유연하게 바꾸고 싶다면 Comparator 사용!


⚙️ 3. Java 8+ 스타일 정렬

📍 List.sort()

list.sort(Comparator.comparing(Person::getName));

📍 Stream.sorted()

List<Person> result = list.stream()
    .sorted(Comparator.comparing(Person::getAge))
    .collect(Collectors.toList());

🔎 스트림 안에서도 정렬 가능 — 특히 필터, 맵핑, 정렬 후 수집하는 경우 유용


 

🔁 4. 정렬이 유지되는 자료구조

자료구조 특징
PriorityQueue 자동 정렬 큐 (min-heap 구조)
TreeSet 자동 정렬된 Set
TreeMap 자동 정렬된 Map
Set<Integer> set = new TreeSet<>();
set.add(3); set.add(1); set.add(2);
System.out.println(set); // [1, 2, 3]
 

🧪 실전 예제: 객체 리스트 정렬

class Student {
    String name;
    int score;
}

List<Student> students = ...

// 점수 기준 오름차순
students.sort(Comparator.comparingInt(s -> s.score));

// 점수 기준 내림차순 + 이름 기준 오름차순
students.sort(Comparator.comparingInt(Student::getScore).reversed()
                        .thenComparing(Student::getName));

 

✅ 마무리 요약

정렬 도구 용도
Arrays.sort() 배열 정렬
Collections.sort() 리스트 정렬
Comparator, Comparable 사용자 정의 정렬 기준
List.sort(), Stream.sorted() 자바 8 이후의 정렬 방식
PriorityQueue, TreeSet 자동 정렬 유지되는 컬렉션

 

자바는 정렬을 위한 다양한 도구와 전략을 제공합니다.
목적에 맞게 정렬 기준을 정의하고,
성능과 가독성 모두를 챙긴 코드를 작성해보세요! 😊

⏫ Java PriorityQueue 완벽 정리

- 우선순위에 따라 자동 정렬되는 큐


 

✅ PriorityQueue란?

PriorityQueue는 Java Collection Framework에서 제공하는 큐(Queue)의 일종으로,
요소들이 우선순위(priority)에 따라 자동으로 정렬되는 자료구조입니다.

❗ 삽입 순서대로 처리되는 일반 Queue와 달리,
항상 가장 우선순위가 높은 요소가 먼저 꺼내집니다.


 

📦 어떤 구조로 되어 있을까?

내부적으로는 최소 힙(min-heap) 자료구조를 기반으로 동작합니다.

  • 기본 정렬: 오름차순 (작은 값이 먼저)
  • 직접 Comparator를 지정하면 내림차순 등 원하는 정렬 방식 가능

📚 기본 사용법

PriorityQueue<Integer> pq = new PriorityQueue<>();

pq.offer(5);
pq.offer(1);
pq.offer(3);

System.out.println(pq.poll()); // 출력: 1 (가장 작은 값부터 꺼냄)
System.out.println(pq.poll()); // 출력: 3
System.out.println(pq.poll()); // 출력: 5

 


🔁 주요 메서드

메서드 설명
offer(E e) 요소 추가 (add와 동일)
poll() 가장 우선순위 높은 요소 제거 + 반환
peek() 가장 우선순위 높은 요소 확인 (제거 X)
isEmpty() 큐가 비어있는지 확인
size() 요소 개수 반환

 

🔧 사용자 정의 정렬 (내림차순 등)

// 내림차순 우선순위 (최대 힙처럼 사용)
PriorityQueue<Integer> maxPQ = new PriorityQueue<>(Comparator.reverseOrder());

maxPQ.offer(10);
maxPQ.offer(3);
maxPQ.offer(7);

System.out.println(maxPQ.poll()); // 출력: 10

 


👥 객체 정렬 (Custom Class)

class Task {
    String name;
    int priority;

    public Task(String name, int priority) {
        this.name = name;
        this.priority = priority;
    }

    public String toString() {
        return name + "(" + priority + ")";
    }
}

// 우선순위가 낮은 숫자가 먼저 (priority 기준 오름차순)
PriorityQueue<Task> taskQueue = new PriorityQueue<>(Comparator.comparingInt(t -> t.priority));

taskQueue.offer(new Task("Fix Bug", 1));
taskQueue.offer(new Task("Implement Feature", 5));
taskQueue.offer(new Task("Write Tests", 3));

System.out.println(taskQueue.poll()); // Fix Bug(1)

 

🧠 정렬 기준 요약

생성 방식 정렬 방향
new PriorityQueue<>() 기본: 오름차순 (작은 값 우선)
new PriorityQueue<>(Comparator.reverseOrder()) 내림차순 (큰 값 우선)
new PriorityQueue<>(customComparator) 사용자 지정 기준

 

⚠️ 주의할 점

  1. Null 요소는 넣을 수 없음
    • NullPointerException 발생
  2. 정렬된 상태로 꺼낼 뿐, 내부 순서는 보장되지 않음
    • toString() 등으로 출력하면 정렬된 것처럼 안 보일 수 있음
  3. 중간 요소 접근 불가
    • 인덱스로 직접 접근 불가능 (pq.get(1) 이런 건 없음)
  4. 삭제 시에는 반드시 poll() 사용
    • 우선순위 가장 높은 요소부터 삭제됨

⏱ 시간 복잡도

연산 시간 복잡도
삽입 (offer) O(log n)
삭제 (poll) O(log n)
조회 (peek) O(1)
 

✔ 우선순위가 중요할 때 PriorityQueue는 매우 빠르고 효율적인 선택입니다.


📌 언제 사용할까?

  • 최단 경로 알고리즘 (ex: 다익스트라 알고리즘)
  • K번째로 큰 값, 작은 값 찾기
  • 실시간 작업 스케줄링
  • 응급도, 우선도에 따라 순서 처리하는 시스템
  • 최소 또는 최대 우선 요소를 반복적으로 추출할 때

✅ 마무리 정리

항목 설명
구조 최소 힙 (Heap) 기반
정렬 기준 기본 오름차순, Comparator로 변경 가능
삽입/삭제 성능 O(log n)
장점 자동 정렬 + 높은 우선순위부터 빠르게 처리 가능
단점 인덱스로 접근 불가, 내부 순서 보장 안 됨

 

PriorityQueue는 정렬된 큐가 필요한 다양한 상황에서
성능과 편의성을 모두 만족시키는 강력한 도구입니다.
특히 알고리즘 문제 풀이나 대기 순서 처리 로직에서 자주 활용됩니다! 💡

Collections.binarySearch()와 Arrays.binarySearch()는 둘 다 이진 탐색을 수행하는 유틸리티 메서드이지만,
다음과 같은 차이점이 있다.


🔹 1. 패키지와 사용 대상의 차이

항목 Collections.binarySearch() Arrays.binarySearch()
위치 java.util.Collections java.util.Arrays
대상 List (예: ArrayList) 배열 (예: int[], String[])
제네릭 지원 있음 있음 (참조형 배열), 기본형은 오버로드
정렬 필요 여부 정렬된 List 정렬된 배열

🔹 2. 사용 예시

✅ Collections.binarySearch()

List<String> list = Arrays.asList("apple", "banana", "cherry");
Collections.sort(list); // 정렬 필요
int index = Collections.binarySearch(list, "banana");
System.out.println(index); // 1

✅ Arrays.binarySearch()

int[] arr = {1, 3, 5, 7, 9};
int index = Arrays.binarySearch(arr, 5);
System.out.println(index); // 2

 

🔹 3. Comparator 사용 가능 여부

둘 다 Comparator를 인자로 받는 오버로드 버전을 제공하지만, 적용 방식이 조금 다르다.

  • Collections.binarySearch(List<T> list, T key, Comparator<? super T> c)
  • Arrays.binarySearch(T[] a, T key, Comparator<? super T> c)
    → 단, T[ ]처럼 참조형 배열만 가능 (int[], double[]에는 불가능)

🔹 4. 기본형 배열 지원

  • Arrays.binarySearch()는 int[], double[] 등 기본형 배열에 대해서도 오버로드 제공
  • Collections.binarySearch()는 List만 대상이므로, 기본형 배열은 사용 불가

✅ 요약

구분 Collections.binarySearch() Arrays.binarySearch()
대상 List<T> T[], int[], double[] 등
위치 java.util.Collections java.util.Arrays
정렬 반드시 정렬되어 있어야 함 반드시 정렬되어 있어야 함
기본형 지원
Comparator 지원 ✅ (T[ ]만)
 

'Web Programming Language > JAVA' 카테고리의 다른 글

자바의 정렬 방법들  (0) 2025.07.16
자바 PriorityQueue  (0) 2025.07.16
자바 Collections.binarySearch()  (0) 2025.07.16
default method  (0) 2025.05.18
필드(Field)와 프로퍼티(Property)의 차이  (1) 2025.04.22

🔍 자바 Collections.binarySearch()
- 정렬된 리스트에서 빠르게 원하는 값을 찾는 방법


✅ Collections.binarySearch()란?

Java의 Collections 클래스는 다양한 컬렉션 관련 유틸리티 메서드를 제공합니다.
그 중 binarySearch()는 정렬된 리스트에서 이진 탐색(binary search)을 통해 지정된 값의 위치(index)를 빠르게 찾는 메서드입니다.

📌 즉, O(log n)의 속도로 리스트에서 특정 값을 검색할 수 있습니다.


📚 기본 문법

public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key)

또는, 사용자 정의 비교기(comparator)를 사용할 수도 있습니다:

public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c)

🛠 사용 예제

예제 1: 정수 리스트에서 값 찾기

List<Integer> numbers = Arrays.asList(10, 20, 30, 40, 50);
int index = Collections.binarySearch(numbers, 30);
System.out.println(index); // 출력: 2

✔ 리스트는 오름차순으로 정렬되어 있어야 합니다.
30은 인덱스 2 위치에 있으므로, 결과는 2입니다.


예제 2: 존재하지 않는 값

int result = Collections.binarySearch(numbers, 35);
System.out.println(result); // 출력: -4

✔ -4는 다음을 의미합니다:

  • 삽입 위치 = 3
  • 반환값 = -(삽입 위치) -1 = -4

🔎 즉, 값이 없을 때는 삽입 가능한 위치를 음수로 반환합니다.


예제 3: 사용자 정의 객체

class Person {
    String name;
    int age;

    // 생성자, getter 등 생략
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String toString() {
        return name + "(" + age + ")";
    }
}
List<Person> people = new ArrayList<>();
people.add(new Person("Alice", 20));
people.add(new Person("Bob", 30));
people.add(new Person("Charlie", 40));

// 나이 기준 정렬
people.sort(Comparator.comparingInt(p -> p.age));

// 바이너리 서치 (Comparator 필요!)
int index = Collections.binarySearch(
    people,
    new Person("Temp", 30),
    Comparator.comparingInt(p -> p.age)
);

System.out.println("Index: " + index); // Index: 1

⚠️ 주의사항

항목 설명
리스트는 반드시 정렬되어 있어야 함 정렬되지 않은 리스트에서 binarySearch를 호출하면 예상치 못한 결과 발생
비교 기준을 일치시켜야 함 정렬 기준과 binarySearch의 Comparator는 반드시 같아야 함
중복값 존재 시 처음 찾은 값의 인덱스를 반환 (보장 X: 중복 중 어느 하나)
정렬 후에만 검색 Collections.sort() → binarySearch() 순서 필수

🔄 존재 여부만 판단하고 싶다면?

int index = Collections.binarySearch(numbers, 40);
if (index >= 0) {
    System.out.println("존재함");
} else {
    System.out.println("존재하지 않음");
}

⏱ 시간 복잡도

  • O(log n) — 이진 탐색의 특성상 매우 빠름
  • 단, 정렬되지 않은 리스트라면 의미 없음 → 반드시 정렬 후 사용!

✅ 마무리 정리

항목 내용
메서드명 Collections.binarySearch()
목적 정렬된 리스트에서 값을 빠르게 찾기
정렬 필수 여부 ✅ 반드시 정렬되어 있어야 함
반환값 인덱스 (존재하지 않으면 -(삽입 위치) - 1)
성능 O(log n) — 빠름
자주 쓰는 경우 검색, 존재 여부 판단, 삽입 위치 탐색 등

 

Collections.binarySearch()는 자바에서 빠르게 정렬된 데이터에서 값을 찾는 가장 간단한 방법입니다
단, 정렬 상태와 비교 기준이 일치해야 정확한 결과가 나온다는 점을 반드시 기억.

 

🧩 Java default method란?

- 인터페이스에 구현을 허용한 이유와 쓰임 정리


✅ default method란?

자바 8부터, 인터페이스 안에 메서드의 기본 구현(body)을 작성할 수 있게 되었고,
이때 사용하는 키워드가 바로 default입니다.

public interface MyInterface {
    default void greet() {
        System.out.println("Hello from default method!");
    }
}

✔ 즉, 인터페이스지만 메서드 구현이 가능해진 겁니다.


🔍 왜 생겼을까? (등장 배경)

자바 8에서는 Stream, Lambda, Functional Interface 등 다양한 API가 추가되었고,
이를 기존 인터페이스에 기능을 추가해야 하는 상황이 생겼습니다.

하지만 기존 방식대로라면…

  • 인터페이스에 메서드를 추가하면 모든 구현체가 컴파일 에러가 납니다!
    → 기존 라이브러리와 하위 호환 깨짐 😥

그래서!

기본 구현이 있는 메서드를 인터페이스에 추가할 수 있도록
default method가 도입되었습니다. (하위 호환 확보!)

 


🛠 사용 예제

public interface Vehicle {
    void start();

    default void honk() {
        System.out.println("빵빵!");
    }
}

public class Car implements Vehicle {
    public void start() {
        System.out.println("자동차 시동 켜짐");
    }
}

public class Main {
    public static void main(String[] args) {
        Vehicle car = new Car();
        car.start(); // 자동차 시동 켜짐
        car.honk();  // 빵빵!
    }
}

🔎 포인트

  • Car 클래스는 honk()를 구현하지 않았지만, Vehicle에서 기본 구현이 제공되므로 바로 사용 가능
  • 기존 인터페이스에 새로운 메서드를 추가해도 구현체는 깨지지 않음

🧠 default method는 override도 가능

public class Truck implements Vehicle {
    public void start() {
        System.out.println("트럭 시동 켜짐");
    }

    @Override
    public void honk() {
        System.out.println("트럭 빵빵!");
    }
}

→ Truck에서는 honk() 메서드를 재정의해서 자신만의 동작을 가질 수 있어요.


⚠️ 주의할 점

항목 설명
다중 상속 충돌 두 인터페이스에 동일한 default 메서드가 있으면 명시적으로 오버라이드 해야 함
상태 유지 불가 인터페이스는 상태(필드)를 가질 수 없기 때문에 default method 내에서는 필드 사용 불가
남용 금지 너무 많은 로직을 default method에 넣으면 인터페이스가 무거워지고, 객체지향 설계 원칙을 해칠 수 있음
 

❗ 다중 상속 충돌 예시

interface A {
    default void hello() { System.out.println("Hello from A"); }
}

interface B {
    default void hello() { System.out.println("Hello from B"); }
}

class C implements A, B {
    @Override
    public void hello() {
        A.super.hello(); // 또는 B.super.hello()
    }
}

 


📌 default method vs abstract method

구분 abstract method default method
구현 여부 ❌ 없음 ✅ 있음
오버라이드 필요 여부 ✅ 반드시 구현해야 함 ❌ 선택적으로 구현 가능
목적 공통 규약 선언 하위 호환 유지 및 공통 로직 제공

 


✅ 실무에서의 활용 팁

  • 자주 반복되는 공통 동작을 미리 구현해줄 때 유용
  • 하위 호환을 보장하며 API를 확장할 때 적합
  • 인터페이스지만 전략 패턴처럼 일부 기본 동작을 제공하고, 필요한 부분만 구현하도록 유도할 수 있음

🔚 마무리 정리

항목 요약
정의 인터페이스에서 메서드 구현을 허용하는 기능 (default 키워드)
목적 하위 호환성과 공통 구현 제공
사용 시 주의 다중 상속 충돌, 상태 불가, 남용 금지
실전 팁 전략 패턴, 유틸성 메서드, 기존 API 확장 등에 적합

 

 

default method는 단순한 문법이 아니라,
자바 인터페이스의 역할을 확장하고 객체지향을 유연하게 만드는 기능입니다.
적절히 활용하면 코드 중복을 줄이고 유지보수성을 높일 수 있습니다.

 

🧩 자바의 필드(Field)와 프로퍼티(Property) 차이 완벽 정리 - getter/setter와 실제 변수, 무엇이 다를까?

 

자바로 객체 지향 프로그래밍을 하다 보면 "필드"와 "프로퍼티"라는 용어를 자주 접하게 됩니다.
두 용어가 비슷하게 사용되지만, 의미와 쓰임은 명확히 다릅니다.
이번 글에서는 이 둘의 개념과 차이를 정확히 정리해보겠습니다.


✅ 필드(Field)란?

필드는 클래스 내부에 정의된 멤버 변수(실제 데이터 저장소)입니다.
객체가 상태(state)를 가지기 위해 보유하는 값이며, 보통 private으로 선언됩니다.

public class Person {
    private String name; // ← 이게 필드!
}
  • name은 클래스 안에 존재하는 실제 데이터 공간
  • 클래스 내부에서 this.name처럼 직접 접근 가능

✅ 프로퍼티(Property)란?

프로퍼티는 객체 외부에서 getter/setter 메서드를 통해 간접적으로 접근하는 속성입니다.
즉, 필드와 연결된 외부 공개 인터페이스라고 볼 수 있습니다.

public class Person {
    private String name;

    public String getName() {
        return name; // ← 이게 name이라는 "프로퍼티"
    }

    public void setName(String name) {
        this.name = name;
    }
}
  • getName()과 setName() 메서드는 name 필드의 프로퍼티 역할
  • 자바빈(JavaBeans) 규약에서는 이 메서드 집합을 "name 프로퍼티"라고 부릅니다.

📊 필드 vs 프로퍼티 비교 정리

구분 필드(Field) 프로퍼티(Property)
정의 클래스 내부에 선언된 멤버 변수 필드에 접근하기 위한 getter/setter 메서드 집합
접근 방식 직접 접근 (this.name) 간접 접근 (getName(), setName())
존재 유무 실제로 메모리에 존재 개념적으로 존재 (인터페이스로서의 접근 수단)
사용 목적 객체의 내부 상태 저장 외부에서 안전하게 상태를 읽거나 수정할 수 있도록 함
예시 private String name; getName(), setName(String name)

⚠️ 헷갈리지 마세요!

❗ "필드 = 변수", "프로퍼티 = 변수 + 접근자(getter/setter)" 개념입니다.
프로퍼티는 "상태" 자체라기보단, 상태를 다루는 접근 수단에 가까워요.


🛠️ 실무에서 왜 중요할까?

📌 예시 1: JPA에서의 접근 전략

JPA에서는 데이터를 읽고 쓸 때 어떻게 접근할지를 선택할 수 있습니다:

  • 필드 접근(Field Access) → 필드에 직접 접근
  • 프로퍼티 접근(Property Access) → getter/setter를 통해 접근
@Access(AccessType.PROPERTY)
@Entity
public class Member {

    private String name;

    @Column(name = "member_name")
    public String getName() {
        return name; // ← 프로퍼티 접근 방식
    }
}

✔ JPA는 이 getName()을 통해 name 필드를 읽고 씁니다.
따라서 어디에 어노테이션을 붙이느냐에 따라 접근 방식이 달라집니다.


📌 예시 2: JSON 직렬화 (Jackson, Gson 등)

Jackson도 기본적으로 getter를 기준으로 직렬화/역직렬화합니다.

public class User {
    private String name;

    public String getName() {
        return name; // ← 이걸 통해 JSON에 포함됨
    }
}

✔ 필드에 직접 접근할 수도 있지만, 설정에 따라 다르기 때문에 "필드냐, 프로퍼티냐"를 명확히 구분해야 합니다.


✅ 마무리 정리

항목 필드 프로퍼티
존재 방식 실제 객체 내부의 변수 getter/setter를 통해 노출되는 속성
객체 내부 사용 this.name 등으로 직접 접근 내부보단 외부에서 접근 시 활용
프레임워크 사용 시 JPA, Jackson 등에서 접근 방식 차이 발생 getter/setter에 따라 매핑 방식 달라짐

👉 필드 = 저장소, 프로퍼티 = 인터페이스
이 구분만 정확히 기억해도 실무에서 큰 도움이 됩니다.

 

 

 

+ Recent posts