국비/Java

2023.03.15 34일차 Java

춘핑이 2023. 3. 15. 16:45

34일차

컬렉션 프레임워크 다른객체를 가지고 있는 객체
주로 사용할 것임.
컬렉션에 관련한 유용한 메소드 Collections에 있다.

15.2 List

15.2.1 List.of

ArrayList의 생성자중 colleciton을 받을 수있다.
List는 Collection을 상속받고 있다. 그래서 ArrayList에 List를 생성자로 넣을 수있다.

//수정불가한 리스트
List<String> list1 = List.of("java", "css", "spring", "jsp", "html");
// 수정가능한 리스트로 다시만듬
List<String> list2 = new ArrayList<>(list1); 

15.3 Collections 메소드

다 static메소드이다.

15.3.1 Collections.sort()

컬렉션에서 정렬해준다.

public static void main(String[] args) {
    List<String> list1 = List.of("java", "css", "spring", "jsp", "html");
    List<String> list2 = new ArrayList<>(list1); 
    Collections.sort(list2);
    System.out.println(list2);
    //[css, html, java, jsp, spring]
}

15.3.2 Collections.max()

컬렉션에서 최대값 출력

String maxStr = Collections.max(list2);
System.out.println(maxStr); //spring

15.3.3 Collections.min();

컬렉션에서 최소값출력

String minStr = Collections.min(list2);
System.out.println(minStr); //css

15.3.4 Collections.reverse();

컬렉션 뒤집기

Collections.reverse(list2);
System.out.println(list2);
//[spring, jsp, java, html, css]

15.3.5 Collections.fill(컬렉션, 값);

컬렉션을 파라미터에 들어온 값으로 다 변경해준다.

Collections.fill(list2, "web");
System.out.println(list2);
//[web, web, web, web, web]

15.4 Comparable와 Comparator

String타입은 어떤기준으로 정렬하나?
Comparable 인터페이스를 구현하고 있다면 가능하다.
natural ordering이 가능하다.

비교가 불가능한 클래스를 정렬하는 등의 하려면
Comparator을 구현해서 넣어주거나 클래스가 Comparable을 구현해야한다.

15.4.1 Comparator

Comparator은 함수형인터페이스이다. compare(T o1, T o2)메소드를 가지고 있다.
두 아규먼트를 순서를 정하기 위해 비교한다.
리턴하는 인티저 값은 음수 , 0 , 양수 세개중 하나이다.
첫번째 아규먼트가 작으면 음수 같으면 0 첫번째가 더크면 양수

Person02 maxAge = Collections.max(list2, (a, b) -> a.getAge() - b.getAge());
System.out.println(maxAge);

Person02 minAge = Collections.min(list2, (a, b) -> a.getAge() - b.getAge());
System.out.println(minAge);

System.out.println(list2);
Collections.sort(list2, (a, b) -> a.getAge() - b.getAge());
System.out.println(list2);

앞 아규먼트 - 뒤 아규먼트해서 음수리턴 0리턴 양수리턴에 따라서 값을 주는 것임.
어떻게 비교하든 음수 0 양수만 리턴하면 앞과 뒤를 비교할 수 있는 것이다.
책의 예제에서는 비교해서 -1(음수) 0 1(양수) 리턴햇는데

Person02 maxAge1 = Collections.max(list2, (a, b) -> {
    return a.getAge() - b.getAge();
});

위는 age라 가능한것이다.
이름으로 비교하려면 String타입이니 그거대로 비교자를 작성해줘야한다.
compareTo타입이 Integer타입을 리턴한다. 유니코드 순으로 비교한다.
음수 0 양수를 리턴한다.

Collections.sort(list2, (a, b) -> a.getName().compareTo(b.getName()));
System.out.println(list2);

15.4.2 Comparable

크다 작다가 자연스럽게 적용이 되도록 구현을 하고 싶다.

class Person03 implements Comparable<Person03>{
    private String name;
    private int age;

    @Override
    public int compareTo(Person03 o) {
        //이름순 정렬
        //return this.name.compareTo(o.name);
        //나이순 정렬
        //return age - o.age;

        //1.나이순 비교 같다면 이름순 비교
        int ageDiff = this.age - o.age;

        if (ageDiff == 0) {
            return this.name.compareTo(o.name);
        }
        return ageDiff;
    }
}

객체 비교를 어떻게 할지 내가 정할 수 있다.

15.4.3 연습 comparator

생일로 주어졌을때 나이순 정렬 (나이가 어릴 수록 작은 index에)
위치만 바꾸어주면 된다.

public class C04Compare {
    public static void main(String[] args) {
        var list = new ArrayList<Person04>();
        list.add(new Person04("kang", "20230315"));
        list.add(new Person04("seo", "20221203"));
        list.add(new Person04("chae", "20020605"));
        list.add(new Person04("song", "20121203"));
        list.add(new Person04("jung", "20210707"));

        // 나이순 정렬 (나이가 어릴 수록 작은 idnex에)
        System.out.println(list);
        Collections.sort(list, (a,b) -> b.getBirth().compareTo(a.getBirth()));
        System.out.println(list);
    }
}

15.5 Set

Set은 순서를 유지하지 않고 저장한다. 그래서 중복저장이안된다.
수학의 집합이라고 보면된다.

구현한 클래스를 별일 없으면 HashSet을 사용하면된다.

삭제할 때 list는 인덱스를 넣었는데 그냥 값을 넣어서 지정하면된다.
element를 넣은 순서와 set이 가지고 있는 순서는 상관이 없다.

전체탐색 시 index가 없어서 고전 for문을 사용할 수 없다.

public class C01Set {
    public static void main(String[] args) {
        Set<Integer> set = new HashSet<>();

        // add element
        set.add(30);
        set.add(90);
        set.add(3);
        set.add(7);
        set.add(90); // 안들어감

        // size 크기
        int size = set.size();
        System.out.println(size); //4

        // remove
        set.remove(3);
        System.out.println(set.size()); //3

        // 전체탐색
        System.out.println("향상된 for ##########");
        for (Integer e : set) {
            System.out.println(e);
        }

        System.out.println("Iterator $$$$$$$$");
        Iterator<Integer> iter = set.iterator();
        while (iter.hasNext()) {
            System.out.println(iter.next());
        }

        System.out.println("forEach ***********");
        set.forEach(e -> System.out.println(e));

        System.out.println("forEach 메소드 참조");
        set.forEach(System.out::println);
    }
}

15.5.1 Iterator(반복자)

Iterator는 collection 탐색을 위한 객체의 타입이다.
hasNext : 다음 탐색할 원소가 있는지?
next : 다음 원소 리턴

기존 for문은 탐색중에 변화를 일으키면 예외를 발생한다.
따라서 지우는 목적으로 탐색하고 싶다면 반복자를 사용해야한다.
hasNext하면서 값을 하나씩 가져오는 방식이기 때문에 중간에 값을 지워도 예외가 발생하지 않는다.
Iterator.remove하면 반복자를 가져오면서 set의 값을 지운다.

public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("java");
        set.add("css");
        set.add("spring");
        set.add("jsp");
        set.add("html");

        // 향상된 for문 사용시 set이 변경되면
        // exception 발생
        /*
         * for (String e : set) { if (e.length() % 2 == 0) { set.remove(e); } }
         */

        Iterator<String> iter = set.iterator();
        while (iter.hasNext()) {
            String n = iter.next();

            if ((n.length() % 2) == 0) {
                iter.remove();
            }
        }
        System.out.println(set);
    }
}

15.5.2 RemoveIf()

향상된 for문을 사용하는 것은 위험한 일이고
Iterator를 사용하는 것은 코드가 너무 길다.
이를 해결하기 위해 Collection에 RemoveIf()라는 메소드가 있다.
Predicate라는 타입을 파라미터로 받는다.
Predicate는 함수형인터페이스로 test(T t)추상메소드를 가지고 있다.
boolean타입의 리턴값을 준다.
즉 RemoveIf메소드는 true가 나오면 값을 지우는 과정을 거치게 된다.

public class C03RemoveIf {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("java");
        set.add("css");
        set.add("spring");
        set.add("jsp");
        set.add("html");

        set.removeIf(e -> e.length() % 2 == 0);
        System.out.println(set);
    }
}

15.5.3 중복판별

HashSet은 중복을 어떻게 판별하나?
contains메소드를 활용하면 이미 set에 이미 원소가 있는지 없는지를 알 수 있다.

public class C04Contains {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("java");
        set.add("css");
        set.add("jsp");
        set.add("html");

        //contains : 해당 원소가 이미 있는지?
        System.out.println(set.contains("java")); //true
        System.out.println(set.contains("jsp")); //true
        System.out.println(set.contains("python")); //false
    }
}

Set은 hashcode와 equals메소드를 비교해서 동일한 객체인지 아닌지를 판별한다.
우리가 클래스를 만든다면 같다 틀리다를 우리가 정의 해줘야한다.

class Book {
    private String title;

    public Book(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    @Override
    public String toString() {
        return "Book [title=" + title + "]";
    }

    @Override
    public int hashCode() {
        return Objects.hash(title);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Book other = (Book) obj;
        return Objects.equals(title, other.title);
    }
}

public class C05Contains {
    public static void main(String[] args) {
        Set<Book> set = new HashSet<>();
        set.add(new Book("slamdunk"));
        set.add(new Book("avatar"));
        set.add(new Book("topgun"));

        System.out.println(set.size());

        System.out.println(set.contains(new Book("avatar"))); // true
    }
}

15.5.4 확인문제 8번

HashSet에 Student객체를 저장하려고한다. 학번이 같으면 동일한 Student라고 가정하고 중복저장이 되지 않도록 하고싶다.
해시코드는 학번이라고 가정하고 Student를 작성해보자.

public class Student {
    public int studentNum;
    public String name;

    public Student(int studentNum, String name) {
        this.studentNum = studentNum;
        this.name = name;
    }

    // 코드 작성
    @Override
    public int hashCode() {
        return studentNum;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Student target) {
            if (target.studentNum == studentNum) {
                return true;
            } else {
                return false;
            }
        }
        return false;
    }
}

public class HashSetExample {
    public static void main(String[] args) {
        Set<Student> set = new HashSet<>();

        set.add(new Student(1, "홍길동"));
        set.add(new Student(2, "신용권"));
        set.add(new Student(1, "조민우"));

        System.out.println("저장된 객체 수 : " + set.size()); //2
        for (Student s : set) {
            System.out.println(s.studentNum + ":" + s.name);
        }
    }
}

15.6 Map

Map은 저장되는 방식이 List, Set과 다르다.
List와 Set은 Collection인데 Map은 다르다.
Map은 하나의 아이템을 키와 밸류로 구분해서 저장한다.
이쌍을 entry라고 한다.
맵은 즉 entry<Key, Value>와 같이 저장한다.
Map은 중복된 키를 저장하지 않는다. 키는 최대 하나의 밸류에만 매핑된다.
다른언어에서는 딕셔너리라고 부르기도 한다. 단어-설명 이런느낌

이를 구현한 클래스는 HashMap 등등이 있고 메소드는 다음과 같다.

entry추가 : put(키, 값);
entry의 수 : size();
key로 value얻기 : get(키);
entry 지우기 : remove(키);

15.6.1 HashMap

public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();

        // entry 추가
        map.put("kang", "slamdunk");
        map.put("serly", "avatar");
        map.put("maverick", "topgun");

        // entry의 수
        int size = map.size();
        System.out.println(size); // 3

        map.put("kang", "baseball");
        System.out.println(map.size()); // 3

        // key로 value얻기
        String v1 = map.get("serly"); // avatar
        System.out.println(v1);
        System.out.println(map.get("kang")); // baseball

        // entry 지우기
        map.remove("kang");
        System.out.println(map.size()); // 2
    }
}

15.6.2 전체탐색

1.ketset
키의 특징이 set의 특징과 같다.
전체탐색을 위해서는 key로 set을 만들수 있다.
2.foreach
함수형 인터페이스를 구현해야하는데 키와 값을 각각 받을 수 있다.
3.entrySet
각 entry를 Set으로 받을 수 있다.

System.out.println("keySet사용 ###########");
Set<String> keys = map.keySet(); // 키 set얻기
for (String key : keys) {
    System.out.println(key + ":" + map.get(key));
}

System.out.println("foreach 메소드 $$$$$$$$$$");
map.forEach((k, v) -> System.out.println(k + ":" + v));

System.out.println("entrySet 메소드 ***********");
Set<Entry<String, String>> entries = map.entrySet();
for (Entry<String, String> entry : entries) {
    System.out.println(entry.getKey() + ":" + entry.getValue());
}

15.6.3 확인문제 9번

map에서 평균점수 최고점수 최고점수를 받은 아이디를 출력해보자.

public class MapExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("blue", 96);
        map.put("hong", 86);
        map.put("white", 92);

        String name = null;
        int maxScore = 0;
        int totalScore = 0;

        Set<String> keySet = map.keySet();
        for (String key : keySet) {
            int value = map.get(key);
            totalScore += value;
            if (value > maxScore) {
                maxScore = value;
                name = key;
            }
        }

        int avg = totalScore / map.size();
        System.out.println(avg); // 91
        System.out.println(maxScore); // 96
        System.out.println(name); // blue
    }
}

2023.03.15 후기

책은 컬렉션 다음 람다식이라 컬렉션에서 람다식을 사용할 생각을 못햇는데
컬렉션의 함수형인터페이스 파라미터들을 람다식으로 작성하는 것을 배우게 되었다.
스트림을 보면 각 인터페이스들의 파라미터와 구성방법이 나오는데 연장해서 생각하는 것을 못했다.