기초단계/JAVA

2023.02.23 Java복습

춘핑이 2023. 2. 23. 19:29

15. 컬렉션

자바는 널리 알려져 있는 자료구조를 바탕으로 객체들을 효율적으로 추가삭제 검색할 수 있도록 java.util패키지에 포함시켜 놓앗다.
이들을 총칭해서 컬렉션 프레임워크라고 한다.
컬렉션 프레임워크는 몇 가지 인터페이스를 통해서 다양한 컬렉션 클래스를 이용할 수 있도록 설계되어있다.

주요 인터페이스로는 List, Set, Map이 있다.
List와 Set는 객체를 추가 삭제 검색하는 방법에 있어 공통점이 있어 공통된 메소드만 따로모아 Collection 인터페이스에 정의해두고 이것을 상속하고 있다.
Map은 키와 값을 하나의 쌓응로 묶어서 관리하는 구조로 되어 있어 사용법이 다르다.

15.2 List컬렉션

List컬렉션은 객체를 인덱스로 관리하기 때문에 객체를 저장하면 인덱스가 부여되고 인덱스로 객체를 검색 삭제할 수 있다.
1.객체 추가
boolean add(E e) 맨끝에 추가
void add(int index, E element) 인덱스에 추가
set(int index, E element) 주어진 인덱스 객체를 새로운 객체로 바꿈
2.객체 검색
boolean contains(Object o) 주어진객체가 저장되어 있는지 여부
E get(int index) 주어진 인덱스에 저장된 객체 리턴
isEmpty() 비어잇는지 조사
int size() 저장된 전체 객체수 리턴
3.객체삭제
void clear() 주어진 모든 객체 삭제
E remove(int indx) 주어진 인덱스에 저장된 객체 삭제
boolean remove(object o) 주어진 객체를 삭제

15.2.1 ArrayList

ArrayList는 list컬렉션에서 가장많이 사용하는 컬렉션이다.
ArrayList에 객체를 추가하면 내부 배열에 객체가 저장된다. 일반 배열과의 차이점은 ArrayList는 제한없이 객체를 추가할 수 있다는 것이다.

List 컬렉션은 객체 자체를 저장하는 것이 아니라 객체의 번지를 저장한다.
또한 동일한 객체를 중복저장할 수 있는데 이 경우에는 동일한 번지가 저장된다. null도 저장가능하다.

List<E> list = new ArrayList<>();으로 생성가능하다.

타입파라미터 E에는 ArrayList에 저장하고 싶은 객체 타입을 지정하면된다.
객체타입을 생략하면 모든 종류의 객체를 저장할 수 있다.

ArrayList컬렉션에 객체를 추가하면 인덱스 0번부터 차례대로 저장된다.
특정 인덱스의 객체를 제거하면 바로 뒤 인덱스부터 마지막 인덱스까지 앞으로 1씩당겨진다.
마찬가지로 특정 인덱스에 객체를 삽입하면 모두 1씩 밀려난다.

따라서 빈번한 객체 삭제와 삽입이 일어나는 곳에 ArrayList를 사용하는 것은 바람직하지 않다.

public class ArrayListExample {
    public static void main(String[] args) {
        //ArrayList 컬렉션 생성
        List<Board> list = new ArrayList<>();

        //객체 추가
        list.add(new Board("제목1", "내용1", "글쓴이1"));
        list.add(new Board("제목2", "내용2", "글쓴이2"));
        list.add(new Board("제목3", "내용3", "글쓴이3"));
        list.add(new Board("제목4", "내용4", "글쓴이4"));
        list.add(new Board("제목5", "내용5", "글쓴이5"));

        //저장된 총 객체 수 얻기
        int size = list.size();
        System.out.println("총 객체 수: " + size);
        System.out.println();

        //특정 인덱스의 객체 개져오기
        Board board = list.get(2);
        System.out.printf("%s\t%s\t%s\n", board.getSubject(), board.getContent(), board.getWriter());
        System.out.println();

        //모든 객체를 하나씩 가져오기
        for (int i = 0; i < list.size(); i++) {
            Board b = list.get(i);
            System.out.printf("%s\t%s\t%s\n", b.getSubject(), b.getContent(), b.getWriter());
        }
        System.out.println();

        //객체 삭제 2번삭제->3번이2번됨->또삭제
        list.remove(2);
        list.remove(2);

        for (int i = 0; i < list.size(); i++) {
            Board b = list.get(i);
            System.out.printf("%s\t%s\t%s\n", b.getSubject(), b.getContent(), b.getWriter());
        }
    }
}

15.2.2 Vector

Vector은 ArrayList와 동일한 내부 구조를 가지고 있다.
차이점은 동기화된 메소드로 구성되어 있어서 멀티스레드가 동시에 Vector()메소드를 실행할 수 없다는 것이다.
그래서 멀티스레드 환경에서 안전하게 객체를 추가 또는 삭제를 할 수 있다.

public class VectorExample {
    public static void main(String[] args) {
        // Vector 컬렉션 생성
        List<Board> list = new Vector<>();

        // 작업스레드 객체 생성
        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                // 객체 1000개추가;
                for (int i = 1; i <= 1000; i++) {
                    list.add(new Board("제목" + i, "내용" + i, "글쓴이" + i));
                }
            }
        });

        // 작업스레드 객체 생성
        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                // 객체 1000개추가;
                for (int i = 1001; i <= 2000; i++) {
                    list.add(new Board("제목" + i, "내용" + i, "글쓴이" + i));
                }
            }
        });

        // 작업스레드 실행
        threadA.start();
        threadB.start();

        // 작업 스레드들이 모두 종료될 때 까지 메인 스레드 기다리기
        try {
            threadA.join();
            threadB.join();
        } catch (InterruptedException e) {
        }

        //저장된 총객체수 얻기
        int size = list.size();
        System.out.println("총객체수: " + size); //2000
        System.out.println();
    }
}

이 예제를 ArrayList로 하면 비동기화된 메소드기 때문에 스레드가 서로 다퉈서 2000개가 저장되지 않을 수 있다.
하나의 객체를 담고 저장하려다 가져가고 그러면 저장안된다.

15.2.3 LinkedList

LinkedList는 ArrayList와 사용방법은 동일하지만 내부 구조가 완전히 다르다.
ArrayList은 내부 배열에 객체를 저장하지만 ArrayList는 인접 객체를 체인처럼 연결해서 관리한다.

LinkedList는 특정위치에서 객체를 삽입하거나 삭제하면 바로 앞 뒤링크만 변경하면 되므로 빈번한 객체 삭제와 삽입이 일어나는 곳에서 더 좋은 성능을 발휘한다.

public class LinkedListExample {
    public static void main(String[] args) {
        //ArrayList 컬렉션 객체 생성
        List<String> list1 = new ArrayList<String>();

        //LinkedList 컬렉션 객체 생성
        List<String> list2 = new LinkedList<String>();

        //시작 시간과 끝 시간을 저장할 변수 선언
        long startTime;
        long endTime;

        //ArrayList 컬렉션에 저장하는 시간 측정
        startTime = System.nanoTime();
        for(int i=0; i<10000; i++) {
            list1.add(0, String.valueOf(i));//제일앞에 삽입 하나씩 밀고 넣기 
        }
        endTime = System.nanoTime();
        System.out.printf("%-17s %8d ns \n", "ArrayList 걸린 시간: ", (endTime-startTime) );

        //LinkedList 컬렉션에 저장하는 시간 측정
        startTime = System.nanoTime();
        for(int i=0; i<10000; i++) {
            list2.add(0, String.valueOf(i)); //제일앞에 삽입 앞에 넣고 새로운 링크만 만들기
        }
        endTime = System.nanoTime();
        System.out.printf("%-17s %8d ns \n", "LinkedList 걸린 시간: ", (endTime-startTime) );
    }
}

ArrayList 걸린 시간: 15183800 ns
LinkedList 걸린 시간: 4142900 ns
확연한 차이가 있긴하다.

15.3 Set컬렉션

List컬렉션은 저장 순서를 유지하지만 Set컬렉션은 저장순서가 유지되지 않는다.
또한 객체를 중복해서 저장할 수 없고 하나의 null만 저장할 수 있다.
Set컬렉션은 수학의 집합에 비유될 수 있다. 순서는 상관없지만 중복이 허용되지 않는다.

구슬 주머니와도 같다. 동일한 구슬을 두개 넣을 수 없으며 들어갈때와 나올때 순서가 다를 수도 있기 때문이다.

Set컬렉션에는 HashSet LinkedSet TreeSet등이 있다.
1.객체추가
boolean add(E e) 주어진객체를 성공적으로 저장하면 true 중복이면 false
2.객체검색
boolean contains(Obejct o) 주어진 객체 저장되어있는지 여부
isEmpty() 비어있는지
Iterator<E> iterator() 저장된 객체를 한번씩 가져오는 반복자 리턴
int size() 저장된 객체수 리턴
3.객체 삭제
void clear() 저장된 모든객체 삭제
boolean remove(Object o) 주어진 객체를 삭제

객체 자체가 키값이 되는것임.

15.3.1 HashSet

Set컬렉션 중에 가장 많이 사용되는 것이 HashSet이다.
HashSet은 동일한 객체는 중복 저장하지 않는다. 여기서 동일한 객체란 동등객체를 말한다.
다른객체라도 hashCode() 메소드의 리턴값이 같고 equals()메소드가 true를 리턴하면 동일한 객체라고 판단하고 중복저장하지 않는다.

public class HashSetExample {
    public static void main(String[] args) {
        //HashSet 컬렉션 생성
        Set<String> set = new HashSet<>();

        //객체 저장
        set.add("Java");
        set.add("JDBC");
        set.add("JSP");
        set.add("Java"); //중복객체이므로 저장하지 않음.
        set.add("Spring");

        //저장된 객체수 출력
        int size = set.size();
        System.out.println("총 객체 수: " + size); //4
    }
}

다음 예제는 이름과 나이가 동일할경우 Member객체를 HashSet에 중복저장하지 않는다.
Member클래스를 선언할때 이름과 나이가 동일하다면 동일한 해쉬코드가 리턴되도록 재정의하고 equals가 true를 리턴하도록 재정의 했기 때문이다.

public class Member {
    public String name;
    public int age;

    public Member(String name, int age) {
        this.name = name;
        this.age = age;
    }

    //hashCode 재정의
    @Override
    public int hashCode() {
        return name.hashCode() + age;
    }

    //equals 재정의
    @Override
    public boolean equals(Object obj) {
        if(obj instanceof Member target) {
            return target.name.equals(name) && (target.age == age);
        } else {
            return false;
        }
    }
}

public class HashSetExample {
    public static void main(String[] args) {
        // HashSet컬렉션 생성
        Set<Member> set = new HashSet<>();

        // Member객체 저장
        // 인스턴스는 다르지만 동등객체이므로 객체1개만 저장
        set.add(new Member("홍길동", 30));
        set.add(new Member("홍길동", 30));

        // 저장된 객체 수 출력
        System.out.println("총 객체 수: " + set.size()); // 1
    }
}

동등비교를 지우면 2개가 저장이된다.
entity로 set을 사용하려면 hashcode와 equals를 재정의 해둘필요가 있어보인다.
롬복을 사용하면 알아서 만들어주니 이거 사용해도 될듯하다.
중복값을 넣기 싫을때 사용하자.

Set컬렉션은 인덱스로 객체를 검색하는 메소드가 없다. 대신 객체를 한개씩 반복해서 가져와야한다.
1.for문을 이용하는 것이다.
Set<E> set = new HashSet<>();
for(E e : set){}

다른 방법은
2.Set컬렉션의 iterator() 메소드로 반복자를 얻어 객체를 하나씩 가져오는 것이다.
Set<E> set = new HashSet<>();
Iterator<E> iterator = set.iterator();

iterator는 Set컬렉션의 객체를 가져오거나 제거하기 위해 메소드를 제공한다.

boolean | hasNext() | 가져올 객체가 있으면 treu리턴 없으면 false리턴
E | next() | 컬렉션에서 하나의 객체를 가져온다.
void | remove() | next()로 가져온 객체를 Set컬렉션에서 제거한다.

hasNext로 가져올객체가 있는지 먼저 확인하고 true를 리턴할때만 next메소드로 객체를 가져온다.

  public class HashSetExample {
      public static void main(String[] args) {
          //HashSet 컬렉션 생성
          Set<String> set = new HashSet<>();

          //객체 추가
          set.add("Java");
          set.add("JDBC");
          set.add("JSP");
          set.add("Spirng");

          //객체를 하나씩 가져와서 처리
          Iterator<String> iterator = set.iterator();

          while (iterator.hasNext()) {
              //객체를 하나씩 가져오기
              String element = iterator.next();
              System.out.println(element);
              if(element.equals("JSP")) {
                  //가져온 객체를 Set에서 삭제
                  iterator.remove();
              }
          }
          System.out.println();

          //객체 제거
          set.remove("JDBC");

          //객체를 하나씩 가져와서 처리
          for(String element : set) {
              System.out.println(element);
          }
      }
  }

15.4 Map컬렉션

Map컬렉션은 키(Key)와 값(Value)으로 구성된 엔트리(Entry) 객체를 저장한다.
여기서 키와 값은 모두 객체이다. 키는 중복저장이 불가능하지만 값은 중복저장이 가능하다.
기존에 저장된값은 없어지고 새로운 값으로 대치된다

Map은 키로 객체들을 관리하기때문에 키를 매개값으로 하는 메소드가 많다.


Set<K> keySet = map.keySet();
Set<Entry<K, V>> entrySet = map.entrySet();
+set으로 만들고 Iteraotor 반복자로 꺼내는 것 기억하자.

15.4.1 HashMap

HashMap은 키로 사용할 객체가 hashCode() 메소드의 리턴값이 같고 equals()메소드가 true를 리턴할 경우 동일키로보고 중복저장을 허용하지 않는다.
Map<K, V> map = new HashMap<>();
K와 V에는 각각 키와 값의 타입을 지정할 수 있는 타입파라미터이다.

public class HashMapExample {
    public static void main(String[] args) {
        // Map컬렉션 생성
        Map<String, Integer> map = new HashMap<>();

        // 객체 저장
        // 키가 같으면 제일 마지막에 저장된 값만 저장
        map.put("신용권", 85);
        map.put("홍길동", 90);
        map.put("동장군", 80);
        map.put("홍길동", 95);
        System.out.println("총 Entry 수: " + map.size());
        System.out.println();

        // 키로 값 얻기
        String key = "홍길동";
        int value = map.get(key);
        System.out.println(key + ": " + value);
        System.out.println();

        // 키 Set컬렉션을 얻고 반복해서 키와 값을 얻기
        // 키를 반복하기 위해 반복자를 얻음
        Set<String> keySet = map.keySet();
        Iterator<String> keyIteraotr = keySet.iterator();
        while (keyIteraotr.hasNext()) {
            String k = keyIteraotr.next();
            Integer v = map.get(k);
            System.out.println(k + ": " + v);
        }
        System.out.println();

        // 엔트리 Set컬렉션을 얻고 반복해서 키와 값을 얻기
        Set<Entry<String, Integer>> entrySet = map.entrySet();
        Iterator<Entry<String, Integer>> entryIterator = entrySet.iterator();
        while (entryIterator.hasNext()) {
            Entry<String, Integer> entry = entryIterator.next();
            String k = entry.getKey();
            Integer v = entry.getValue();
            System.out.println(k + ": " + v);
        }
        System.out.println();

        // 키로 엔트리 삭제
        map.remove("홍길동");
        System.out.println("총 Entry 수: " + map.size());
        System.out.println();
    }
}

15.4.2 Hashtable

HashMap과 Hashtable은 동일한 내부구조를 가지고 있다.
차이점은 Hashtable은 동기화된 메소드를 가지고 있다는 것이다.(Vector와 비슷)
따라서 멀티스레드 환경에서도 안전하게 객체를 추가 삭제 할 수있다.

public class HashtableExample {
    public static void main(String[] args) {
        // Hashtable 컬렉션 생성
        Map<String, Integer> map = new Hashtable<>();

        // 작업 스레드 객체 생성
        Thread threadA = new Thread() {
            @Override
            public void run() {
                // 엔트리 1000개 추가
                for (int i = 1; i <= 1000; i++) {
                    map.put(String.valueOf(i), i);
                }
            }
        };

        // 작업 스레드 객체 생성
        Thread threadB = new Thread() {
            @Override
            public void run() {
                // 엔트리 1000개 추가
                for (int i = 1001; i <= 2000; i++) {
                    map.put(String.valueOf(i), i);
                }
            }
        };

        // 작업 스레드 실행
        threadA.start();
        threadB.start();

        // 작업 스레드들이 모두 종료될 때까지 메인 스레드를 기다리게 함
        try {
            threadA.join();
            threadB.join();
        } catch (Exception e) {
        }

        // 저장된 총 엔트리 수 얻기
        int size = map.size();
        System.out.println("총 엔트리 수: " + size); //2000
        System.out.println();
    }
}

Vector와 ArrayList의 차이처럼 경합이 이루어지지 않게 하도록 하는 것이다.

15.4.3 Properties

Properties는 Hashtable의 자식클래스이기 때문에 Hashtable의 특징을 그대로 가지고 있다.
Properties는 키와 값을 Stringㅇ로 제한한 컬렉션이다. Properties는 주로 확장자가 .properties인 프로퍼티 파일을 읽을 때 사용한다.
프로퍼티 파일은 키와 값이 = 기호로 연결되어 있는 텍스트 파일이다.
일반 텍스트파일과는 다르게 ISO 8859-1 문자셋으로 저장되며 한글일경우는 \u유니코드로 표현되어 저장된다.

Properties properties = new Properties();
properties.load("경로");
load() 메소드로 프로퍼티 파일의 내용을 메모릐로 로드한다.
일반적으로 프로퍼티 파일은 클래스 파일들과 함께 저장된다. 따라서 클래스 파일을 기준으로 상대 경로를 이용해서 읽는 것이 편리하다.
Class객체의 getResourceAsStream() 메소드는 주어진 상대 경로의 리소스파일을 읽는 인풋스트림을 리턴한다.

driver=oracle.jdbc.OracleDirver
url=jdbc:oracle:thin:@localhost:1521:orcl
username=scott
password=tiger
admin=\uD64D\uAE38\uB3D9

public class PorpertiesExample {
    public static void main(String[] args) throws IOException {
        //프로퍼티 컬렉션 생성
        Properties properties = new Properties();

        //PorpertiesExample.class와 동일한 ClassPath에 있는 database.properties파일로드
        properties.load(PorpertiesExample.class.getResourceAsStream("database.properties"));

        //주어진 키에 대한 값 얻기
        String driver = properties.getProperty("driver");
        String url = properties.getProperty("url");
        String username = properties.getProperty("username");
        String password = properties.getProperty("password");
        String admin = properties.getProperty("admin");

        //값 출력
        System.out.println("driver: " + driver);
        System.out.println("url: " + url);
        System.out.println("username: " + username);
        System.out.println("password: " + password);
        System.out.println("admin: " + admin);
    }
}

15.5 검색 기능을 강화시킨 컬렉션

TreeSet과 TreeMap은 검색기능을 강화시킨 컬렉션이다.

15.5.1 TreeSet

이진트리를 기반으로한 Set 컬렉션이다.
이진트리는 여러 노드가 트리형태로 연결된구조로 루트 노드로 불리는 하나의 로드에서 시작해 각 노드에 최대 2개를 연결할 수 있는 구조를 가지고 있다.
Set타입 변수에 대입해도 되지만 TreeSet타입으로 대입하는 이유는 관련 메소드가 TreeSet에만 정의되어 있기 때문이다.
부모 = 자식하면 부모 메소드만 사용가능하기 때문임!!

NavigableSet<Integer>
headSet(E, inclusive) 주어진 객체보다 낮은 객체들을 set으로 리턴 treu하면 포함 false면 미포함
tailSet(E, inclusive) 주어진객체보다 높은 객체들을 리턴 포함여부표기
subSet(E, inclusive, E2, in~)시작과 끝 사이 포함여부는 각매개값

public class TreeSetExample {
    public static void main(String[] args) {
        // TreeSet 컬렉션 생성
        TreeSet<Integer> scores = new TreeSet<>();

        // Integer객체 저장
        scores.add(87);
        scores.add(98);
        scores.add(75);
        scores.add(95);
        scores.add(80);

        // 정렬된 Integer 객체를 하나씩 가져오기
        for (Integer s : scores) {
            System.out.print(s + " ");
        }
        System.out.println();

        // 특정 Intger객체를 가져오기
        System.out.println("가장 낮은 점수: " + scores.first());
        System.out.println("가장 높은 점수: " + scores.last());
        System.out.println("95점 아래 점수: " + scores.lower(95));
        System.out.println("95점 위의 점수: " + scores.higher(95));
        System.out.println("95점이거나 바로 아래 점수: " + scores.floor(95));
        System.out.println("85점이거나 바로 위의 점수: " + scores.ceiling(85) + "\n");

        // 내림차순으로 정렬하기
        NavigableSet<Integer> descendingScores = scores.descendingSet();
        for (Integer s : descendingScores) {
            System.out.print(s + " ");
        }
        System.out.println();

        // 범위검색 (80 <= )
        NavigableSet<Integer> rangeSet = scores.tailSet(80, true);
        for (Integer s : rangeSet) {
            System.out.print(s + " ");
        }
        System.out.println();

        // 범위검색 (80<= scroe < 90)
        rangeSet = scores.subSet(80, true, 90, false);
        for (Integer s : rangeSet) {
            System.out.print(s + " ");
        }
    }
}

15.5.2 TreeMap

이진트리를 기반으로한 Map컬렉션이다. 부모 키값과 비교해서 낮은것은 왼쪽 높은 것은 오른쪽

public class TreeMapExample {
    public static void main(String[] args) {
        //TreeMap 컬렉션 생성
        TreeMap<String,Integer> treeMap = new TreeMap< >();

        //엔트리 저장 (키, 값) 키들로 정렬시킴.
        treeMap.put("apple", 10);
        treeMap.put("forever", 60);
        treeMap.put("description", 40);
        treeMap.put("ever", 50);
        treeMap.put("zoo", 80);
        treeMap.put("base", 20);
        treeMap.put("guess", 70);
        treeMap.put("cherry", 30);

        //정렬된 엔트리를 하나씩 가져오기
        //Map자체는 향상된 반복문에 못넣음. 그래서 Set으로 바꾸어서 저장하고 불러오기
        Set<Entry<String, Integer>> entrySet = treeMap.entrySet();
        for(Entry<String, Integer> entry : entrySet) {
            System.out.println(entry.getKey() + "-" + entry.getValue());
        }
        System.out.println();

        //특정 키에 대한 값 가져오기
        Entry<String,Integer> entry = null;
        entry = treeMap.firstEntry();
        System.out.println("제일 앞 단어: " + entry.getKey() + "-" + entry.getValue());
        entry = treeMap.lastEntry();
        System.out.println("제일 뒤 단어: " + entry.getKey() + "-" + entry.getValue());
        entry = treeMap.lowerEntry("ever");
        System.out.println("ever 앞 단어: " + entry.getKey() + "-" + entry.getValue() + "\n");

        //내림차순으로 정렬하기
        NavigableMap<String,Integer> descendingMap = treeMap.descendingMap();
        Set<Entry<String,Integer>> descendingEntrySet = descendingMap.entrySet();
        for(Entry<String,Integer> e : descendingEntrySet) {
            System.out.println(e.getKey() + "-" + e.getValue());
        }
        System.out.println();

        //범위 검색
        System.out.println("[c~h 사이의 단어 검색]");
        NavigableMap<String,Integer> rangeMap = treeMap.subMap("c", true, "h",    false); //c이상 h미만
        for(Entry<String, Integer> e : rangeMap.entrySet()) { //여기도 Set으로 바꾸어서 넣음.
            System.out.println(e.getKey() + "-" + e.getValue());
        }
    }
}

15.5.3 Compareable과 Comparator

TreeSet에 저장되는 객체와 TreeMap에 저장되는 키 객체는 저장과 동시에 오름차순으로 정렬이된다.
어떤 객체든 정렬할 수 잇는 것은 아니고 객체가 Comparable인터페이스를 구현하과 잇어야 가능하다.
Integer Double String타입은 모두 Complarable을 구현하고 있기 때문에 상관없지만 사용자 객체 정의를 저장할때는 반드시 Complarble을 구현하고 있어야한다.
Compareable 인터페이스에는 compareTo() 메소드가 정의되어 있다. 따라서 사용자 정의 클래스에서 이 메소드를 재정의 해서 비교결과를 정수값으로 리턴해야한다.

int compareTo(T o) 주어진 객체와 같으면 0리턴 적으면 음수 크면 양수리턴

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

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int compareTo(Person o) {
        if(age<o.age) {
            return -1;
        } else if(age == o.age) {
            return 0;
        } else {
            return 1;
        }
    }
}

public class ComparableExample {
    public static void main(String[] args) {
        //TreeSet컬렉션 생성
        TreeSet<Person> treeSet = new TreeSet<>();

        //객체 저장
        treeSet.add(new Person("홍길동", 45));
        treeSet.add(new Person("김자바", 25));
        treeSet.add(new Person("박지원", 31));

        //객체를 하나씩 가져오기
        for(Person person : treeSet) {
            System.out.println(person.name + ":" + person.age);
        }
    }
}

Person에 compareTo구현을 안하면 TreeSet에 담는것 조차 불가능하다.
객체를 이진트리로 순서대로 넣어야되는데 그 순서를 잡지 못하는 것이다.

비교기능이 있는 Compareable 구현객체를 TreeSet에 저장하거나 TreeMap의 키로 저장하는게 원칙이지만
비교기능이 없는 Comparable 비구현 객체를 저장하고 싶다면 생성할대 비교자(Comparator)를 제공하면된다.

비교자는 Comparator 인터페이스를 구현한 객치를 말한다. Comparator인터페이스에는 compareTo()메소드가 정의되어 있다.
비교자는 이 메소드를 재정의해서 비교결과를 정수 값으로 리턴하면 된다.

public class Fruit {
    public String name;
    public int price;

    public Fruit(String name, int price) {
        this.name = name;
        this.price = price;
    }
}

public class FruitComparator implements Comparator<Fruit>{
    @Override
    public int compare(Fruit o1, Fruit o2) {
        if (o1.price < o2.price) {
            return -1;
        }
        else if (o1.price == o2.price) {
            return 0;
        }
        else {
            return 1;
        }
    }
}

public class ComparotorExample {
    public static void main(String[] args) {
        TreeSet<Fruit> treeSet = new TreeSet<>(new FruitComparator());

        treeSet.add(new Fruit("포도", 3000));
        treeSet.add(new Fruit("수박", 10000));
        treeSet.add(new Fruit("딸기", 6000));

        //객체 하나씩 가져오기
        for(Fruit fruit : treeSet) {
            System.out.println(fruit.name + ":" + fruit.price);
        }
    }
}

2023.02.23 후기

트리셋과 트리맵에 담기위해서 Comparable를 왜 구현해야하는지 잘몰랐다. 구현하지 않으면 나무에 순서대로 나열하는게 불가능해서 그렇다.


iterator 자주잊는다 기억잘해두기
어려운 문제를 만나서 오래 푸느라 다 끝내지 못했다. 내일 이어서 하도록하겠다.