이것이 자바다 2일차 - Chapter 15 컬렉션 자료구조

Seo-Faper·2023년 1월 10일
0

이것이 자바다

목록 보기
3/20

컬렉션 프레임워크

자바는 각종 자료구조를 이용해서 객체들을 효율적으로 다룰 수 있는 여러 인터페이스와 클래스들을 제공하는데, 이를 컬렉션 프레임워크 라고 부른다.
주요 인터페이스는 List, Set, Map 등이 있다.

Collection 인터페이스에 List와 Set이 있으며 Collection과 별개로 존재하는 Map이 있다.

인터페이스 분류특징구현 클래스
List(Collection)순서를 유지하고 저장, 중복 저장 가능ArrayList, Vector, LinkedList
Set(Collection)순서를 유지하지 않고 저장, 중복 저장 불가HashSet, TreeSet
Map키와 값으로 구성된 엔트리 저장, 키는 중복 저장 불가HashMap, HashTable, TreeMap, Properties

List 컬렉션

List 컬렉션은 객체를 인덱스로 관리한다는 특징이 있다. 객체를 저장하면 인덱스가 부여되고 인덱스를 기준으로 탐색, 삭제 등을 수행한다.

List 컬렉션의 대표적인 클래스는 ArrayList, Vector, LinkedList가 있고 해당 클래스들이 공통적으로 사용할 수 있는 List 인터페이스 메소드는 다음과 같다.

객체 추가 메소드

메소드설명
boolean add(E e)주어진 객체를 맨 끝에 추가
void add(int index, E element)주어진 인덱스에 객체를 추가
set(int index, E element)주어진 인덱스의 객체를 새로운 객체로 바꿈

객체 검색 메소드

메소드설명
boolean Contains(Object o)주어진 객체가 저장되어 있는지 여부
E get(int index)주어진 인덱스에 저장되어된 객체를 리턴
boolean isEmpty() 컬렉션이 비어 있는지 조사

객체 삭제 메소드

메소드설명
void clear()저장된 모든 객체를 삭제
E remove(int index)주어진 인덱스에 저장되어된 객체를 삭제
boolean remove(Object o)주어진 객체를 삭제

그럼 이제부터 하나씩 알아보도록 하겠다.


ArrayList

ArrayList는 가장 자주 쓰는 컬렉션으로, 일반 배열과 다르게 크기 제한이 없다.
선언 할 때 미리 들어갈 객체를 지정할 수 있으며 별도로 지정하지 않을 시 모든 종류의 객체를 넣을 수 있다.

List<E> a = new ArrayList<E>(); // E 만 담을 수 있음
List b = new ArrayList(); // 모든 객체를 담을 수 있음

또한 ArrayList 중간에 값을 삽입하거나 삭제 할 수도 있다.
하지만 중간의 값을 삭제하거나 삽입 할 시 그 뒤의 모든 데이터를 뒤로 한칸씩 당기거나 밀기 때문에 알고리즘 포퍼먼스가 심하게 떨어진다. 그래서 리스트의 중간중간에 삭제/추가가 많이 일어날 때는 LinkedList가 적합하다.

package ch15.sec02.exam01;

public class Board {
    private String subject;
    private String content;
    private String writer;

    public Board(String subject, String content, String writer) {
        this.subject = subject;
        this.content = content;
        this.writer = writer;
    }

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getWriter() {
        return writer;
    }

    public void setWriter(String writer) {
        this.writer = writer;
    }
}

이렇게 Board 클래스를 하나 만들어 주고

package ch15.sec02.exam01;

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

public class ArrayListExample
{
    public static void main(String[] args) {
        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.println(board.getSubject()+"\t"+board.getContent()+"\t"+board.getWriter());

        for(int i = 0; i<list.size(); i++){
            Board b = list.get(2);
            System.out.println(b.getSubject()+"\t"+b.getContent()+"\t"+b.getWriter());
        }
        System.out.println();

        list.remove(2);
        list.remove(2);

        for(Board b: list){
            System.out.println(b.getSubject()+"\t"+b.getContent()+"\t"+b.getWriter());
        }
    }
}

이렇게 객체를 생성하고 삭제 / 추가 / 순회를 할 수 있다.


Vector

벡터(Vector)는 ArrayList와 거의 비슷하다. 다 똑같은데 다른 점은 동기화된 메소드로 구성되어 있다는 점이다. 대량의 트래픽을 처리 할 때 충돌이 일어나 데이터가 유실되는걸 막기 위해 멀티 스레드가 동시에 Vector() 메소드를 실행 할 수 없게 설정되어 있다. 그래서 멀티 스레드 환경에서 안전하게 객체를 추가 또는 삭제할 수 있다는 특징이 있다.

package ch15.sec02.exam02;

import ch15.sec02.exam01.Board;

import java.util.List;
import java.util.Vector;

public class VectorExample
{
    public static void main(String[] args) {
        List<Board> list = new Vector<>();
        Thread threadA = new Thread(){
            @Override
            public void run() {
                for(int i = 1; i<=1000; i++){
                    list.add(new Board("제목"+i, "내용"+i,"글쓴이"+i));
                }
            }
        };

        Thread threadB = new Thread(){
            @Override
            public void run() {
                for(int i = 1001; i<=2000; i++){
                    list.add(new Board("제목"+i, "내용"+i,"글쓴이"+i));
                }
            }
        };

        threadA.start();
        threadB.start();

        try{
            threadA.join();
            threadB.join();
        }catch (Exception e){

        }
        int size = list.size();
        System.out.println("총 객체 수: "+size);
        System.out.println();
    }
}

이렇게 Vector를 쓰면 멀티 스레드가 충돌하지 않아 정확히 2000개가 찍힌다.
그러나 list를 ArrayList로 변경하면 데이터가 충돌해 에러가 나거나 2000개가 나오지 않는 모습을 볼 수 있다.


LinkedList

LinkedList는 사용법은 ArrayList와 동일하지만 내부 구조가 다르다.
ArrayList는 내부 배열에 모든 인덱스와 객체를 매칭하지만
LinkedList는 인접 객체를 near와 head로 연결해서 관리한다.
그래서 삭제나 삽입이 일어나면 near와 head의 주소를 바꿔 링크를 수정해 체인을 끊었다가 새로운 객체를 각각 앞과 뒤에 연결하는 식으로 삽입이 이루어 진다.
그래서 ArrayList에 비교했을 때 데이터 삽입이나 삭제는 압도적으로 빠르고 좋다.
하지만 LinkedList에도 치명적인 단점이 존재한다. 바로 인덱스가 따로 없기 때문에 특정 인덱스의 값을 찾기 위해선 그만큼 순회를 해야 한다.
즉, ArrayList는 배열과 유사하기 때문에 List.get(10000)을 했을 때 O(1)O(1)의 시간복잡도로 인덱스에 매칭되는 객체를 가져오지만 LinkedList는 동일한 작업을 수행했을 때 앞에서 부터 계속 다음 링크로 넘어가며 총 O(n)O(n) 만큼 걸린다. 어떤 용도로 리스트를 만들지가 알고리즘의 포퍼먼스를 크게 결정짓는다.


Set 켤렉션

저장순서를 유지하는 List에 반해 Set은 저장 순서가 유지되지 않는다.
또한 객체를 중복해서 저장 할 수 없다. Set은 수학에서 집합과 유사하다.
집합도 순서 상관없이 중복이 허용되지 않기 때문이다.
이 Set 은 HashSet, LinkedHashSet, TreeSet이 대표적이며 Set 인터페이스에서 공통적으로 사용 할 수 있는 메소드는 다음과 같다.

객체 추가 메소드

메소드기능
boolean add(E e)주어진 객체를 성공적으로 저장하면 true를 리턴하고 중복 객체면 false를 리턴

set은 중복해서 데이터를 저장 할 수 없기 때문이다.

객체 검색 메소드

메소드설명
boolean Contains(Object o)주어진 객체가 저장되어 있는지 여부
boolean isEmpty() 컬렉션이 비어 있는지 조사
Iterator<E> iterator()저장된 객체를 한 번씩 가져오는 반복자 리턴
int size()저장되어 있는 전체 객체 수 리턴

객체 삭제 메소드

메소드설명
void clear()저장된 모든 객체를 삭제
boolean remove(Object o)주어진 객체를 삭제

HashSet

Set 컬렉션 중에서 가장 많이 쓰는 클래스이다.
ArrayList와 동일하게 타입을 정할 수도 있고 정하지 않고 모든 객체를 받을 수도 있다.

HashSet에서 동일한 객체로 인식하는 기준은 HashCode(),equals()의 리턴값이 모두 true면 중복으로 판단해 저장하지 않는다.

  		Set<String> set = new HashSet<String>();
  
        set.add("Java");
        set.add("JDBC");
        set.add("JSP");
        set.add("Java"); // 저장 하지 않음
        set.add("Spring");

이렇게 equals로 비교했을 때 같은 String은 동일한 객체로 취급한다.
그래서 객체를 HashSet으로 다룰 때는 반드시 Override를 통해 기준을 만들어 줘야 한다.

package ch15.sec03.exam02;

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

    @Override
    public int hashCode() {
        return name.hashCode() + age;
    }

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

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

이렇게 Member 클래스에 대한 equals와 hashCode의 기준을 바꿔주었다.

package ch15.sec03.exam02;

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

public class HashSetExample
{
    public static void main(String[] args) {
        Set<Member> set = new HashSet<Member>();
        set.add(new Member("홍길동",30));
        set.add(new Member("홍길동",30));
        int size = set.size();
        System.out.println("총 객체 수: "+size);
    }
}

그 후 이렇게HashSet을 만들면 인스턴스는 다르지만 사전에 미리 정의해 놓은 기준에 의해 중복으로 인식되어 객체 1개만 저장된다.

또한 Set컬렉션은 인덱스로 객체를 검색해서 찾는 메소드가 없다. 대신 한 객체씩 반복해서 가져오는 방법이 있다. for문을 쓰던지 반복자인 iterator() 메소드를 쓰는 방법이다.

//for문
Set<E> set = new HashSet<>();
for(E e : set){ ... }
//iterator() 반복자 사용
Set<E> set = new HashSet<>();
Iterator<E> i = set.iterator();

while(i.hasNext()){
	E e = i.next();
}

Map 컬렉션

Map 컬렉션은 key와 value가 쌍으로 이루어진 엔트리 객체를 저장한다. 여기서 key와 value는 모두 객체가 들어갈 수 있다. key는 중복 저장 할 수 없지만 value는 중복이 가능 하다.
중복된 key가 들어올 경우 덮어써진다.

Map에는 HashMap, Hashtable,LinkedHashMap,Properites,TreeMap 등이 있으며
공통적으로 쓸 수 있는 메소드는 다음과 같다.

객체 추가 메소드

메소드설명
V put(K key, V value)주어진 키와 값을 추가, 저장이 되면 값을 리턴

객체 검색

메소드설명
boolean ContainKey(Object key)주어진 key가 있는지 여부
boolean ContainValue(Object value) 주어진 value가 있는지 여부
Set<Map.Entry<K,V>> entrySet()key와 value의 쌍으로 구성된 모든 Map.Entry 객체를 Set에 담아서 리턴
V get(Object key)주어진 키의 값을 리턴
boolean isEmpty() 컬렉션이 비어 있는지 조사
Set<K> keySet()모든 키를 Set 객체에 담아서 리턴
int size()저장되어 있는 전체 객체 수 리턴
Collection<V> values저장된 모든 값 Collection에 담아서 리턴

객체 삭제 메소드

메소드설명
void clear()모든 Map.Entry(key와 value)를 삭제
boolean remove(Object key)주어진 키와 일치하는 Map.Entry삭제, 삭제가 되면 값을 리턴

HashMap

HashMap역시 HashSet과 동일하게 HashCode(),equals()의 리턴값이 모두 true 이면 동일한 키로 보고 중복 저장을 허용하지 않고 덮어써진다.

HashMap의 생성 방법은 다음과 같다.

Map<K, V> map = new HashMap<K, V>();

쉼표로 구분해서 key와 value를 넣는다.

		Map<String, Integer> map = new HashMap<>();

        map.put("신용권",85);
        map.put("홍길동",90);
        map.put("동장군",80);
        map.put("홍길동",95); // 동일한 key 때문에 덮어써짐
        System.out.println("총 Entry 수 : "+map.size());
        System.out.println();

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

        //키 Set 컬렉션을 얻고 반복해서 키와 값을 얻기
        Set<String> keySet = map.keySet();
        Iterator<String> keyIterator = keySet.iterator();
        while (keyIterator.hasNext()){
            String k = keyIterator.next();
            Integer v = map.get(key);
            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();

HashTable

HashTable은 HashMap과 동일하지만 또 ArrayList와 Vector의 차이점 처럼 안전하게 데이터를 넣기 위해 멀티스레드 환경에서 쓸 수 없다는 것이다.


Properties

Prperies는 Hashtable의 자식 클래스이기 때문에 Hashtable의 특징을 그대로 가지고 있다. Properties는 key와 value의 값을 String 타입으로 제한한 컬렉션이다. 주로 확장자가 *.properties인 프로퍼티 파일을 읽을 때 사용된다.
javascript의 json과 비슷한 역할을 한다고 보면 된다.

driver=oracle.jdbc.OracleDriver
url=jdbc:oracle:thin:@localhost:1521:orc
username=scott
password=tiger
admin=\uD64D\uD64D\uD64D

한글을 저장할 때는 \u+ 유니코드로 표현된다.
Properties를 이용해 해당 파일을 코드에서 쉽게 읽을 수 있다. 객체를 생성하고 load() 메소드로 내용을 메모리에 로드한다.

        Properties properties = new Properties();

        properties.load(PropertiesExample.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);

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

컬렉션 프레임워크 중에서도 검색 기능을 더욱 강화시킨 클래스들이 있다.
바로 TreeSet과 TreeHash다.


TreeSet

TreeSet은 이진트리를 기반으로 만들어진 Set 컬렉션이다.
이진트리는 간단히 말해 하나의 부모 노드에서 좌 우로 두개의 자식노드를 생성 할 수 있는 트리 구조이다.
이진트리에 객체를 저장하면 부모노드의 객체와 비교해 낮은 것은 왼쪽, 높은 곳은 오른쪽의 자식노드로 들어간다.

TreeSet에서 사용 할 수 있는 메소드는 다음과 같다.

리턴타입메소드설명
Efirst()제일 낮은 객체를 리턴
Elast()제일 높은 객체를 리턴
Elower(E e)주어진 객체보다 바로 아래 객체를 리턴
Ehigher(E e)주어진 객체의 바로 위 객체를 리턴
Efloor(E e)주어진 객체와 동등한 객체가 있으면 리턴, 만약 없다면 주어진 객체의 바로 아래의 객체를 리턴
Eceiling(E e)주어진 객체와 동등한 객체가 있으면 리턴, 만약 없다면 주어진 객체의 바로 위의 객체를 리턴
EpollFirst()제일 낮은 객체를 꺼내오고 컬렉션에서 제거
EpollLast()제일 높은 객체를 꺼내오고 컬렉션에서 제거
Iterator<E>descendingIterator()내림차순으로 정렬된 Iterator를 리턴
NavgableSet<E>descendingSet()내림차순으로 정렬된 NavigableSet을 리턴
NavigableSet<E>headSet(E toElement, boolean inclusive)주어진 객체보다 낮은 객체들은 NavigableSet으로 리턴, 주어진 객체 포함 여부는 두 번째 매개값에 따라 달라짐
NavigableSet<E>tailSet(E toElement, boolean inclusive)주어진 객체보다 높은 객체들은 NavigableSet으로 리턴, 주어진 객체 포함 여부는 두 번째 매개값에 따라 달라짐
NavigableSet<E>subSet(E toElement, boolean fromInclusive,E toElement, boolean toInclusive)시작과 끝으로 주어진 객체 사이의 객체들은 NavigableSet으로 리턴. 시작과 끝 객체의 포함 여부는 두 번째, 네 번째 매개값에 따라 달라짐

이런 식으로 쓸 수 있다.

 TreeSet<Integer> scores = new TreeSet<>();

        scores.add(87);
        scores.add(98);
        scores.add(75);
        scores.add(95);
        scores.add(80);

        for(Integer s : scores){
            System.out.print(s+" ");
        }
        System.out.println("\n");

        //특정 Integer 객체를 가져오기
        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("\n");

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

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

TreeMap

이것도 TreeSet랑 동일하게 이진트리를 기반으로 만들었다. 차이점은 key와 value가 저장된 Entry를 저장한다는 것이다. TreeMap에서는 key를 기준으로 비교해서 부모의 key보다 낮으면 왼쪽, 높은 것은 오른쪽 자식 노드에 Entry 객체를 저장한다.

  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);

        //정렬된 엔트리를 하나씩 가져오기
        Set<Entry<String, Integer>> entrySet = treeMap.entrySet();
        for(Entry<String, Integer> entry : entrySet){
            System.out.println(entry.getKey()+" - "+entry.getValue());
        }
        System.out.println();

        //특정 key에 대한 value 가져오기
        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>> decendingSet = descendingMap.entrySet();
        for(Entry<String ,Integer> e : decendingSet){
            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);
        for(Entry<String ,Integer> e : rangeMap.entrySet()){
            System.out.println(e.getKey()+" - "+e.getValue());
        }
        System.out.println();

이런 식으로 사용 할 수 있다.


Comparable과 Comparator

3장에서 언급한 내용이지만 TreeSet에 저장되는 객체와 TreeMap에 저장되는 키 객체는 저장과 동시에 오름차순으로 정렬되는데, 이는 어떤 객체건 자동으로 되는게 아니라 Integer, String 등은 기본적으로 Comparable을 구현하고 있기 때문이다. 따라서 사용자 정의 클래스를 TreeMap이나 TreeSet에 저장하려면 반드시 Comparable을 구현해 줘야 한다.

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;
    }
}

이런 식으로 사용자 정의 객체를 만들고 이것을 TreeSet에 넣으려면 comapreTo 함수를 다시 만들어줘야 한다.

        TreeSet<Person> treeSet = new TreeSet<Person>();

        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);
        }

이렇게 하면 사용자 정의 객체도 정렬되서 나온다.

하지만 비교 기능이 있는 Comparable 구현 객체를 꼭 만들어야 하는 것은 아니다.
TreeSet과 TreeMap을 생성 할 때 비교자를 다음과 같이 비교자를 제공하면 비교기능이 없는 Comparable 객체를 만들 수 있다.

TreeSet<E> treeSet = new TreeSet<E> ( new ComparatorImpl() );

TreeMap<K,V> treeMap = new TreeMap<K,V> ( new ComparatorImpl() );

LIFO와 FIFO 컬렉션

말 그대로 Stack과 Queue에 대한 이야기이다.

public class Coin
{
    private int value;

    public Coin(int value){
        this.value = value;
    }
    public int getValue(){
        return value;
    }
}
        Stack<Coin> coinBox = new Stack<Coin>();

        coinBox.push(new Coin(100));
        coinBox.push(new Coin(50));
        coinBox.push(new Coin(500));
        coinBox.push(new Coin(10));

        while (!coinBox.isEmpty()){
            Coin coin = coinBox.pop();
            System.out.println("꺼낸 동전 : "+coin.getValue()+"원");
        }

스택은 후입선출, 큐는 선입 선출이다.

public class Message
{
    public String command;
    public String to;

    public Message(String command, String to) {
        this.command = command;
        this.to = to;
    }
}
        Queue<Message> messageQueue = new LinkedList<>();
        messageQueue.offer(new Message("sendMail","홍길동"));
        messageQueue.offer(new Message("sendSMS","신용권"));
        messageQueue.offer(new Message("sendKakaotalk","감자바"));

        while (!messageQueue.isEmpty()){
            Message message = messageQueue.poll();
            switch (message.command){
                case "sendMail":
                    System.out.println(message.to+"님에게 메일을 보냅니다.");
                    break;
                case "sendSMS":
                    System.out.println(message.to+"님에게 SMS를 보냅니다.");
                    break;
                case "sendKakaotalk":
                    System.out.println(message.to+"님에게 카카오톡을 보냅니다.");
                    break;
            }
        }

동기화된 컬렉션

컬렉션 프레임워크는 대부분 싱글 스레드 기준으로 만들었다.
하지만 때에 따라 멀티스레드를 쓸 수도 있는데, 이 때 의도치 않게 요소가 변경될 수 있다.
Vector와 Hashtable은 안전하게 보호하는 기능이 있지만 나머지들은 불안전하기에 이를 대비해 synchronizedXXX() 메소드를 제공한다.

        Map<Integer, String> map = Collections.synchronizedMap(new HashMap<>());

        Thread threadA = new Thread(){
            @Override
            public void run() {
                for(int i = 1; i<=1000; i++){
                    map.put(i,"내용"+i);
                }
            }
        };
        Thread threadB = new Thread(){
            @Override
            public void run() {
                for(int i = 1001; i<=2000; i++){
                    map.put(i,"내용"+i);
                }
            }
        };

        threadA.start();
        threadB.start();

        try{
            threadA.join();
            threadB.join();
        } catch(Exception e){

        }
        int size = map.size();
        System.out.println("총 객체 수 : "+size);

수정할 수 없는 컬렉션

수정할 수 없는(unmodifiable) 컬렉션이란, 요소를 추가하거나 삭제할 수 없는 컬렉션을 말한다.
컬렉션 생성 시 기본값을 변경하고 싶지 않을 때 유용하다. 주로 배열이나 컬렉션을 복사할 때 사용한다.

    	//List 불변 컬렉션 생성
        List<String> immutableList1 = List.of("A","B","C");

        //Set 불변 컬렉션 생성
        Set<String> immutableSet1 = Set.of("A","B","C");

        Map<Integer, String > immutableMap1 = Map.of(
          1, "A",
          2, "B",
          3,"C"
        );
        // List 컬렉션을 불변 컬렉션으로 복사
        List<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");
        List<String> immutableList2 = List.copyOf(list);

        // Set 컬렉션을 불변 컬렉션으로 복사
        Set<String> set = new HashSet<>();
        set.add("A");
        set.add("B");
        set.add("C");
        Set<String> immutableSet2 = Set.copyOf(set);

        // Map 컬렉션을 불변 컬렉션으로 복사
        Map<Integer,String> map = new HashMap<>();
        map.put(1,"A");
        map.put(2,"B");
        map.put(3,"C");
        Map<Integer,String> immutableMap2 = Map.copyOf(map);

        //배열로부터 List 불변 컬렉션 생성
        String[] arr = {"A","B","C"};
        List<String> immutableList3 = Arrays.asList(arr);

연습문제

4번, Stack은 FILO (후입선출)입니다.

3번 배열과 달리 모든 값이 앞으로 땡겨옵니다.

4번 null도 딱 하나 저장 할 순 있긴 합니다.

3번 HashTable이 더 안젼하죠 멀티 스레드를 지원하지 않으니까..

List<Board> 변수 = new ArrayList<>(); 

저장 순서는 인덱스를 말하는 거겠지? 그럼 List컬렉션의 ArrayList를 쓰면 되겠네요.
LinkedList는 인덱스가 없기 때문에 정답이 아닙니다.

Map<String, Integer> 변수 = new HashMap<>();

import java.util.*;
class BoardDao{

	public List<Board> list;
    
    public ArrayList getBoardList(){
    
        list = new ArrayList<>();
        
        for(int i = 1; i<=3; i++){
        	Board b = new Board("제목"+i,"내용"+i);
            list.add(b);
        }
        
        return(list);
    }
}

머 이런 식으로 작동하지 않을까!


    @Override
    public int hashCode() {
        return studentNum;
    }

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

HashSet의 중복 기준은 equals와 hashCode에서 정해주면 되지요.

		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();
            if(v > maxScore){
                name = k;
                maxScore = v;
            }
            totalScore += v;
        }
        System.out.println(name+", "+maxScore+", "+totalScore/3);

반복자를 이용해서 순회해 주면 됩니다.
처음에 value 기준으로 정렬하는 줄 알고 식겁했는데 최대값만이였네요.

자동 정렬은 Comparator를 만들어 줘야 합니다.

implements Comparable<Student>

를 먼저 넣어주고

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

compareTo(T o) 메소드의 기준을 생각해서 내림차순 해주겠습니다.

4번입니다. 클래스 타입이 아니라 인터페이스고 Queue같은 경우는 LinkedList 클래스를 이용해 사용합니다.

3번 HashMap이 아니라 HashTable이 동기화된 컬렉션입니다.

4번 asList()는 배열로부터 불변 컬렉션을 생성합니다.

profile
gotta go fast

0개의 댓글