TreeSet과 TreeMap은 계층 구조를 활용해서 검색 기능을 강화시킨 컬렉션입니다.

이 컬렉션에 나오는 자료구조인 이진 트리의 내부 구조를 알고 있어야 API를 사용할 수 있습니다.

간단히 개념만 설명하면, 루트 노드를 정하고, 대소 비교를 통해 크면 오른쪽, 작으면 왼쪽으로 비교하며 위치하게 됩니다.

 

SQL에서 SELECT문을 통한 JOIN이나 복잡한 쿼리로 데이터를 조회할 때, 인덱스를 이용합니다.

이 때, 이진 트리의 구조와 개념을 제대로 알고 사용하면 많은 도움이 될 수 있습니다.

 

 


 

1. 트리 셋( TreeSet )

TreeSet은 이진 트리(binary tree)를 기반으로 한 Set 컬렉션입니다.

좌, 우 자식 노드를 참조하기 위한 두 개의 변수로 구성됩니다.

 

 

다음은 주요 메서드입니다.

 

  • 특정 객체를 찾는 메서드 : fist(), last(), lower(), higher() 등
  • 정렬 메서드 : descendingiterator(), descendingSet()
  • 범위 검색 메서드 : headSet(), tailSet(), subSet()

 

숫자에 대한 TreeSet은 간단하므로, 다루지 않고 String으로 넘어가겠습니다.

다음은 간단한 예시 코드입니다.

 

@Log4j2
public class TreeSetExample {

	
    public static void main(String[] args) {
        TreeSet<String> treeSet = new TreeSet<>();
		
        treeSet.add("apple");
        treeSet.add("forever");
        treeSet.add("description");
        treeSet.add("ever");
        treeSet.add("zoo");
        treeSet.add("base");
        treeSet.add("guess");
        treeSet.add("cherry");
		
        log.info("**** [c~f 사이의 단어 검색] ****");
		
        // 부분범위(Sub-range Search) 검색 수행
        NavigableSet<String> rangeSet = treeSet.subSet("cherry", true, "forever", true);
        rangeSet.forEach(log::info);
		
    } // main
	
} // end class

 

subSet에 boolean은 해당 문자열을 포함할 것인지를 결정하게 합니다.

문자열의 맨처음 문자만 넣으면 boolean의 사용의미가 없어지므로, 반드시 문자열을 넣어야 합니다.

 

 

 

1. 트리 맵( TreeMap )

TreeMap은 이진 트리(binary Tree)를 기반으로 한 Map 컬렉션입니다.

키와 값이 저장된 Map.Entry를 저장하며 좌, 우 자식 노드를 참조하기 위한 두 개의 변수로 구성됩니다.

 

다음은 주요 메서드입니다.

 

  • 특정 객체를 찾는 메서드 : fistEntry(), lastEntry(), lowerEntry(), higherEntry() 등
  • 정렬 메서드 : descendingKeySet(), descendingMap()
  • 범위 검색 메서드 : headMap(), tailMap(), subMap()

 

TreeMap은 TreeSet과 비슷한 구성을 가지고 있으므로 따로 다루지 않겠습니다.

 

 

 

 

'Java > Collection Framework' 카테고리의 다른 글

맵 컬렉션  (0) 2023.08.03
셋 컬렉션  (0) 2023.08.03
리스트 컬렉션  (0) 2023.08.02
컬렉션 프레임워크 개요  (0) 2023.08.02

 

 

 

Map 컬렉션은 키(key)와 값(value)으로 구성된 Map 엔트리 객체를 저장하는 구조입니다.

구현 클래스는 'HashMap', 'HashTable', 'Properties' 등이 있습니다.

 


 

Map 컬렉션의 특징 및 주요 메서드입니다

 

  • V put(K key, V value) : 주어진 키와 값을 추가, 저장이 되면 값을 리턴

  • boolean containKey(Object key) : 주어진 키가 있는지 여부
  • boolean contains Value(Object value) : 주어진 값이 있는지 여부
  • Set<Map.Entry><K,V>> entrySet() : 키와 값의 쌍으로 구성된 모든 Map.Entry 객체를 Set에 담아 리턴
  • V get(Object key) : 주어진 키의 값을 리턴
  • boolean isEmpty() : 컬렉션이 비어있는지 여부
  • Set<K> keySet() : 모든 키를 Set 객체에 담아서 리턴
  • int size() : 저장된 키의 총 수를 리턴
  • Collection<V> values() : 저장된 모든 값 Collection에 담아서 리턴
  • void clear() : 모든 Map.Entry를 삭제
  • V remove(Object key) : 주어진 키와 일치하는 Map.Entry 삭제, 삭제가 되면 값을 리턴

 


 

 

1. 해시 맵( HashMap)

일반적으로 HashMap 보다는 HashTable을 많이 사용합니다.

map은 set과 다르게 키가 같다면, 기존 요소를 교체합니다.

 

먼저 Student 클래스입니다.

 

@AllArgsConstructor
@EqualsAndHashCode
public class Student {
    public Integer sno;	// 학번
    public String name;	// 이름

} // end class

 

Lombok의 어노테이션인 @EqualsAndHashCode를 이용해 메서드를 오버라이드하지 않고, 코드를 구현하였습니다.

 

 

다음은 실행 클래스입니다. 

 

@Log4j2
public class HashMapExample {

	
    public static void main(String[] args) {
        // Map 컬렉션 생성
        @Cleanup("clear")
        Map<String, Integer> map = new HashMap<String, Integer>();
		
        map.put("신용권", 85);
        map.put("홍길동", 90);
        map.put("동장군", 80);
        map.put("홍길동", 95);
		
        log.info("총 엔트리 수 : {}", map.size());
        log.info(map);
		
        // 요소 객체 찾기
        log.info("홍길동 : {}", map.get("홍길동"));
		
        // Map 컬렉션 순회(Traverse)
        Set<String> keySet = map.keySet();
        Iterator<String> keyIterator = keySet.iterator();
		
        while(keyIterator.hasNext()) {
            String key = keyIterator.next();
            Integer value = map.get(key);
			
            log.info("\t + key : {}, value : {}", key, value);
        } // while
		
    } // main
	
} // end class

 

 

 

2. 해시 테이블( HashTable)

키 객체를 만드는 법은 HashMap과 동일합니다.

HashTable은 스레드 동기화(synchronization)가 된 상태입니다.

따라서, 멀티 스레드가 동시에 HashTable에 접근해서 객체를 추가, 삭제를 하더라도 thread-safe합니다.

 

 

 

3. 프로퍼티스( Properties, 속성 )

Properties는 키와 값을 String 타입으로 제한한 Map 컬렉션입니다.

Properties는 프로퍼티 파일(~.properties) 파일을 읽어 들일 때 주로 사용합니다.

이러한 프로퍼티 파일을 Resource Buncle(리소스 번들)이라고 합니다.

 

리소스 번들은 유지 보수를 편리하게 만들어 주고, ISQ-8859-1 문자셋으로 저장됩니다.

리소스 번들은 다국어를 고려하지 않고 만들었기 때문에, 한글은 유니코드(Unicode)로 변환되어 저장합니다.

 

 

간단한 리소스 번들입니다.

 

#database.properties
driver = oracle.jdbc.OracleDriver
url = jdbc:oracle:this:@localhost:1521:orcl
username = scott
password = tiger

# Set Your Name
yourname = \uD64D\uAE38\uB3D9

 

위의 리소스 번들을 기반으로 실행 코드를 만들었습니다.

 

@Log4j2
public class PropertiesExample {

	
    public static void main(String[] args) throws Exception {
        // Step.1   리소스 번들을 사용할 Map 객체릀 생성
        Properties properties = new Properties();
		
        // Step.2   실행 클래스 위치를 기준으로, 리소스 번들의 경로를
        //          지정해서 완전한 절대경로를 얻음
        String path = 
                PropertiesExample.
                class.
                getResource("/database.properties").
                getPath();
		
        log.info("1. path : {}", path);
		
        // ---------------------------------------------
		
        // Step.3   URL Encoding에 대한 URL Encoding 수행
        //          위에서 얻은 리소스 번들에는 다국어 문자가
        //          포함되어 있을 수도 있으므로, 이러한 경우
        //          다국어 문자가 포함된 경로를 읽어 들이는데 문제가 없도록
        //          URL Decoding을 수행해야 함
        path = URLDecoder.decode(path, "utf-8");
        log.info("2. path : {}", path);
		
        // ---------------------------------------------
		
        // Step.4 Step.3에서 얻어낸 완전한 경로를 Properties의
        // load 메서드에 지정해서 읽어들이게 함
        properties.load(new FileReader(path));
        log.info("2. properties : {}", properties);
		
        // --------------------------------------------
		
        // Step.5   리소스 번들이 이제 Map객체의 요소가 되었기 때문에
        //          각 항목의 값을 얻어내고 싶을 때, getProperty(key) 메서드 사용
        String driver = properties.getProperty("driver");
        String url = properties.getProperty("url");
        String username = properties.getProperty("username");
        String password = properties.getProperty("password");
        		
		
		
    } // main

} // end class

 

위 코드에서 URL Encoding은 웹 브라우저가 인식 가능한 부호화된 문자열입니다.

이러한 URL Encoding을 사람이 읽을 수 있도록 역변환하는 것이 URL Decoding이며

코드에서는 URLDecoder.decode()로 진행합니다.

 

properties.load()는 path를 기반으로 리소스 번들을 읽어들입니다.

 

 

먼저 path1, path2, properties2 실행결과를 보면 다음과 같습니다.

 

path1, path2, properties2

 

리소스 번들 주소를 설정할 때, 소스 폴더는 패키지와 다르게 물리적인 폴더로서 인식되지 않습니다.

루트(타겟)에서 패키지 폴더를 반영해서 리소스 번들을 지정하면 URL을 반환합니다.

루트에 가려면 코드처럼 백슬래시(/)를 앞에 붙이면 됩니다.

 

 

간단한 예시입니다.

 

  • 프로젝트 폴더이름 : example
  • 리소스 번들 이름 : database.properties
  • 리소스 번들 위치 : src(소스 폴더) / A / B / C
  • 리소스 번들 주소 : "/ A / B / C / database.properties "

 

 

 

 

 

 

 

 

'Java > Collection Framework' 카테고리의 다른 글

트리 컬렉션  (0) 2023.08.04
셋 컬렉션  (0) 2023.08.03
리스트 컬렉션  (0) 2023.08.02
컬렉션 프레임워크 개요  (0) 2023.08.02

 

 

 

Set 컬렉션은 저장 순서가 유지되지 않으며, 객체의 중복 저장이 불가능합니다.

구현 클래스는 'HashSet', 'LinkedHashSet', 'TreeSet'이 있습니다.

 


Set 컬렉션에서 사용되는 주요 메서드는 다음과 같습니다.

 

 

  • boolean add(E e) : 주어진 객체를 저장하고 저장이 성공하면 ture, 아니면 false를 리턴

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

  • void clear() : 저장된 모든 객체를 삭제
  • boolean remove(Object o) : 주어진 객체를 삭제

 


 


 

해쉬 셋( HashSet )

HashSet은 동일 객체나 동등 객체는 중복 저장하지 않습니다.

해쉬코드는 논리적인 주소가 동일하게 나올 수 있기 때문에, 해쉬코드가 동일하다면 재정의한 eqauls() 메서드를 통해 비교합니다.

 

@Log4j2
@AllArgsConstructor
public class Member {
    public String name;
    public Integer age;
	
	
    // 중복 판정을 위해서 Override(재정의)
    @Override
    public boolean equals(Object obj) {
        log.trace("equals({}) invoked.", obj);
		 
        if(obj instanceof Member member) {
            // Member member = (Member) obj;
            return member.name.equals(name) && (member.age == age);
        } else {
            return false;
        } // if-else
		
    } // equals 
	
    @Override
    public int hashCode() {
        log.trace("hashCode({}) invoked.");
        return name.hashCode() + age;
    } // hashCode
	
} // end class

 

해쉬코드는 동일할 수 있기 때문에,동일 객체의 여부를 판단할 수 있는 알고리즘을 가지고 메서드를 오버라이드해야 합니다.

 

 

다음은 실행클래스입니다.

 

@Log4j2
public class HashSetExample {
	

    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
		
        // 요소 객체 추가
        set.add("Java");
        set.add("JDBC");
        set.add("Servlet/JSP");
        set.add("Java");
        set.add("iBATIS");
		
        // 객체의 크기 반환
        int size = set.size();
        log.info("총 객체 수 : {}", size);
		
        // 객체들을 순회(Traverse)
        // Iterator<E> 객체를 필요로 한다
        Iterator<String> iterator = set.iterator(); // ---- 1
        while(iterator.hasNext()) {                 // ---- 2
            String element = iterator.next();       // ---- 3
            log.info("element : {}", element);
        } // while
		
        System.out.println(); // 개행
		
        // ------------------------------------------
		
        // 두 번째 방법 - Enhanced for
        for(String element : set) {
            log.info("element : {}", element);
        } // Enhanced for
		
		
        // 세 번째 방법 - forEach(Consume<E>) 메서드로 순회
        set.forEach(log::info);
		
        // ·
        // ·
        // ·
        // ·
		
    } // main

} // end class

 

위 코드처럼, 다양한 순회 방법이 있습니다.

만약, HashSet에 같은 필드 정보의 2개의 객체가 add() 메서드로 추가된다면

첫 번째 객체만 추가되고, 두 번째로 add 시도되는 객체는 추가되지 않습니다.

 

 

 

목적에 맞게 컬렉션을 다 사용한 후에는, 반드시 GC가 빠르게 될 수 있도록 clear 해야 합니다.

 

        // List, Set, Map 모두 추가/삭제/변경시에, 중복 판정 알고리즘을 통해 
        // 추가/삭제/변경 가능한 존재한 요소 객체인지 알 수 있음
        set.remove("JDBC");
        set.remove("iBATIS");
		
        log.info("총 객체 수 : {}", set.size());
		
        for(String element : set) {
			log.info("\t{}", element);
        } // enhanced for
		
        // 컬렉션의 모든 요소를 clear 시킴
        set.clear();
		
        // 확인
        if(set.isEmpty()) {
            log.info("비어 있음");
        } // if
        
    } // main
    
} // end class

 

Lombok으로 더 간단하게 코드를 구성할 수 있습니다.

 

       @Cleanup("clear")
       
        // ·
        // ·
        // ·
        // ·
        
        
        
        // List, Set, Map 모두 추가/삭제/변경시에, 중복 판정 알고리즘을 통해 
        // 추가/삭제/변경 가능한 존재한 요소 객체인지 알 수 있음
        set.remove("JDBC");
        set.remove("iBATIS");
		
        log.info("총 객체 수 : {}", set.size());
		
        log.info(set)
        // log.info(set)으로 대체
        // for(String element : set) {
		//	log.info("\t{}", element);
        // } // enhanced for
		
        // 컬렉션의 모든 요소를 clear 시킴
        // set.clear(); // @Cleanup("clear") annotation으로 대체
		
        // 확인
        if(set.isEmpty()) {
            log.info("비어 있음");
        } // if
        
    } // main
    
} // end class

 

위의 코드처럼 Lombok의 어노테이션을 이용하면 간결하고 가독성 좋은 코드를 구성할 수 있습니다.

 

 

 

 

'Java > Collection Framework' 카테고리의 다른 글

트리 컬렉션  (0) 2023.08.04
맵 컬렉션  (0) 2023.08.03
리스트 컬렉션  (0) 2023.08.02
컬렉션 프레임워크 개요  (0) 2023.08.02

 

 

 

List 컬렉션은 인덱스로 관리하고 중복해서 객체 저장이 가능합니다.

구현 클래스는 'ArrayList', 'Vector', 'LinkedList'가 있습니다.

 


 

List 컬렉션에서 사용되는 주요 메서드는 다음과 같습니다.

 

 

  • boolean add(E e) : 주어진 객체를 맨 끝에 추가
  • void add(int index, E element) : 주어진 인덱스에 객체를 추가
  • set(int index, E element) : 주어진 인덱스에 저장된 객체를 주어진 객체로 변경

  • boolean contains(Object o) : 주어진 객체가 저장되어 있는지의 여부
  • get(int index) : 주어진 인덱스에 저장된 객체를 리턴
  • isEmpty() : 컬렉션이 비어 있는지 확인
  • int size() : 저장되어있는 젠체 객체 수를 리턴

  • void clear() : 저장된 모든 객체를 삭제
  • remove(int index) : 주어진 인덱스에 저장된 객체를 삭제
  • boolean remove(Object o) : 주어진 객체를 삭제

 


 


 

1. 어레이리스트( ArrayList )

ArrayList는 순서를 보장하고, 중복을 허용하고, 인덱스 번호로 관리합니다.

 

다음 코드는 객체를 추가하는 방법입니다.

 

@Log4j2
public class ArrayListExample {

	
    // 1. 순서보장
    // 2. 중복 허용
    // 3. 인덱스
    public static void main(String[] args) {
       List<String> list = new ArrayList<>();
		
        // ================================
        // 요소(객체) 추가
        // ================================
		
        list.add("java");           // 컬렉션의 맨 끝에 추가
        list.add("JDBC");
        list.add("Servlet/JSP");
        list.add(2, "Database");    // 지정된 인덱스 번호 위치에 추가
        list.add("iBATIS");
		
    } // main
	
} // end class

 

 

List는 인덱스 배열이기 때문에 대부분의 메서드가 인덱스 기반으로 되어 있습니다.

 

add() 메서드의 소괄호에 데이터 외에 숫자가 들어간다면, 그 숫자는 List의 인덱스 번호입니다.

즉, 인덱스 번호에 데이터를 할당한다는 의미입니다.

 

만약 메서드의 시그니처(선언부)가 궁금하다면 해당 메서드에 마우스를 호버링(hovering, 올려놓기)하면 됩니다.

 

 

메서드 시그니처

 

 

여기서 list 객체를 log.info로 출력해보시면 다음과 같은 내용이 출력됩니다.

 

log.info("list : {}", list);

 

List

 

 

그런데, add() 메서드로 인덱스 번호를 지정하고 데이터를 할당하면,

기존의 데이터들은 뒤로 밀려나게 되고 데이터를 삭제하면 삭제된 데이터의 후순위 데이터는 당겨지게 됩니다.

 

이러한 밀고 당겨지는 현상때문에 빅데이터에서의 List 컬렉션 사용은 지양해야 합니다.

 

 

2. 벡터( Vector )

벡터는 thread-safe하기 때문에 ArrayList보다 많이 사용됩니다.

 

게시판 글에 대한 예제입니다.

먼저 Borad 클래스입니다.

 

@ToString
@AllArgsConstructor
@Getter @Setter
public class Board {
    private String subject;		// 제목
    private String content;		// 내용
    private String writer;		// 작성자
	
	
} // end Board

 

다음은 Board 클래스를 기반으로 한 Vector 실행 클래스 코드입니다.

 

@Log4j2
public class VectorExample {

	
    public static void main(String[] args) {
        List<Board> list = new Vector<Board>();
		
        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"));
		
        list.remove(2);
        list.remove(3);
		
        for(int i = 0; i < list.size(); i++) {
            Board board = list.get(i);

			
            // StringBuffer => Thread - Safe
            String boardLog = 
                    new StringBuffer().
                    append(board.getSubject()).append('\t').
                    append(board.getContent()).append('\t').
                    append(board.getWriter()).
                    toString();
			
            log.info(boardLog);
         } // Classical For
		
        // 남아있는 요소를 가진 컬렉션을 순회(Traverse) #2
        for(Board board : list) {
            log.info(board);
        } // enhanced for
		
        // 남아있는 요소를 가진 컬렉션을 순회(Traverse) #3 
        list.forEach(log::info);
		
    } // main
	
} // end class

 

Vector는 멀티 스레드가 동시에 Vector에 접근해서 객체를 추가, 삭제하더라도 thread-safe합니다.

 

 

3. 링크 리스트( LinkedList )

LinkedList는 thread-unsafe하지만,  ArrayList와 Vector의 요소를 저장하는 구조와 다릅니다.

근처의 참조를 링크해서 체인처럼 관리하고, 특정 인덱스에서 객체를 삭제하거나 추가하게 되면 바로 앞뒤 링크만 재설정합니다.

그렇기 때문에, 실질적인 자리이동이 없습니다.

 

하지만, 다른 레퍼런스를 참조할 때, Vector나 ArrayList 보다 속도가 느리다는 것이 단점입니다.

 

다음은 ArrayList와 LinkedList의 속도 비교입니다.

 

@Log4j2
public class LinkedListExample {
	
	
    public static void main(String[] args) {
        // 1. ArrayList 컬렉션
        List<String> list1 = new ArrayList<String>();
        // 2. LinkedList 컬렉션
        List<String> list2 = new LinkedList<String>();
		
        long startTime;
        long endTime;
		
        startTime = System.nanoTime();
        for(int i = 0; i < 100000; i++) {
            // 미는 현상을 일부러 발생시키기 위해,
            // 리스트의 가장 처음 요소로 무조건 추가
            list1.add(0, String.valueOf(i));
        } // Classical For
		
        endTime = System.nanoTime();
        log.info("ArrayList Elased Time : {}", (endTime - startTime));
		
        // ===========================================================
		
        startTime = System.nanoTime();
		
        for(int i = 0; i < 100000; i++) {
            // 0번 인덱스에 새로운 요소를 추가하지만 미는 현상이 발생 안함
            list2.add(0, String.valueOf(i));
        } // Classical For
		
        endTime = System.nanoTime();
        log.info("ArrayList Elased Time : {}", (endTime - startTime));
        
    } // main
	
} // end class

 

다음은 코드의 결과입니다.

 

LinkedList

 

위의 결과에서도 알 수 있듯이, LinkedList는 ArrayList보다 훨씬 빠른 속도를 보여주고 있습니다.

 

인터페이스의 성질 뿐만 아니라 구현 클래스의 성질과 차이도 잘 알고있어야, 코드를 효율적으로 구축할 수 있습니다.

 

 

 

 

 

 

 

 

'Java > Collection Framework' 카테고리의 다른 글

트리 컬렉션  (0) 2023.08.04
맵 컬렉션  (0) 2023.08.03
셋 컬렉션  (0) 2023.08.03
컬렉션 프레임워크 개요  (0) 2023.08.02

 

 

 

컬렉션 프레임워크는 수 많은 인터페이스가 있는 인터페이스 덩어리입니다.

 

 


 

JCF(Java Collection Framework)는객체들을 효율적으로 CRUD할 수 있도록 제공되는 컬렉션 라이브러리입니다.

java.util(표준) 패키지에 포함되며, 인터페이스를 통해서 이미 정해진 규격대로 컬렉션을 사용하면 됩니다.

 

JCF의 주요 인터페이스입니다.

 


 

JCF Interface

 

예를 들어, List 인터페이스의 코드를 구현하면 다음과 같습니다.

 

List<Integer> list = new ArrayList<>();

 

JCF Interface

 

JCF는 OOP에 근거되어 구현됩니다.

List라는 인터페이스의 규격에 맞춰 ArrayList가 구현한다고 할 수 있습니다.

 


 

멀티 스레드 환경에서 ArrayList는 'thread-unsafe'하기 때문에, 일반적으로 'thread-safe'한 Vector를 사용합니다.

 

다른 Set과 Map 인터페이스에서도 Vector와 비슷하게 thread-safe한 클래스가 있는데,

바로 HashSet, HashTable 클래스가 thread-safe합니다.

 

컬렉션의 특징을 잘 알고 있어야, 데이터의 성격에 따라 올바른 인터페이스를 선택할 수 있습니다.

 

 

 

 

 

 

'Java > Collection Framework' 카테고리의 다른 글

트리 컬렉션  (0) 2023.08.04
맵 컬렉션  (0) 2023.08.03
셋 컬렉션  (0) 2023.08.03
리스트 컬렉션  (0) 2023.08.02

+ Recent posts