[260115] _ 자료구조의 순회, 정렬

호두·2026년 1월 30일

✔️ Java / Spring

목록 보기
13/25

드디어
자바 중급 2 강의를 완강했다....
이제 Spring 으로 넘어간다.
갈 길이 멀구나 ⛰️....😂

예전부터 있었던 강박(?) 인데 복습을 할 때 전부 하나하나 다 내가 말로 설명할 수 있을 정도로 되지 않으면 복습은 제대로 한거 같지 않아서 공부하기 힘들어하는 경우가 많았다.
하지만 이제는 안다. 당연히 한 번 복습했다고 다 기억하면 좋겠지만‼️ 보통은 여러번 복습을 해서 설명할 수 있을 정도로 익혀나간다는 것을.

그러니까 너무 강박을 가지고 무겁게 공부하지 말자.
한 번에 모든 걸 하려고 하지 말자 🐥💛



✅ Iterable 과 Iterator

자료구조의 요소를 순회할 때 사용하는 인터페이스

  • Iterable = Iterator 을 사용할 수 있게 해주는 증표
  • Iterator = 순회할 때 사용하는 메서드를 제공
package collection.iterable;

import java.util.Iterator;

public class MyArrayMain {

    public static void main(String[] args) {
        MyArray myArray = new MyArray(new int[]{1, 2, 3, 4});

        Iterator<Integer> iterator = myArray.iterator();
        System.out.println("iterator 사용");
        while (iterator.hasNext()) {
            Integer value = iterator.next();
            System.out.println("value = " + value);
        }
    }
}

Iterable

순회의 기능을 사용하려는 타입에 구현되는 인터페이스다. 이 인터페이스에는 Iterator 를 사용하는 iterator() 라는 메서드가 정의되어있다. 그래서 이 인터페이스를 순회의 인증표 라고 생각하면 된다.

package collection.iterable;

import java.util.Iterator;

public class MyArray implements Iterable<Integer> {

    private int[] numbers;

    public MyArray(int[] numbers) {
        this.numbers = numbers;
    }

    @Override
    public Iterator<Integer> iterator() {
        return new MyArrayIterator(numbers);
    }
}

자바에서 제공하는 "컬렉션"들은 모두 Iterable 이 구현되어있다. 배열은 JVM에서 특별 취급해서 없이 순회 가능하다.

💡 왜 순회 가능 표식을 따로 둘까?

순회는 여러 개일 때만 의미가 있기 때문이다. 데이터가 하나 있는 값을 순회하는 건 말이 안된다. 그래서 자바는 하나짜리 값은 순회 불가능하게 하고, 여러 개를 담은 것은 이라는 표식으로 Iterable 을 붙여 사용가능하게하는 것이다.


Iterator

순회에 사용되는 메서드들을 가지고 있는 실질적인 반복자 인터페이스다. hasNext(), next() 메서드를 통해서 Iterable 이 구현된 모든 타입을 순회할 수 있다.

대부분은 직접 구현하지 않고 return list.iterator(); 즉, 자바 컬렉션이 제공(List, Set, Map) 하는 걸 사용한다.

  1. 내부 자료구조가 컬렉션이 아닐 경우 ‼️
class Tree {
    Node root;
}
  1. 커스텀 순회가 필요할 경우 ‼️
class CustomOrder implements Iterable<Item> {
    private List<Item> items;
}

보통 이 2가지 경우에만 직접 구현한다.


for - each

향상된 for문은 객체를 순회하는 기능을 가지고 있다. 이 순회 문법을 사용하기 위해서는 배열이거나, Iterable 을 구현한 타입이어야 한다. 그렇지 않으면 컴파일 오류가 발생한다.

내가 만든 타입에 항상 Iterable을 붙이기 ❌
내가 만든 타입이 컬렉션을 가질 때 Iterable 구현하기 ⭕️

📌 순회 방법
1. 컬렉션이 직접 순회
2. 감싼 타입이 대신 순회 ➡️ 내부 컬렉션을 숨기고 for-each 로 순회가능하게 만듬.

내가 만든 타입에 구현

class StudentGroup implements Iterable<Student> {
    private List<Student> students; 
}

(자료구조 안의 타입은 뭐든 상관 없음)


순회 헷갈렸던 예시 🤨

이 메서드를 보면서 왜 매개변수를 저 두 개로 만들어서 받는 걸로 설정했는지, 목적이 뭔지 이해할 수가 없었다.

 private static void printAll(Iterator<Integer> iterator) {
        System.out.println("iterator = " + iterator.getClass());
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }

 private static void foreach(Iterable<Integer> iterable) {
        System.out.println("iterable = " + iterable.getClass());
        for (Integer i : iterable) {
            System.out.println(i);
        }
    }

Iterator ver.

Iterator<Integer> it = list.iterator();
printAll(it);
printAll(it);   // ❌ 두 번째는 안 됨

현재 위치(currentIndex)를 기억하는 객체라서 한번 끝까지 돌면 다시 돌아갈 방법이 없다. Iterator 안에는 '지금 몇 번째에 있는지, 다음에 꺼낼 게 뭔지' 가 들어있다. 끝에 도달하면 it.hasNext() == false 가 된다. 되돌아 가는 기능은 없다.

Iterable ver.

foreach(list);
foreach(list);  // ⭕ 항상 됨

두 인터페이스의 차이점을 보여주기 위해 따로 뽑은 코드다. Iterator 은 소모품이고 Iterable 은 재사용 가능한 원본이다. 실무에서는 특별한 경우가 아닌 이상, Iterable 타입의 printAll() 만 사용하면 된다.

두 개의 인터페이스로 기능하는 이유

'순회할 수 있는 대상'과 '지금 순회중인 상태'는 역할이 완전 다르기 때문에 인터페이스를 둘로 나눴다. 하나로 만들면 재사용이 어려워지고 파일/네트워크/스크림 같은 데이터에 사용이 불가능하다.



✅ Comporable 과 Comporator

자료구조의 데이터를 정렬할 때 사용하는 인터페이스다. 순서가 있는 자료구조에서 사용이 가능하다. List, Map 처럼 순서가 있으면 사용가능 하지만 Set 처럼 순서가 유효하지 않은 경우에는 사용할 수 없다.

Comporable

'정렬을 할 수 있는 상태' 를 표시하는 역할을 하는 인터페이스다.

package collection.compare;

public class MyUser implements Comparable<MyUser> {

    private String id;
    private int age;

    public MyUser(String id, int age) {
        this.id = id;
        this.age = age;
    }

    public String getId() {
        return id;
    }

    public int getAge() {
        return age;
    }

    @Override
    public int compareTo(MyUser o) {
        return this.age < o.age ? -1 : (this.age == o.age ? 0 : 1);
    }

    @Override
    public String toString() {
        return "MyUser{" +
                "id='" + id + '\'' +
                ", age=" + age +
                '}';
    }
}

➡️ compareTo() : 객체 안의 정렬 기준 메서드로 고정된 기준을 객체가 직접 가지고 있다.

🚨 순회는 표준방식이 다 갖춰져 있어서 어떤 객체든 이 방식으로만 하면 순회가 가능하도록 표준화 했지만, 정렬은 "이 객체는 정렬 가능하다" 라는 공식적인 표식 인터페이스와 문법을 만들기에 그 규칙이 너무 다양해 표준화가 어렵다. 그래서 방식을 사용자가 정하고 그에 대한 도구만 제공한다.

Collections.sort(list, comparator);

⬆️ 이렇게 함수만 제공


Comporator

사용자가 정하는 정렬 방식을 생성하는 인터페이스다.

package collection.compare;

import java.util.Comparator;

public class IdComparator implements Comparator<MyUser> {

    @Override
    public int compare(MyUser o1, MyUser o2) {
        return o1.getId().compareTo(o2.getId());
    }

}

➡️ compare() : 임시 기준을 외부에서 제공하는 메서드다.

  • 항상 하나로 고정 : compareTo()
  • 상황마다 바뀌면 : compare()
    (✔️ 실무에서는 Comparator + compare() 가 훨씬 유연함)

정렬 예시 🔍

package collection.compare;

import java.util.LinkedList;
import java.util.List;

public class SortMain4 {

    public static void main(String[] args) {
        MyUser myUser1 = new MyUser("a", 30);
        MyUser myUser2 = new MyUser("b", 20);
        MyUser myUser3 = new MyUser("c", 10);

        List<MyUser> list = new LinkedList<>();
        list.add(myUser1);
        list.add(myUser2);
        list.add(myUser3);
        System.out.println("기본 데이터");
        System.out.println(list);

        System.out.println("Comparable 기본 정렬");
        list.sort(null);
        // Collections.sort(list);
        System.out.println(list);

        System.out.println("IdComparator 정렬");
        list.sort(new IdComparator());
        // Collections.sort(list,new IdComparator());
        System.out.println(list);
    }
}

➡️ sort() : 순서가 있는 컬렉션을 정렬해주는 메서드다.

*list.sort(null) ➡️ 기본 정렬을 사용하려면 null을 넣으면 된다.

컬렉션 프레임워크

0개의 댓글