삼십 번째 수업

정혅·2024년 3월 24일

더 조은 아카데미

목록 보기
35/76
post-thumbnail

오전문제

29일차 예제 참조

  1. Integer 인스턴스를 저장할 수 있는 ArrayList를 생성하고 저장용량을 500으로 늘린다.
    *ArrayList에 저장되어 있는 인스턴스 수의 두 배로 저장용량을 늘린다.

ensureCapacity() - 내부 배열의 크기가 최소 용량보다 작을 경우, 내부 배열을 확장해 요소를 추가할 수 있도록 공간을 확보해준다.

package com.test.memo;

import java.util.ArrayList;

public class Practice2 {
    public static void main(String[] args) {
        ArrayList<Integer> arr = new ArrayList<>();
        arr.ensureCapacity(500);
        arr.ensureCapacity((arr.size() * 2));

    }
}

예제

import java.util.ArrayList;

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

        // 요소를 추가하기 전에 현재 용량 확인
        System.out.println("현재 용량: " + list.size());

        // 요소를 추가하기 전에 최소한의 용량을 보장
        list.ensureCapacity(20);

        // 요소를 추가할 수 있음
        list.add(1);
        list.add(2);
        list.add(3);

        // 요소를 추가한 후의 용량 확인
        System.out.println("확장된 용량: " + list.size());
    }
}

ArrayList를 생성한 후에 ensureCaparcity(20)을 호출하여 최소한의 용량을 20으로 보장한다. >> 요소를 추가할 때 내부 배열의 크기가 부족한 경우에도 재할당 과정 없이 요소를 추가할 수 있다.


  1. ArrayList와 LinkedList 둘 중 하나를 선택하면 된다.

    1. 상황1
      저장하게 되는 데이터의 수가 대략적으로 예측 가능하며, 빈번한 데이터의 참조가 일어나는 상황에서 유용하게 사용할 수 있는 컬렉션 클래스는 무엇인가?

      • ArrayList
    2. 상황2
      저장하게 되는 데이터의 수가 예측 불가능하며, 빈번한 데이터의 저장 및 삭제가 일어나는 상황에서 유용하게 사용할 수 있는 컬렉션 클래스는 무엇인가?

      • LinkedList

  1. 두 인스턴스를 HashSet에 저장할 때, 두 인스턴스의 데이터(name & age)가 완전히 동일하다면, 하나만 저장되도록 hashCode 메소드와 equals 메소드를 오버라이딩 하자.

저장된 데이터 수 : 4
김명호(20세)
김명호(15세)
이진호(10세)
이진호(20세)

package com.test.memo;

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

class Person {
    String name;
    int age;

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

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

    @Override
    public int hashCode() {// 동일한 내용을 가진 객체들이 동일한 해시코드를 반환하여 효율적인 검색을 돕는 역할이다. 
        return name.hashCode();
        // Person객체가 같은 이름을 가질 때 같은 해시코드를 반환한다.
        // HashSet에 동일한 이름을 가진 두사람을 저장하더라도 HashSet은 동일한 해시코드를 가진 객체를 중복으로 간주해 저장하지 않는다.

        // return age%3; >> 정답 
    }

    @Override
    public boolean equals(Object obj) {//중복을 가리는 기능을 하는 것이다. 
        Person pp = (Person) obj;
        return pp.age == age && pp.name.equals(name);
    }

}

public class Practice2 {
    public static void main(String[] args) {
        HashSet<Person> hSet = new HashSet<Person>();
        hSet.add(new Person("이진호", 10));
        hSet.add(new Person("이진호", 20));
        hSet.add(new Person("김명호", 20));
        hSet.add(new Person("김명호", 15));
        hSet.add(new Person("이진호", 20));
        hSet.add(new Person("김명호", 20));

        System.out.println("저장된 데이터 수 : " + hSet.size());

        Iterator<Person> itr = hSet.iterator();
        while (itr.hasNext())
            System.out.println(itr.next());
    }
}
  • hashCode() 메서드를 age속성의 해시 코드를 기준으로 변경하면, 같은 나이를 가진 두사람이 HashSet에 중복으로 저장될 수 있다.

    • 두 사람의 이름은 다르지만 나이가 같은 경우에는 해시 코드가 동일하게 게산되어 중복으로 저장하는 것이다.
  • 현재 hashCode()메서드에서는 이름을 기준으로 중복의 기준을 다루지만 equals메서드에서 이름과 나이를 가지고 중복비교를 하고있기때문에 출력이 올바르게 이루어진 것이다.

    • hashCode()메서드와 equals()메서드를 함께 구현하면 객체를 올바르게 다루고 예기지 않은 동작을 방지할 수 있다.

근데 이름으로 중복을 나누나 나이로 중복을 나누나 어차피 equals메소드에서 처리가 다 이루어지는거라 똑같은것같다.


  1. Lotto한 세트를 만들어 출력하기
package com.test.memo;

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

public class Practice2 {
    public static void main(String[] args) {

        Set<Integer> lotto = new TreeSet<>();//호환성을 위해 Set으로 선

        while (lotto.size() < 6) {
            int ran = (int) (Math.random() * 45) + 1;
            lotto.add(ran);
        }
        System.out.println(lotto);
    }
}

  • TreeSet은 이진검색트리의 자료구조를 사용해 요소를 정렬한다.

  • while문에서 TreeSet의 크기가 6이 될때 까지 난수를 생성하고, 추가하는 과정을 반복한다.

    • TreeSet은 중복을 허용하지 않기 때문에 중복된 요소가 저장되지 않고, 순서대로 데이터를 저장하기 때문에 오름차순으로 나열되어있다.

  1. Person클래스는 나이를 기준으로 정렬하게끔 되어있는데, 이를 이름을 기준으로 정렬하게끔 변경시키자, > Person의 코드가 바뀌면 안된다. > 익명클래스로도 만들어보기

전화번호부는 29일차 참고

package com.test.memo;

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

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

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

    public void showData() {
        System.out.printf("%s %d \n", name, age);
    }

    public int compareTo(Person p) {
        if (age > p.age)
            return 1;
        else if (age < p.age)
            return -1;
        else
            return 0;
    }
}

//class NameComparator implements Comparator<Person> {
//    public int compare(Person p1, Person p2) {
//        return p1.name.compareTo(p2.name);
//    }
//} 이렇게 클래스를 생성해서도 가능하지만 한번 사용하고 말것이기 때문에 익명클래스가 더 효율적
class Organize {

    public static void main(String[] args) {
        TreeSet<Person> sTree = new TreeSet<Person>(new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.name.compareTo(o2.name);
            }
        });// 익명 클래스로 Comparator클래스 생성
        sTree.add(new Person("Lee", 24));
        sTree.add(new Person("Hong", 29));
        sTree.add(new Person("Choi", 21));

        Iterator<Person> itr = sTree.iterator();
        while (itr.hasNext())
            itr.next().showData();

    }
}

  1. 실수가 컴파일 과정에서 발견될 수 있도록 매개변수 선언을 수정하자. 그리고 프로그래머의 실수를 바로잡자.
package com.test.memo;

class Box<T> {
    private T ob;

    public void set(T o) {
        ob = o;
    }

    public T get() {
        return ob;
    }
}

class Organize {
    public static void addBox(Box<? super Integer> b1, Box<? extends Integer> b2, Box<? extends Integer> b3) {
        // 담아져야 하니까 super로 선언해 Integer클래스이거나, 해당 상위 클래스에서만 담을 수 있도록 제한
        b1.set(b2.get() + b3.get()); // 프로그래머의 실수가 있는 부분
    }

    public static void main(String[] args) {
        Box<Integer> box1 = new Box<>();
        box1.set(24);
        Box<Integer> box2 = new Box<>();
        box2.set(37);
        Box<Integer> result = new Box<>();
        result.set(0);

        addBox(result, box1, box2); // result에 24 + 37의 결과 저장
        System.out.println(result.get()); // 61 출력
    }
}

  1. 실수가 컴파일 과정에서 발견될 수 있도록 매개변수 선언을 수정하자. 그리고 프로그래머의 실수를 바로잡자.
package com.test.memo;

class Box<T> {
    private T ob;

    public void set(T o) {
        ob = o;
    }

    public T get() {
        return ob;
    }
}

class Organize {
    // box에 con과 동일한 내용물이 들었는지 확인
    public static <T> boolean compBox(Box<? extends T> box, T con) {
        T bc = box.get();
        box.set(con); // 프로그래머의 실수로 삽입된 문장, 때문에 내용물이 바뀐다.
        return bc.equals(con);
    }

    public static void main(String[] args) {
        Box<Integer> box1 = new Box<>();
        box1.set(24);

        Box<String> box2 = new Box<>();
        box2.set("Poly");

        if (compBox(box1, 25))
            System.out.println("상자 안에 25 저장");

        if (compBox(box2, "Moly"))
            System.out.println("상자 안에 Moly 저장");

        System.out.println(box1.get());
        System.out.println(box2.get());

    }
}

Box<? extends T>는 와일드카드를 사용하여 T의 하위 클래스를 나타내지만, 그 클래스의 하위 클래스까지는 나타내지 않는다.

이것은 Java 제네릭에서 와일드카드의 한 종류로, 와일드카드가 특정 유형의 서브타입을 포함하지만, 그 하위 타입을 포함하지 않는다는 것을 의미한다.

  • 따라서 Box<? extends T>에는 *T의 어떤 서브타입의 상자든 들어갈 수 있지만, 정확한 하위타입은 알 수 없다. 그렇끼에 컴파일러는 안전을 위해 box.set(con)에서 con을 허용하지 않습니다. 왜냐하면 conT의 하위타입이 될 수 있기 때문입니다.

  1. ArrayList로 전화번호부
package com.test.memo;

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

@SuppressWarnings("serial")
class MenuChoiceException extends Exception {
    private int choice;

    MenuChoiceException(int chioce) {
        super("유효하지 않은 메뉴값입니다.");// Exception의 getMessage()메소드에 저장된다.
        this.choice = chioce;
    }

    void showWrongMenu() {
        System.out.println(choice + "에 해당하는 선택은 존재하지 않습니다.");
        System.out.println("메뉴 선택을 처음부터 다시 진행합니다.");
    }
}

class PhoneInfo {
    private String name;
    private String phone;

    PhoneInfo(String name, String phone) {
        this.name = name;
        this.phone = phone;
    }

    String getName() {
        return name;
    }

    void showPhoneInfo() {
        System.out.println("이름 : " + name);
        System.out.println("전화번호 : " + phone);
    }
}

class PhoneUnivInfo extends PhoneInfo {
    private String major;
    private int year;

    PhoneUnivInfo(String name, String phone, String major, int year) {
        super(name, phone);
        this.major = major;
        this.year = year;
    }

    void showPhoneInfo() {
        super.showPhoneInfo();
        System.out.println("전공 : " + major);
        System.out.println("학년 : " + year);
    }
}

class PhoneCompanyInfo extends PhoneInfo {
    private String company;

    PhoneCompanyInfo(String name, String phone, String company) {
        super(name, phone);
        this.company = company;
    }

    void showPhoneInfo() {
        super.showPhoneInfo();
        System.out.println("회사 : " + company);
    }
}

class PhoneBook {
    private static PhoneBook pb;
    private List<PhoneInfo> pInfo;

    PhoneBook(int size) {
        pInfo = new ArrayList<>(size);
    }

    static PhoneBook getPhoneBookInst(int sizePhonfInfo) {
        if (pb == null) {
            pb = new PhoneBook(sizePhonfInfo);
        }
        return pb;
    }

    void inputPhoneInfo(PhoneInfo pp) {
        int i = 0;
        for (i = 0; i < pInfo.size(); i++) {
            if (pInfo.get(i).getName().compareTo(pp.getName()) > 0) {
                break; // break;문을 반드시 넣어줘야한다. 안넣었더니 배열에 값이 들어가지 못했다.
            }
        }
        this.pInfo.add(i, pp);
    }

    int search(String name) {
        for (int i = 0; i < pInfo.size(); i++) {
//            if (pInfo.get(i).getName().contains(name)) {
            // 위와 같이 작성하면 이름만을 검색하지 않고, 입력한 이름을 포함한 모든 항목을 찾기에, 원하는 결과를 얻을 수 없다.
//                if (pInfo.get(i).getName().equals(name)) {
            if (pInfo.get(i).getName().compareTo(name) == 0) {
                return i;
            }
        }
        return -1;
    }

    void searchPhoneInfo(String name) {
        int result = search(name);
        for (int i = 0; i < pInfo.size(); i++) {
            if (result != -1) {
                pInfo.get(result).showPhoneInfo();
            } else
                System.out.println("찾으시는 번호가 없습니다.");
        }
    }

    void deletePhoneInfo(int idx) {
        pInfo.remove(idx);
        System.out.println("삭제가 완료되었습니다.");
    }

    void showAllPhoneInfo() {
        for (PhoneInfo p : pInfo) {
            p.showPhoneInfo();
        }
    }
}

interface PhoneMenuString {
    int INPUT_PHONEINFO = 1;
    int SEARCH_PHONEINFO = 2;
    int DELETE_PHONEINFO = 3;
    int SHOW_ALL_PHONEINFO = 4;
    int PROGRAM_QUIT = 5;

    int GENERAL = 1;
    int UNIVERCITY = 2;
    int COMPANY = 3;

    int YES = 1;
    int NO = 2;
}

class PhoneUI {
    private static final int MAX_CNT = 100;
    static Scanner sc = new Scanner(System.in);
    private static PhoneBook pb = PhoneBook.getPhoneBookInst(MAX_CNT);

    static void mainMenu() {
        System.out.println("선택하세요...");
        System.out.println("1. 데이터 입력");
        System.out.println("2. 데이터 검색");
        System.out.println("3. 데이터 삭제");
        System.out.println("4. 모든 데이터 보기");
        System.out.println("5. 프로그램 종료");
        System.out.print("선택 : ");
    }

    static void inputMenu() {
        System.out.println("1. 일반, 2. 대학, 3. 회사");
    }

    static void inputGeneralPhoneInfo() {
        String name;
        String phone;

        System.out.println("데이터 입력을 시작합니다.");
        System.out.print("이름 : ");
        name = sc.nextLine();
        System.out.print("전화번호 : ");
        phone = sc.nextLine();
        System.out.println("데이터 입력이 완료되었습니다.");
        pb.inputPhoneInfo(new PhoneInfo(name, phone));
    }

    static void inputUniversityPhoneInfo() {
        String name;
        String phone;
        String major;
        int year;

        System.out.println("데이터 입력을 시작합니다.");
        System.out.print("이름 : ");
        name = sc.nextLine();
        System.out.print("전화번호 : ");
        phone = sc.nextLine();
        System.out.print("전공 : ");
        major = sc.nextLine();
        System.out.print("학년 : ");
        year = sc.nextInt();
        sc.nextLine();
        System.out.println("데이터 입력이 완료되었습니다.");
        pb.inputPhoneInfo(new PhoneUnivInfo(name, phone, major, year));
    }

    static void inputCompanyPhoneInfo() {
        String name;
        String phone;
        String company;

        System.out.println("데이터 입력을 시작합니다.");
        System.out.print("이름 : ");
        name = sc.nextLine();
        System.out.print("전화번호 : ");
        phone = sc.nextLine();
        System.out.print("회사 : ");
        company = sc.nextLine();
        System.out.println("데이터 입력이 완료되었습니다.");
        pb.inputPhoneInfo(new PhoneCompanyInfo(name, phone, company));
    }

    static void inputMenuChoice() throws MenuChoiceException {
        int choice = 0;

        choice = sc.nextInt();
        sc.nextLine();
        if (choice < PhoneMenuString.GENERAL || choice > PhoneMenuString.COMPANY) {
            throw new MenuChoiceException(choice);
        }
        switch (choice) {
        case PhoneMenuString.GENERAL:
            inputGeneralPhoneInfo();
            break;
        case PhoneMenuString.UNIVERCITY:
            inputUniversityPhoneInfo();
            break;
        case PhoneMenuString.COMPANY:
            inputCompanyPhoneInfo();
            break;
        }
    }

    static void searchPhoneInfo() {
        String name;
        System.out.println("데이터 검색을 시작합니다.");
        System.out.println("검색하시고자 하는 이름을 입력하세요.");
        name = sc.nextLine();
        pb.searchPhoneInfo(name);
    }

    static void deletePhoneInfo() {
        String name;
        int result = 0;
        int answer = 0;
        System.out.println("검색하시고자 하는 이름을 입력하세요.");
        name = sc.nextLine();
        result = pb.search(name);

        if (result != -1) {
            System.out.println("정말 삭제하시겠습니까? 1. Yes 2. No");
            answer = sc.nextInt();
            sc.nextLine();
            switch (answer) {
            case PhoneMenuString.YES:
                pb.deletePhoneInfo(result);
                break;
            case PhoneMenuString.NO:
                break;
            default:
                System.out.println("잘못 누르셨습니다.");
            }
        } else
            System.out.println("삭제하시려는 이름이 없습니다.");
    }

    static void showAllPhoneInfo() {
        pb.showAllPhoneInfo();
    }
}

public class Practice2 {
    public static void main(String[] args) {
        int choice = 0;

        while (true) {
            try {
                PhoneUI.mainMenu();
                choice = PhoneUI.sc.nextInt();
                PhoneUI.sc.nextLine();
                if (choice < PhoneMenuString.INPUT_PHONEINFO || choice > PhoneMenuString.PROGRAM_QUIT)
                    throw new MenuChoiceException(choice);

                switch (choice) {
                case PhoneMenuString.INPUT_PHONEINFO:
                    PhoneUI.inputMenu();
                    PhoneUI.inputMenuChoice();
                    break;
                case PhoneMenuString.SEARCH_PHONEINFO:
                    PhoneUI.searchPhoneInfo();
                    break;
                case PhoneMenuString.DELETE_PHONEINFO:
                    PhoneUI.deletePhoneInfo();
                    break;
                case PhoneMenuString.SHOW_ALL_PHONEINFO:
                    PhoneUI.showAllPhoneInfo();
                    break;
                case PhoneMenuString.PROGRAM_QUIT:
                    return;

                }
            } catch (MenuChoiceException e) {
                System.out.println(e.getMessage());
                e.showWrongMenu();
            }
        }
    }
}

Set인터페이스 복습

package com.test.memo;

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

class Member {
    private String name;
    private int age;

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

//    @Override 해당 메소드가 없다고 가정하
//    public int hashCode() {
//        return this.name.hashCode() + age;
//    }
//
//    @Override
//    public boolean equals(Object obj) {
//        if(obj instanceof Member) {
//            Member m = (Member)obj;
//            return this.name.equals(m.name) && this.age == m.age;
//        }else return false;
//    }

    @Override
    public String toString() {
        return "(" + name + ", " + age + ")";
    }

}

public class Practice2 {
    public static void main(String[] args) {
        Set set = new HashSet();

        set.add(new String("abc"));
        set.add(new String("abc"));

        set.add(new Member("홍길동", 20));
        set.add(new Member("홍길동", 20));

        System.out.println(set);

    }
}

  • String 객체는 중복으로 인식되어 한 개는 저장되지 않은 상태이지만, Member객체는 중복으로 인식되지 못하고 그대로 Set객체에 저장된것을 알 수 있다.

Map 인터페이스

Collection을 상속받지 않아, Iterator()메서드가 존재하지 않는다.

Map 컬렉션 클래스들은 키와 값을 하나의 쌍으로 저장하는 방식(key-value 방식)을 사용한다.

여기서 키(key)란 신질적인 값(value)을 찾기 위한 이름의 역할을 한다.

  • Map은 리스트나 배열처럼 순차적으로(sequentioal) 해당 값을 구하지 않고 key를 통해 value를 얻는다.

    • Map의 가장 큰 특징은 key로 value를 얻는다는 점이다.

특징

  1. 요소의 저장 순서를 유지하지 않는다.

  2. key : 중복을 허용 X >> 만약 기존에 저장된 키와 동일한 키로 값을 저장하면 새로 저장된 값으로 덧붙여진다.

    vlaue : 중복을 허용 O

    여기서 key와 vlaue는 모두 객체다.

메서드

Entry인터페이스는 Map의 키-값 쌍을 나타내는데 사용된다. Map.Entry를 사용하여 맵의 키와 값을 가져올 수 있다.


HashMap

많은 양의 데이터를 검색하는데 뛰어난다.

  • HashMap은 해시 함수를 통해 '키'와 '값'이 저장되는 위치를 결정해, 사용자가 그 위치를 알 수 없고, 삽입되는 순서와 들어있는 위치 또한 관계가 없다.

  • HashMap은 Map을 구현한다. key와 value를 묶어 하나의 entry로 저장한다는 특징을 갖는다.

  •  많은 양의 데이터를 검색하는데 검색 속도가 매우 빠르다.

  • value에 null값도 사용 가능하다.

  • HashMap 클래스는 Map 인터페이스를 구현하므로, 중복된 키로는 값을 저장할 수 없다.

    같은 값을 다른 키로 저장하는 것은 가능하다.

HashMap 선언

HashMap<String,String> map1 = new HashMap<String,String>();//HashMap생성
HashMap<String,String> map2 = new HashMap<>();//new에서 타입 파라미터 생략가능
HashMap<String,String> map3 = new HashMap<>(map1);//map1의 모든 값을 가진 HashMap생성
HashMap<String,String> map4 = new HashMap<>(10);//초기 용량(capacity)지정
HashMap<String,String> map5 = new HashMap<>(10, 0.7f);//초기 capacity,load factor지정
HashMap<String,String> map6 = new HashMap<String,String>(){{//초기값 지정
    put("a","b");
}};

put()

key와 value는 put메소드를 이용해 입력한다.

HashMap<String, String> map = new HashMap<String, String>();
map.put("people", "사람");
map.put("baseball", "야구");
HashMap<Integer, String> map1 = new HashMap<>();
map1.put(1, "One");
map1.put(2, "Two");
map1.put(3, "Three")
  • Map은 리스트나 배열처럼 순차적으로 해당 값을 구하지 않고 key를 통해 value를 얻는다.

    baseball이란 단어의 값을 찾기 위해서 순차적으로 모두 검색하는 것이 아니라 baseball이라는 단어가 있는 곳만을 펼쳐 보는것이다.

    MAP TABLE

    keyvalue
    people사람
    baseball야구
    1One
    2Two
    3Three

get( Object key )

key에 해당하는 value를 얻기위한 메소드다.

System.out.println(map.get("people"));
  • 위와 같이 get메소드를 이용해 people을 입력하면, 결과로 "사람"이라는 문자열을 출력할 것이다.

containsKey( Object key )

boolean타입으로 해당 Map에 전달된 key가 있는지를 조사하여 그 결과값을 리턴한다.

System.out.println(map.containsKey("people"));
  • 위와 같이 containsKey메소드를 사용해 people을 입력하면, 현재 위 예제에 people키가 존재하므로 true가 반환된다.

boolean containsValue(Object value)

해당 맵이 전달된 값에 해당하는 하나 이상의 키를 포함하고 있는지 확인한다.

System.out.println(map.containsValue("사람");

remove(Object key)

Map의 항목을 삭제하는 메소드로 key값에 해당되는 아이템(key, value)을 삭제한 후 그 value값을 리턴한다.

System.out.println(map.remove("people"));
  • "people"에 해당되는 아이템(people : 사람)이 삭제된 후 "사람"(value)이 출력될 것이다.

size()

Map의 갯수를 리턴한다.

System.out.println(map.size());
  • 위 예제들에서 map은 "people", "baseball" 두 값을 가지고 있다가 "people"항목이 삭제되었으므로 1이 출력될 것이다.

V replace(K key, V value)

해당 맵에서 전달된 키에 대응하는 값을 특정 값으로 대체함


예제1

package com.test.memo;

import java.util.HashMap;

class Organize {
    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<>();
        map.put("pe", "사람");
        map.put("ba", "야구");

        System.out.println(map.get("pe"));
        System.out.println(map.containsKey("ba"));
        System.out.println(map.size());
        System.out.println(map.remove("pe"));
        System.out.println(map.size());
    }
}


예제 2

import java.util.HashMap;

class IntroHashMap
{
    public static void main(String[] args)
    {
        HashMap<Integer, String> hMap=new HashMap<Integer, String>();

        hMap.put(Integer.valueOf(3), "나삼번");        
        hMap.put(5, "윤오번");    
        hMap.put(8, "박팔번");    

        System.out.println("6학년 3반 8번 학생: "+hMap.get(Integer.valueOf(8)));
        System.out.println("6학년 3반 5번 학생: "+hMap.get(5));
        System.out.println("6학년 3반 3번 학생: "+hMap.get(3));

        hMap.remove(5);        /* 5번 학생 전학 감 */
        System.out.println("6학년 3반 5번 학생: "+hMap.get(5));        
    }
}

Iterator 사용

HashMap의 전체 출력 시 반복문을 사용하지 않고, Iterator를 사용해도 된다.

iterator는 맵의 각 항목에 순차적으로 접근해, 맵의 키와 값에 대한 메서드를 제공해서 특정 항목의 키와 값을 가져올 수 있다.

hasNext() , next() 등등

HashMap<Integer,String> map = new HashMap<Integer,String>(){{//초기값 지정
    put(1,"사과");
    put(2,"바나나");
    put(3,"포도");
}};

//entrySet().iterator()
Iterator<Entry<Integer, String>> entries = map.entrySet().iterator();
while(entries.hasNext()){
    Map.Entry<Integer, String> entry = entries.next();
    System.out.println("[Key]:" + entry.getKey() + " [Value]:" +  entry.getValue());
}
//[Key]:1 [Value]:사과
//[Key]:2 [Value]:바나나
//[Key]:3 [Value]:포도

//keySet().iterator()
Iterator<Integer> keys = map.keySet().iterator();
while(keys.hasNext()){
    int key = keys.next();
    System.out.println("[Key]:" + key + " [Value]:" +  map.get(key));
}
//[Key]:1 [Value]:사과
//[Key]:2 [Value]:바나나
//[Key]:3 [Value]:포도
  • Iterator<Entry<Integer, String>> entries = map.entrySet().iterator();

    Map인터페이스의 내부에 중첩된 Entry인터페이스로, 위와 같이 선언해야한다.

    • entrySet() : 맵의 모든 키-값 쌍을 포함하는 'Set'을 가져온 후 해당 'Set'에 대한 iterator를 얻는다.
  • Iterator<Integer> keys = map.keySet().iterator();

    keySet()은 키만 다루므로 위와 같이 따로 선언할 필요는 없다.

    • keySet() : 맵의 모든 키를 포함한는 'Set'을 반환한다. 맵의 키만 얻을 수 있고, 값은 포함되지 않는다.

TreeMap

키와 값을 한 쌍으로 하는 데이터를 이진 검색 트리(binary search tree)의 형태로 저장한다. > Red-Black tree로 구현

  • 같은 Tree구조로 이루어진 TreeSet과의 차이점은 TreeSet은 그냥 값만 저장한다면 TreeMap은 키와 값이 저장된 Map, Etnry를 저장한다는 점이다.

  • TreeMap 은 객체를 저장하면 키를 기준으로 자동 오름차순으로 정렬된다.

    • TreeMap은 데이터를 저장할 때 즉시 정렬하기에 추가나 삭제가 HashMap보다 오래 걸린다.
    • 하지만 정렬된 상태로 Map을 유지해야 하거나 정렬된 데이터를 조회해야 하는 범위 검색이 필요한 경우 TreeMap을 사용하는 것이 효율성면에서 좋다.
  • Map 인터페이스를 구현하므로, 중복된 키로는 값을 저장할 수 없다

    • 같은 값을 다른 키로 저장하는 것은 가능

    메소드

TreeMap 선언

TreeMap<Integer,String> map1 = new TreeMap<Integer,String>();//TreeMap생성
TreeMap<Integer,String> map2 = new TreeMap<>();//new에서 타입 파라미터 생략가능
TreeMap<Integer,String> map3 = new TreeMap<>(map1);//map1의 모든 값을 가진 TreeMap생성
TreeMap<Integer,String> map6 = new TreeMap<Integer,String>(){{//초기값 설정
    put(1,"a");
}};
  • TreeMap을 생성하기 위해서는 키로 저장할 객체의 타입과 값을 저장할 객체 타입을 타입 파라미터로 주고 기본 생성자를 호출하면 된다. >> HashMap 과 크게 다르지 않으나 선언 시 크기를 지정해줄 수 없다.

TreeMap 값 추가, 삭제

추가

TreeMap<Integer,String> map = new TreeMap<Integer,String>();//TreeMap생성
map.put(1, "사과");//값 추가
map.put(2, "복숭아");
map.put(3, "수박");
  • TreeMap에 값을 추가하려면 put(key, value) 메소드를 사용하면 된다.
  • TreeMap의 타입 파라미터와 같은 타입의 key와 value값을 넣어야 정상적으로 값이 input되고, 이미 존재하는 키 값을 입력한다면 기존의 값이 새로 입력되는 값으로 대치된다.(덮어쓰기)

삭제

TreeMap<Integer,String> map = new TreeMap<Integer,String>(){{//초기값 설정
    put(1, "사과");//값 추가
    put(2, "복숭아");
    put(3, "수박");
}};

System.out.println(map); //전체 출력 : {1=사과, 2=복숭아, 3=수박}
System.out.println(map.get(1));//key값 1의 value얻기 : 사과
System.out.println(map.firstEntry());//최소 Entry 출력 :  1 = 사과
System.out.println(map.firstKey());//최소 Key 출력 : 1
System.out.println(map.lastEntry());//최대 Entry 출력: 3=수박
System.out.println(map.lastKey());//최대 Key 출력 : 3
  • TreeMap을 그냥 print하게 되면 {}로 묶어 Map의 전체 key값, value가 출력된다.

  • 특정 key값의 value를 가져오고 싶다면 get()메소드를 사용하면 된다.

    Tree구조로 항상 정렬되어 있어, 최소 Entry값, 최소 key값등을 리턴 받을 수 있다.


TreeMap 전체 값 출력 - Iterator

TreeMap<Integer,String> map = new TreeMap<Integer,String>(){{//초기값 설정
    put(1, "사과");//값 추가
    put(2, "복숭아");
    put(3, "수박");
}};

//entrySet() 활용 - key와 value 모두 필요한 경우
for (Entry<Integer, String> entry : map.entrySet()) {
    System.out.println("[Key]:" + entry.getKey() + " [Value]:" + entry.getValue());
}
//[Key]:1 [Value]:사과
//[Key]:2 [Value]:복숭아
//[Key]:3 [Value]:수박

//KeySet() 활용 - key값만 필요한 경우 
for(Integer i : map.keySet()){ //저장된 key값 확인
    System.out.println("[Key]:" + i + " [Value]:" + map.get(i));
}
//[Key]:1 [Value]:사과
//[Key]:2 [Value]:복숭아
//[Key]:3 [Value]:수박
  • keySet()은 key값을 이용해서 value를 찾는 과정에서 시간이 많이 소모되므로 많은 양의 데이터를 가져와야 하기 때문에, entrySet()이 좋습니다.(약 20%~200% 성능 저하가 있음)

예제 1

import java.util.TreeMap;
import java.util.Iterator;
import java.util.NavigableSet;

class IntroTreeMap
{
    public static void main(String[] args)
    {
        TreeMap<Integer, String> tMap=new TreeMap<Integer, String>();

        tMap.put(1, "data1");        
        tMap.put(3, "data3");    
        tMap.put(5, "data5");    
        tMap.put(2, "data2");    
        tMap.put(4, "data4");    

        NavigableSet<Integer> navi=tMap.navigableKeySet();
  System.out.println("오름차순 출력...");
        //KeySet().iterator() > key값만 받아온다.
        Iterator<Integer> itr=navi.iterator();
        while(itr.hasNext())
            System.out.println(tMap.get(itr.next())); >> 받아온 key값을 get()메소드를 이용해 value를 가져온다.

        System.out.println("내림차순 출력...");
        itr=navi.descendingIterator();
        while(itr.hasNext())
            System.out.println(tMap.get(itr.next()));    
    }
}
  • navigableKeySet 메소드가 호출되면, 인터페이스 NavigableSet를 구현하는 인스턴스가(인스턴스의 참조 값이)반환된다. 이 때 E는 key의 자료형인 Integer가 되며,
    변환된 인스턴스에는 저장한 데이터들의 key 정보가 저장되어 있다.
    NavigableSet 인터페이스는 Set 인터페이스를 상속한다. 즉 navigableKeySet
    메소드가 반환하는 인스턴스를 대상으로 반복자를 얻기 위해서 iterator 메소드의 호출이
    가능하다. 그리고 이렇게 해서 얻은 반복자로, 저장된 모든 key에 접근이 가능하다.

코드 연습

  1. 배열과 흡사한 제네릭 클래스를 이용해 값을 저장한 후 출력, 삭제 후 출력 해라
package com.test.memo;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;

class Organize {
    public static void main(String[] args) {

        ArrayList<Integer> arr = new ArrayList<>();

        arr.add(11);//오토박싱 오토 언박싱 되어 있는 것
        arr.add(12);//원래면 arr.add(new Integer(12)); 의 형태 
        arr.add(19);
        arr.add(5);

        Collections.sort(arr);
        Iterator<Integer> it = arr.iterator();

        while (it.hasNext()) {
            System.out.print(it.next() + " ");
        }
        System.out.println();

        arr.remove(0);//0번째 인덱스 삭제
        Iterator<Integer> itt = arr.iterator();

        while (itt.hasNext()) {
            System.out.print(itt.next() + " ");
        }
    }
}
/*출력 결과
 5 11 12 19 
 11 12 19
 */

  1. Integer인스턴스를 저장할 수 있는 ArrayList 를 생성하고 저장용량을 500으로 늘리고, 저장되어 있는 인스턴스 수의 두배로 저장용량을 늘린다.
import java.util.ArrayList;

class Organize {
    public static void main(String[] args) {

        ArrayList<Integer> arr = new ArrayList<>();
        arr.ensureCapacity(500); //저장 용량을 500으로 늘린다.
        arr.ensureCapacity(arr.size()*2); // 저장되어 있는 인스턴스 수(저장된 데이터 수)의 두 배로 저장 용량을 늘린다.

    }
}

위처럼 나중에 용량을 늘리는 메서드다. 그런데 용량을 증가시키는 과정에서 수반되는 연산으로 인해 떄로는 성능에 부담이 될 수 있어 초기에 용량을 잡는게 좋다.

0개의 댓글