본문 바로가기
JAVA/이재환의 자바 프로그래밍 입문

[Java] Ch.21 컬렉션 프레임워크

by ♡˖GYURI˖♡ 2023. 10. 25.
728x90

자료구조

대량의 데이터를 효율적으로 관리하는 매커니즘

 

 

배열

배열은 크기가 고정되어 있어 데이터를 추가하거나 삭제할 수 없음

 

 

리스트

리스트는 원소가 원소를 가리켜서 관리하는 자료구조

데이터가 추가되거나 삭제될 때 연결하는 정보만 바꾸면 쉽게 추가, 삭제 가능

 

 

스택

스택은 한 쪽 끝에서만 자료를 넣거나 뺄 수 있는 선형 구조(Last In First Out; LIFO)로 되어 있음

자료를 넣는 것을 '밀어넣는다'하여 푸시(push)라 하고, 반대로 넣어둔 자료를 꺼내는 것을 팝(pop)이라 하는데, 이 때 가장 최근에 푸시한 자료부터 나오게 됨

 

 

큐는 먼저 집어넣은 데이터가 먼저 나오는 FIFO(First In First Out) 구조로 저장하는 자료구조

 

 

트리구조

부모 노드 밑에 여러 자식 노드가 연결되고, 자식 노드 각각에 다시 자식 노드가 연결되는 형태의 자료구조

자식 노드에서 부모 쪽으로 계속해서 타고 올라가다 보면 결국 부모가 없는 하나의 노드로 이어지게 되는데, 이 노드를 루트 노드(root node)라고 부르며, 루트 노드를 중심으로 뻗어나가는 모습이 나무 구조와 비슷함

 

 

컬렉션 프레임워크의 구조

자바에서는 앞에서 살펴 본 자료구조를 개발자가 편리하게 사용할 수 있도록 컬렉션 프레임워크를 제공함

컬렉션 프레임워크에서 제공하는 인터페이스들은 다음과 같은 상속 관계를 가지고 있음

 

▼컬렉션 프레임워크에 속하는 인터페이스

인터페이스 설명 구현 클래스
List<E> 순서가 있는 데이터 집합
추가된 데이터의 순서도 유지되며, 데이터 중복도 허용
ArrayList, LinkedList, Vector, Stack
Set<E> 중복된 데이터가 제거되는 등 추가된 데이터의 순서가 유지되지 않는 데이터 집합
데이터 중복 허용 X
HashSet, TreeSet
Map<K, V> 키와 값으로 이루어진 데이터들의 집합
키는 중복 허용 X, 값은 중복 허용
ex) 출석부에 1번 홍길동과 2번 홍길동(동명이인)이 있는 경우
HashMap, TreeMap, Hashtable, Properties
Queue<E> 순서가 있는 데이터 집합
추가된 데이터의 순서도 유지되며, 데이터 중복 허용
LinkedList

Vector, Stack, Hashtable, Properties와 같은 클래스들은 컬렉션 프레임워크가 만들어지기 전부터 존재하던 것이기 때문에 기존 코드와의 호환을 위해서 남겨져 있을 뿐 현재는 사용하지 않음

 

 

List<E> 인터페이스를 구현하는 컬렉션 클래스들

List<E> 인터페이스를 구현하는 대표적인 컬렉션 클래스

  • ArrayList<E> : 배열 기반 자료구조, 배열을 이용해 객체 저장
  • LinkedList<E> : 연결 기반 자료구조, 리스트를 구성하여 객체 저장

(List는 데이터가 여러 개 쭉 있는 것을 표현)

 

List<E> 인터페이스를 구현하는 컬렉션 클래스들의 공통점

  • 데이터의 저장 순서 유지
  • 동일 데이터 중복 저장 허용

 

ArrayList 사용하기

import java.util.ArrayList;
import java.util.List;

public class Ex01_ArrayList
{
    public static void main(String[] args)
    {
        List<String> list = new ArrayList<>();
        
        // 객체 저장 : 순서 있슴. 중복 허용.
        list.add("orange");
        list.add("apple");
        list.add("apple");
        list.add("banana");
    
        // 객체 참조
        for(int i = 0; i < list.size(); i++)
            System.out.print(list.get(i) + '\t');
        System.out.println();
        
        // 첫 번째 객체 삭제
        list.remove(0);
   
        // 삭제 후 객체 참조
        for(int i = 0; i < list.size(); i++)
            System.out.print(list.get(i) + '\t');
        System.out.println();
    }
}

 

LinkedList 사용하기

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

public class Ex02_LinkedList
{
    public static void main(String[] args)
    {
        List<String> list = new LinkedList<>();
        
        // 객체 저장 : 순서 있슴. 중복 허용.
        list.add("orange");
        list.add("apple");
        list.add("apple");
        list.add("banana");
    
        // 객체 참조
        for(int i = 0; i < list.size(); i++)
            System.out.print(list.get(i) + '\t');
        System.out.println();
        
        // 첫 번째 객체 삭제
        list.remove(0);
   
        // 삭제 후 객체 참조
        for(int i = 0; i < list.size(); i++)
            System.out.print(list.get(i) + '\t');
        System.out.println();
    }
}

 

ArrayList vs. LinkedList

  단점 장점
ArrayList<E> 객체가 추가될 때 저장 공간을 늘리는 과정에서 시간이 비교적 많이 소요됨
객체의 삭제 과정에서 많은 연산이 필요할 수 있어 느릴 수 있음
저장된 객체의 참조가 LinkedList보다 빠름
LinkedList<E> 저장된 객체의 참조 과정이 배열에 비해 복잡함 저장 공간을 늘리는 과정이 간단함

 

ArrayList<E>에서 데이터 추가

배열은 크기를 변경할 수 없으므로 새로 배열을 만들고 옮겨야 함

 

LinkedList<E>에서 데이터 추가

배열과 비교하여 리스트에서 데이터의 추가는 다음과 같이 연결 정보만 만들어주면 됨

 

Iterator 사용하기

Iterable 인터페이스를 구현했기 때문에 저장된 인스턴스의 순차적 접근에 향상된 기능의 for문이나 Iteraor 반복자 이용 가능

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

public class Ex03_IteratorUse
{
    public static void main(String[] args)
    {
        List<String> list = new LinkedList<>();
        
        // 객체 저장 : 순서 있슴. 중복 허용.
        list.add("orange");
        list.add("apple");
        list.add("apple");
        list.add("banana");
    
        // 향상된 for 문으로 객체 참조
        for(String s : list)
            System.out.print(s + '\t');
        System.out.println();

        // 반복자 획득 -> 제네릭으로 자료형 지정
        Iterator<String> itr = list.iterator();
        
        // 반복자를 이용한 순차적 참조
        String str;
        while(itr.hasNext()) 
        {
            str = itr.next();
            System.out.print(str + '\t');
            
            if(str.equals("orange"))
                itr.remove();
        }
        System.out.println();
    
        // 반복자 다시 획득
        itr = list.iterator();
        
        // 삭제 후 결과 확인
        while(itr.hasNext())
            System.out.print(itr.next() + '\t');

        System.out.println();
    }
}

한 번 사용한 반복자는 다시 사용할 수 없기에 다시 반복자를 구함

 

리스트 형식 바꾸기

이 리스트는 배열처럼 선언과 동시에 초기화 불가

그러나 Arrays 클래스의 유틸 메서드를 사용해서 다음과 같이 사용할 수 있음

List<String> list = Arrays.asList("홍길동", "전우치", "손오공", "전우치");

 

인수로 전달된 객체들을 저장한 컬렉션 객체를 생성 및 반환

이렇게 생성된 리스트 객체는 객체에 요소를 추가하거나 삭제할 수 없는 객체

 

수정을 하기 위해서는 다음과 같이 다시 생성해야 함

List<String> list = Arrays.asList("홍길동", "전우치", "손오공", "전우치");
list = new ArrayList<>(list);

 

만들 때 데이터 성격에 따라 ArrayList나 LinkedList를 선택하고 사용은 그냥 List로 한다고 하였기에, 사용 중에도 다음과 같이 성격을 바꾸 수 있음

List<String> list = Arrays.asList("홍길동", "전우치", "손오공", "전우치");
list = new ArrayList<>(list);
list = new LinkedList<>(list);

 

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class Ex04_Convert
{
    public static void main(String[] args)
    {
        // 매개변수로 전달된 객체들을 저장한 컬렉션 객체의 생성 및 반환
        // 이렇게 생성된 리스트 객체는 요소를 추가하거나 삭제할 수 없는 객체임.
        List<String> list = Arrays.asList("홍길동", "전우치", "손오공", "전우치");
//      list.add("멀린");                // 실행하면 에러남
        list = new ArrayList<>(list);    // mutable 객체로 변환
        list.add("해리포터");            // 에러 안남

        // ArrayList<E> 객체의 순환
        for(Iterator<String> itr = list.iterator(); itr.hasNext(); )
            System.out.print(itr.next() + '\t');
        System.out.println();

        // ArrayList<E>를 LinkedList<E>로 변환
        list = new LinkedList<>(list);

        // LinkedList<E> 객체의 순환
        for(String s : list)
            System.out.print(s + '\t');
        System.out.println();
    }
}

 

컬렉션 프레임워크에 기본 자료형을 데이터로 사용하기

컬렉션 프레임워크는 제네릭을 사용하여 자료형을 제한함

이 때 제네릭 부분에 클래스 타입을 지정해주어야 함, 기본 자료형을 직접 적어주는 것은 불가

List<Integer> list = new LinkedList<>();// X
List<int> list = new LinkedList<>();	// O

 

앞서 배웠던 래퍼 클래스들은 오토 박싱과 오토 언박싱이 되기 때문에 자료형만 래퍼 클래스로 적어줄 뿐 기본 자료형을 사용하는 데 제약사항은 없음

 

import java.util.Iterator;
import java.util.LinkedList;

public class Ex05_PrimitiveData
{
    public static void main(String[] args)
    {
        LinkedList<Integer> list = new LinkedList<>();

        // 저장 과정에서 오토 박싱
        list.add(10);    
        list.add(20);        
        list.add(30);    
    
        for(Iterator<Integer> itr = list.iterator(); itr.hasNext(); ) {
            int n = itr.next();    // 오토 언박싱
            System.out.println(n);
        }
    }
}

  • 데이터를 추가할 때 오토 박싱 : int → Integer
  • 데이터를 꺼내올 때 오토 언박싱 : Integer → int

 

 

Set<E> 인터페이스를 구현하는 컬렉션 클래스들

Set<E> 인터페이스를 구현하는 컬렉션 클래스들의 공통점

  • 저장 순서 유지  X
  • 데이터 중복 저장 허용 X

 

HashSet 사용하기

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class Ex06_Set
{
    public static void main(String[] args) 
    {
        Set<String> set = new HashSet<>();
        set.add("orange");
        set.add("apple");
        set.add("banana");
        set.add("apple");

        System.out.println("객체 수: " + set.size());
		
        // 반복자를 이용한 전체 출력
        for(Iterator<String> itr = set.iterator(); itr.hasNext(); )
            System.out.print(itr.next() + '\t');
        System.out.println();

        // 향상된 for 문을 이용한 전체 출력
        for(String s : set)
            System.out.print(s + '\t');
        System.out.println();
    }
}

 

hash와 hashCode() 메서드

Set<E> 인터페이스를 구현하는 컬렉션 클래스들은 데이터 중복 저장을 허용하지 않음

중복 저장을 방지하려면 데이터가 입력되기 전 이미 있는 데이터인지를 검색해보아야 함

이 때 사용하는 알고리즘이 해시(hash)

 

해시는 정보를 저장하거나 검색할 때 사용하는 알고리즘

이렇게 분류해놓으면 탐색 속도가 매우 빨라짐

 

Object 클래스의 hashCode 메서드는 이렇듯 객체들을 분류하는 역할을 함

그래서 HashSet에서 중복 저장을 막으려면 hashCode() 메서드에서 반환하는 해시 코드값에 해당하는 내부 목록을 찾음

그러면 비교해야 할 탐색의 대상이 확 줄어듬

그리고 선택된 대상 중에서 equals 메서드를 호출하여 내용이 같은지 비교하게 됨

 

HashSet의 중복 비교

hashCode() 메서드는 직접 만들수도 있지만, 자바에서 제공하는 메서드를 이용하면 쉽게 만들 수 있음

public int hashCode() {
	return java.util.Objects.hash(가변 인수);
}
import java.util.HashSet;

class Student 
{
    private String name;
    private int age;
    
    public Student(String name, int age) 
    {
        this.name = name;
        this.age = age;
    }

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

//    public int hashCode() 
//    {
//        int num = age % 3;
//        System.out.println(num);
//        return num;
//    }

    public int hashCode() 
    {
        int num = java.util.Objects.hash(name, age);
        System.out.println(num);
        return num;
    }

    public boolean equals(Object obj) 
    {
        System.out.println("비교를 합니다.");
        if(age == ((Student)obj).age)
            return true;
        else
            return false;
    }    
}

public class Ex07_HashSetEqual
{
    public static void main(String[] args)
    {
        HashSet<Student> set = new HashSet<>();
        set.add(new Student("홍길동", 20));
        set.add(new Student("전우치", 20));
        set.add(new Student("홍길동", 25));

        System.out.println("객체 수: " + set.size());

        for(Student s : set)
            System.out.print(s.toString() + '\t');

        System.out.println();
    }
}

 

TreeSet

컬렉션 프레임워크에서 Tree로 시작하는 클래스는 데이터를 추가한 후 결과를 출력하면 결괏값이 정렬됨

TreeSet은 자료 중복을 허용하지 않으면서 출력값을 정렬하는 클래스

import java.util.Iterator;
import java.util.TreeSet;

public class Ex08_TreeSet
{
    public static void main(String[] args)
    {
        TreeSet<String> tree = new TreeSet<>();
        tree.add("홍길동");
        tree.add("전우치");
        tree.add("손오공");
        tree.add("멀린");
        tree.add("손오공");

        System.out.println("객체 수: " + tree.size());
        
        // Iterator 반복자에 의한 반복
        for(Iterator<String> itr = tree.iterator(); itr.hasNext(); )
            System.out.print(itr.next().toString() + '\t');
        System.out.println();
    }
}

 

TreeSet의 정렬

자바는 TreeSet의 정렬을 구현하기 위해 이진 탐색 트리(Binary Search Tree; BST)를 사용

 

트리 자료구조에서 각 자료가 들어가는 공간을 노드라고 함

그리고 위아래로 연결된 노드의 관계를 '부모-자식 노드'라고 함

이진 탐색 트리는 노드에 저장되는 자료의 중복을 허용하지 않고, 부모가 가지는 자식 노드 수가 2개 이하임

 

  • 각 노드의 왼쪽 서브 트리에는 해당 노드의 값보다 작은 값을 지닌 노드들로 이루어져 있음
  • 각 노드의 오른쪽 서브 트리에는 해당 노드의 값보다 큰 값을 지닌 노드들로 이루어져 있음
  • 중복된 노드가 없어야 함
  • 왼쪽 서브 트리, 오른쪽 서브 트리 또한 이진 탐색 트리임

비교 범위가 평균 1/2 만큼씩 줄어들어 효과적으로 자료를 검색할 수 있음

이렇게 만들어진 이진 탐색 트리를 맨 왼쪽 노드부터 시작해서 왼쪽 → 부모 → 오른쪽(뭉치) 순으로 순회하면 오름차순

순회하다 노드의 끝을 만나면 부모 노드로 올라감

 

가장 왼쪽인 노드 2부터 순회한 결과 : 2 → 3 → 4 → 6 →7 → 8

 

그 반대로 오른쪽 → 부모  →왼쪽 순으로 순회하면 내림차순

 

가장 오른쪽인 노드 8부터 순회한 결과 : 8 → 7 → 6 → 4 → 3 → 2

 

자바의 TreeSet은 지금 살펴본 이진 탐색 트리를 활용하여 자료를 정렬함

어떤 기준으로 객체 크기를 비교할 것인지는 프로그래머가 직접 구현해야 함

 

  • 인수로 전달된 o가 작다면 양의 정수 반환
  • 인수로 전달된 o가 크다면 음의 정수 반환
  • 인수로 전달된 o와 같다면 0 반환

 

Comparable<T> 인터페이스의 구현 결과를 근거로 객체의 크기 비교가 이루어짐

따라서 TreeSet<T>에 저장할 객체들은 모두 Comparable<T> 인터페이스를 반드시 구현한 클래스의 객체여야 함 (아닐 시 예외 발생)

import java.util.Set;
import java.util.TreeSet;

class Student2 implements Comparable<Student2>
{
    private String name;
    private int age;
    
    public Student2(String name, int age) 
    {
        this.name = name;
        this.age = age;
    }

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

    public int compareTo(Student2 p) 
    {
        return this.age - p.age;		// 나이 오름차순
//      return p.age - this.age;		// 나이 내림차순
//      return this.name.compareTo(p.name);	// 이름 오름차순
//      return p.name.compareTo(this.name);	// 이름 내림차순
    }
}

public class Ex09_Comparable
{
    public static void main(String[] args)
    {
        Set<Student2> tree = new TreeSet<>();
        tree.add(new Student2("홍길동", 30));
        tree.add(new Student2("전우치", 40));
        tree.add(new Student2("손오공", 20));
	
        for(Student2 s : tree)
            System.out.println(s);
    }
}

 

Comparator<T> 인터페이스

  • 정렬을 구현하는 데 사용하는 인터페이스
  • compare() 메서드를 구현해야 함
  • 기존 클래스가 Comparable<T> 인터페이스를 구현하여 이미 정렬 조건이 있다고 하더라도 새로운 정렬 조건을 주고 싶을 때 사용할 수 있음

 

String 클래스는 Comparable<E> 인터페이스를 구현하여 이미 사전순으로 정렬되도록 만들어져 있음

이 String 클래스의 정렬 조건을 변경하려면 다음 예제처럼 코드를 작성하여 사용하면 됨

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

class MyStringComparator implements Comparator<String>
{
    public int compare(String s1, String s2)
    {
        // 길이가 동일한 데이터는 추가되지 않는다.
        return s1.length() - s2.length();
    }
}

public class Ex10_Comparator
{
    public static void main(String[] args)
    {
//      Set<String> tree = new TreeSet<>();
        Set<String> tree = new TreeSet<>(new MyStringComparator());	// 글자 길이로 비교
        tree.add("홍길동");    
        tree.add("전우치");
        tree.add("전우치");
        tree.add("멀린");
        tree.add("해리포터");
    
        for(String s : tree)
            System.out.print(s.toString() + '\t');

        System.out.println();
    }
}

Set<String> tree = new TreeSet<>(); 으로 실행

Set tree = new TreeSet<>(new MyStringComparator()); 으로 실행

 

응용 : 중복된 객체 삭제

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

public class Ex11_ConvertExt
{
    public static void main(String[] args)
    {
        List<String> fixedSizeList = 
        		Arrays.asList("홍길동", "전우치", "전우치", "손오공");
        ArrayList<String> list = new ArrayList<>(fixedSizeList);
        
        for(String s : list)
            System.out.print(s.toString() + '\t');
        System.out.println();

        // 중복 제거
        HashSet<String> set = new HashSet<>(list);
        // 다시 list로 변환
        list = new ArrayList<>(set);

        for(String s : list)
            System.out.print(s.toString() + '\t');
        System.out.println();
    }
}

 

 

Queue<E> 인터페이스를 구현하는 컬렉션 클래스들

 

Queue의 구현

  • LinkedList<E>는 List<E>와 동시에 Queue<E>를 구현하는 클래스
  • 어떠한 타입의 참조 변수로 참조하느냐에 따라 '리스트'로도 '큐'로도 동작할 수 있음
import java.util.LinkedList;
import java.util.Queue;

public class Ex12_Queue
{
    public static void main(String[] args)
    {
        Queue<String> que = new LinkedList<>(); 

        // 데이터 저장       
        que.offer("A");
        que.offer("B");
        que.offer("C");
        System.out.println(que.size());

        // 무엇이 다음에 나올지 확인
        System.out.println("next: " + que.peek());
        // 첫 번째 객체 꺼내기
        System.out.println(que.poll());
        System.out.println(que.size());

        // 무엇이 다음에 나올지 확인
        System.out.println("next: " + que.peek());
        // 두 번째 객체 꺼내기
        System.out.println(que.poll());
        System.out.println(que.size());

        // 무엇이 다음에 나올지 확인
        System.out.println("next: " + que.peek());
        // 마지막 객체 꺼내기
        System.out.println(que.poll());
        System.out.println(que.size());
    }
}

 

Stack의 구현

자바에서는 Deque를 기준으로 스택을 구현함

Deque<String> deq = new ArrayDeque<>();
Deque<String> deq = new LinkedList<>();

 

다만 Deque를 이용하면 메서드를 사용하는 방법에 따라 자료구조를 큐처럼 사용할 수도 있고, 스택처럼 사용할 수도 있음

import java.util.ArrayDeque;
import java.util.Deque;

public class Ex13_Deque
{
    public static void main(String[] args)
    {
        // 둘 다 사용 가능
        Deque<String> deq = new ArrayDeque<>(); 
//      Deque<String> deq = new LinkedList<>(); 

        // 앞으로 넣고       
        deq.offerFirst("A");
        deq.offerFirst("B");
        deq.offerFirst("C");

        // 앞에서 꺼내기
        System.out.println(deq.pollFirst());
        System.out.println(deq.pollFirst());
        System.out.println(deq.pollFirst());  

        System.out.println("-------------------------------");
      
        // 뒤로 넣고       
        deq.offerLast("A");
        deq.offerLast("B");
        deq.offerLast("C");

        // 뒤에서 꺼내기
        System.out.println(deq.pollLast());
        System.out.println(deq.pollLast());
        System.out.println(deq.pollLast());  

        System.out.println("-------------------------------");
  
        // 뒤로 넣고       
        deq.offerLast("A");
        deq.offerLast("B");
        deq.offerLast("C");

        // 앞에서 꺼내기
        System.out.println(deq.pollFirst());
        System.out.println(deq.pollFirst());
        System.out.println(deq.pollFirst());  
    }
}

 

 

Map<K, V> 인터페이스를 구현하는 컬렉션 클래스들

Map 인터페이스에는 Key-Value 방식의 데이터를 관리하는 데 필요한 메서드가 정의되어 있음

객체의 key값은 유일하며 value값은 중복될 수 있음

 

  • HashMap 클래스 : 내부적으로 해시 알고리즘에 의해 구현되어 있음
  • TreeMap 클래스 : TreeSet과 마찬가지로 이진 탐색 트리로 구현되어 있음, key값으로 정렬하므로 key값에 해당하는 클래스에 Comparable이나 Comparator 인터페이스가 구현되어 있어야 함

 

HashMap<K, V> 클래스

import java.util.HashMap;

public class Ex14_HashMap
{
    public static void main(String[] args)
    {
        HashMap<String, String> map = new HashMap<>();
        
        // Key-Value 기반 데이터 저장
        map.put("홍길동", "010-1234-1443");
        map.put("전우치", "010-4321-1446");
        map.put("손오공", "010-9876-1443");

        // 데이터 탐색
        System.out.println("홍길동: " + map.get("홍길동"));
        System.out.println("전우치: " + map.get("전우치"));
        System.out.println("손오공: " + map.get("손오공"));
        System.out.println();

        // 데이터 삭제
        map.remove("손오공");

        // 데이터 삭제 확인
        System.out.println("손오공: " + map.get("손오공"));
    }
}

 

HashMap<K, V>의 순차적 접근 방법

HashMap<K, V> 클래스는 Iterable<T> 인터페이스를 구현하지 않았기에 향상된 기능의 for문을 통해서, 또는 '반복자'를 얻어서 순차적 접근을 할 수 없음

 

대신 다음 keySet() 메서드 호출을 통해서 Key를 따로 모아놓은 컬렉션 객체를 얻을 수 있음

그리고 이 때 반환된 컬렉션 객체 대상으로 반복자를 얻을 수 있음

import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

public class Ex15_HashMapKeySet
{
    public static void main(String[] args)
    {
        HashMap<String, Integer> map = new HashMap<>();
        
        // Key-Value 기반 데이터 저장
        map.put("홍길동", 20);
        map.put("전우치", 25);
        map.put("손오공", 27);

        // Key만 담고 있는 컬렉션 인스턴스 생성
        Set<String> ks = map.keySet();

        // 전체 Key 출력 (향상된 for문 기반)
        for(String s : ks)
            System.out.print(s + '\t');
        System.out.println();

        // 전체 Value 출력 (향상된 for문 기반)
        for(String s : ks)
            System.out.print(map.get(s).toString() + '\t');
        System.out.println();

        // 전체 Value 출력 (반복자 기반)
        for(Iterator<String> itr = ks.iterator(); itr.hasNext(); )
            System.out.print(map.get(itr.next()).toString() + '\t');
        System.out.println();
    }
}

 

TreeMap<K, V>의 순차적 접근의 예

TreeMap<K, V> 클래스는 Iterable<T> 인터페이스를 구현하지 않았지만, keySet() 메서드 호출을 통해서 Key를 따로 모아놓은 컬렉션 객체를 얻어서 순차적 접근을 할 수 있음

import java.util.Iterator;
import java.util.Set;
import java.util.TreeMap;

public class Ex16_TreeMapKeySet
{
    public static void main(String[] args)
    {
        TreeMap<String, Integer> map = new TreeMap<>();
        
        // Key-Value 기반 데이터 저장
        map.put("홍길동", 20);
        map.put("전우치", 25);
        map.put("손오공", 27);

        // Key만 담고 있는 컬렉션 인스턴스 생성
        Set<String> ks = map.keySet();

        // 전체 Key 출력 (향상된 for문 기반)
        for(String s : ks)
            System.out.print(s + '\t');
        System.out.println();

        // 전체 Value 출력 (향상된 for문 기반)
        for(String s : ks)
            System.out.print(map.get(s).toString() + '\t');
        System.out.println();

        // 전체 Value 출력 (반복자 기반)
        for(Iterator<String> itr = ks.iterator(); itr.hasNext(); )
            System.out.print(map.get(itr.next()).toString() + '\t');
        System.out.println();
    }
}

 

 

컬렉션 기반 알고리즘

정렬

List<E>를 구현한 컬렉션 클래스들은 저장된 객체를 정렬된 상태로 유지하지 않고 입력된 순서대로 유지함

정렬을 해야 한다면 Collections.sort() 메서드를 사용할 수 있음

이 때 객체 크기를 비교해야 정렬할 수 있으므로 객체의 클래스는 Comparable<T> 인터페이스를 구현한 상태이어야 함

 

String 클래스는 Comparable 인터페이스를 구현한 상태이므로 바로 테스트 가능

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Ex17_CollectionsSort
{
    public static void main(String[] args)
    {
        List<String> list = Arrays.asList("홍길동", "전우치", "손오공", "멀린");
        list = new ArrayList<>(list);

        // 정렬 이전 출력       
        System.out.println(list);

        // 정렬
        Collections.sort(list);        
        
        // 정렬 이후 출력
        System.out.println(list);
    }
}

 

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

// Comparator<T> 인터페이스를 구현할 클래스 생성
class StringDesc implements Comparator<String> 
{
    public int compare(String s1, String s2) 
    {
        return s2.compareTo(s1);	// 내림차순 비교, 매개변수의 순서를 바꾸면 오름차순
    }
}

public class Ex18_CollectionsSort2
{
    public static void main(String[] args)
    {
        List<String> list = new ArrayList<>();
        list.add("홍길동");
        list.add("전우치");
        list.add("손오공");

        // 정렬 : 오름차순
        Collections.sort(list);        
        System.out.println(list);

        StringDesc cmp = new StringDesc();

        // 정렬 : 내림차순
        Collections.sort(list, cmp);        
        System.out.println(list);
    }
}

 

검색

이진 탐색 기능을 이용하여 리스트 안에 데이터가 있는지 확인할 수 있음

다만 이진 탐색을 이용하려면 데이터가 먼저 정렬되어 있어야 함

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Ex19_CollectionsSearch
{
    public static void main(String[] args)
    {
        List<String> list = new ArrayList<>();
        list.add("홍길동");
        list.add("전우치");
        list.add("손오공");

        // 정렬
        Collections.sort(list);        

        // 탐색 : 해당 인덱스 반환
        int idx1 = Collections.binarySearch(list, "홍길동");
        System.out.println(idx1);
	// 탐색 : 검색 불가 -> 음수 반환
        int idx2 = Collections.binarySearch(list, "멀린");
        System.out.println(idx2);
    }
}

 

복사

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Ex20_CollectionsCopy
{
    public static void main(String[] args)
    {
        List<String> src = Arrays.asList("홍길동", "전우치", "손오공", "멀린");

        // 수정 가능한 리스트 생성        
        List<String> dst = new ArrayList<>(src);
        System.out.println(dst);

        // 정렬하여 그 결과를 출력
        Collections.sort(dst);
        System.out.println(dst);

        // 사정상 정렬 이전의 상태로 되돌려야 함
        Collections.copy(dst, src);
        System.out.println(dst);

        // 수정 가능한지 확인
        dst.remove(0);
        System.out.println(dst);
    }
}