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

 

 

 

제네릭(Generic)은 API Document 해석을 위해 학습이 필요합니다.

변수 앞에 붙이는 타입을 잘못 기재하는 것을 방지하기 위한 목적으로 Java 8부터 제네릭이 출현하였습니다.

정식적으로 제공된 것은 Java 5부터 였으나, Java 8부터 주목받기 시작했습니다.

 


 

 

제네릭을 학습하기에 앞서, 알아야할 용어를 정리하였습니다.

 

제네릭 타입 :
제네릭 타입은 제네릭 인터페이스와 제네릭 클래스을 의미합니다.


제네릭 메소드 :
메소드에도 제네릭 문법( <> )이 적용 가능합니다.


타입 파라미터(Type Parameter, 타입 매개변수) :
타입 파라미터는 전달인자로 "참조타입의 이름"을 받는 목적의 파라미터입니다. 기본타입은 없습니다.

- 다음은 타입 파라미터의 문법입니다. 타입 파라미터는 최소 1개 이상 선언해야 합니다.
  EX) <E>, <T>, <E, T>

- 다음은 타입 파라미터의 예시입니다.
  EX)
  class ArrayList<E>
  여기서 다이아몬드 기호에 할당되는 단대문자 'E'가 바로 타입 파라미터입니다.

 

타입 파라미터의 예시 코드입니다.

 

class ArrayList<E> { // E : Element
    // sample code
    
    List<String> list = new ArrayList<String>(); // 좌, 우항 모두 타입 파라미터는 같아야 함.
}

 


 

제네릭은 참조타입 간의 강제 형 변환을 하는 것이 주 목적입니다.

간단한 예제로 살펴보겠습니다.

 

Box 클래스

@NoArgsConstructor
@Getter @Setter
public class Box<T> {
    private T t;
	
} // end class

 

실행 클래스

 

@Log4j2
public class BoxExample {

	
    public static void main(String[] args) {
        // 1. 제네릭 클래스로부터 상자를 생성
        Box<String> box = new Box<>();
		
        // 2. 상자에 객체를 넣음
        box.setT("hello");
		
        // 3. 상자에서 다시 꺼냄
        String str = box.getT();
		
        // 4. 꺼낸 객체를 출력
        log.info("str : {}", str);
        
    } // main
	
} // end class

 

제네릭을 사용하게 되면 컴파일 시 강한 타입 체크가 가능하고,

가장 큰 이점은 더 이상 형 변환할 필요가 없다는 것입니다.

 

 

 

 

 

 

 

 

모든 Error와 Exception은 java.lang.throwable에 의해 만들어집니다.

Exception은 Cheched Exception(일반 예외)과 Runtime Exception(실행 예외)이 있는데, 

Checked Exception은 컴파일러가 사용자에게 알려주지만 RuntimeException은 관여하지 않기 때문에 유의해야 합니다.

 

 


 

□ Runtime Exception

Runtime Exception은 Exception 처리 코드를 생략하더라도 컴파일이 되는 Exception을 뜻합니다.

 

다음은 Runtime Exception의 대표적인 종류입니다.

 

  • NullPointerException
  • ArrayIndexOutOfBoundsException
  • ClassCastException
  • NumberFormatException

 


 

1. NullPonterException

NullPonterException은 객체 참조가 없는 상태에서의 Exception입니다.

null 값을 갖는 참조 변수로 객체 접근 연산자인 도트(.)를 사용했을 때 발생합니다.

 

다음은 예시 코드입니다.

 

@Log4j2
public class NullPointerException {

	
    public static void main(String[] args) {
//      String data = null;
//      log.info(data.toString());
		
    } // main
    
} // end class

 

위의 코드를 실행하면 아래와 같은 오류 코드가 발생합니다.

 

NullPointerException

 

첫번 째 줄에서 왼쪽은 Exception 이름, 오른쪽은 Exception이 발생하게 된 이유입니다.

두번 째 줄은 Stack Trace입니다.

Stack Trace은 프로그램의 실행 경로를 역순으로 나열해서 콘솔 뷰에서 보여주는데,

Bottom-Up으로 읽은게 일반적이며, Runtime Exception을 처리할 때 사용됩니다.

 

 

 

2. ArrayIndexOutOfBoundsException

ArrayIndexOutOfBoundsException은 배열에서 인덱스 범위를 초과해서 사용할 경우 발생합니다.

 

다음은 예시 코드입니다.

@Log4j2
public class ArrayIndexOutOfBoundsException {

	
    public static void main(String[] args) {
        if(args.length == 2) {
            String data1 = args[0];
            String data2 = args[1];
            String data3 = args[2];
			
            log.info("args[0] : ", data1);
            log.info("args[1] : ", data2);
            log.info("args[2] : ", data3);
        } else {
            log.info("[ 실행 방법 ]");
            log.info("java ArrayIndexOfBoundsException   ");
            log.info("값1 값2");
        } // if - else
		
    } // main
	
} // end class

 

args의 배열 길이가 2일 경우에만 log.info로 출력하게 하였고,

그 외의 경우는 실행 방법을 출력해서 예외 처리를 하였습니다.

 

위의 코드를 컴파일할 경우 다음과 같은 오류 코드가 발생합니다.

 

ArrayOutOfBoundsException

 

 

 

3.ClassCastException

ClassCastException은 타입 변환이 되지 않을 경우 발생합니다.

 

다음은 예시 코드입니다.

 

 

@Log4j2
public class ClasscastException {

    public static void main(String[] args) {
        Dog dog = new Dog();
        ClasscastException.chageDog(dog);
		
        Cat cat = new Cat();
        ClasscastException.chageDog(cat);
		
    } // main
	
	
    public static void chageDog(Animal animal) {
        if(animal instanceof Dog) {
            Dog dog = (Dog) animal; // ClassCastException 발생 가능
        } else if(animal instanceof Cat) {
            Cat cat = (Cat) animal;
        } else {
            throw new IllegalArgumentException(String.format("값이 잘못 들어왓습니다."));
        }
		
    } // changeDog

} // end class

class Animal{;;}
class Dog extends Animal{;;}
class Cat extends Animal{;;}

 

Animal 부모 클래스를 생성하고, 

Animal 클래스를 extends하는 Dog, Cat클래스를 각각 생성합니다.

실행 클래스에서 Dog와 Cat의 인스턴스를 생성하고 changeDog 메서드를 호출해서 타입변환을 시도합니다.

changeDog 메서드는 Animal 타입의 파라미터를 받고 각 파라미터의 타입을 instanceof로 비교해서 맞는 타입으로 변환합니다.

 

위의 코드는 문제가 없지만 만약 Cat으로 변환하는 else가 없다면 ClassCastException이 발생합니다.

 

ClassCastException

 

ClassCastException이 발생할 경우 IlleageArgumentException을 발생시켜 값이 잘못 들어왔음을 알려줍니다.

 

 

 

4. NumberFormatException

NumberFormatException은 데이터 타입의 타입 변환이 불가할 때 발생합니다.

 

다음은 예시 코드입니다.

 

@Log4j2
public class NumberFormatException {
	
	
    public static void main(String[] args) {
        // 최초 입력 데이터는 문자열
        String data1 = "100";
        String data2 = "a100";
		
        // 위의 문자열 중, 숫자 형태의 문자열을 Int타입으로 변경
        int value1 = Integer.parseInt(data1);
        int value2 = Integer.parseInt(data2);
		
		
        int result = value1 + value2;
        log.info("{} + {} + = {}", data1, data2, result);
    } // main
	
} // end class

 

위의 코드를 실행시키면 NumberFormatException이 발생합니다.

 

NumberFormatException

 

a100에 문자 'a'가 있기 때문에 NumberFormatException이 발생했으며, 콘솔에서도 확인할 수 있습니다.

 

 

 

 

 

 

'Java' 카테고리의 다른 글

보조 스트림  (0) 2023.08.17
파일 클래스  (0) 2023.08.17
Exception  (0) 2023.07.27
익명구현객체  (0) 2023.07.26
인터페이스 다중 상속  (0) 2023.07.26

 

 

예외는 CheckecdException(일반 예외)와 RuntimeException(실행 예외)가 있습니다.

RuntimeException은 Exception에 포함되어 있지만

컴파일러는 RuntimeException에 개입하지 않습니다.

 

 


ChechedException은 체크를 당하는 예외이며 컴파일 당하게 됩니다. 

대표적인 일반 예외는 다음과 같습니다.

java.lang.ClassNotFoundException

java.lang.InterruptedException

 

RuntimeException은 컴파일이 체크에 개입하지 않습니다. 정확히는 컴파일러가 예외를 무시하게 됩니다.

대표적인 실행 예외는 다음과 같습니다.

java.lang.NullPointerException

java.lang.ArrayIndexOutOfBoundsException

java.lang.ClassCastException

java.lang.NumberFormatException

 

 


 

1. NullPointException

객체 참조가 없는 상태, null 값을 갖는 참조변수로 객체 접근 연산자인 도트(.)를 사용했을 때 발생합니다.

 

NullPoinException

 

NullPointException-consoleView

 

첫 줄을 보시면 예외의 타입, 예외의 원인을 보여주고 있습니다.

 

두번째 줄은 stacktrace입니다.

stacktrace는 시스템에서 보내는 오류 메세지인데, 예외가 발생한 시점에서의 메소드 호출 스택을 보여줍니다.

stacetrace가 길어지면 호출의 흐름을 Bottom-Up으로 읽는게 일반적입니다.

그렇다면 위의 메세지에서의 메소드 호출 스택은 "NullPointerException.java:12" 이 됩니다.

즉, "data"가 "null"이므로 String.toString()을 invoked(호출)할 수 없다는 뜻입니다.

 

 

 

2.  ArrayIndexOutOfBoundsException

배열에서 인덱스 범위를 초과해서 사용할 경우 발생합니다.

 

ArrayIndexOutOfBoundsException

 

Run Configuration에서 args에 값을 넣어주지 않으면 else에서 예외를 처리하게 됩니다.

 

ArrayIndexOutOfBoundsException 발생

 

args에 값을 할당하게 되면 정상적으로 동작합니다.

 

 

 

3. ClassCastException

형 변환이 잘못되었을 때 발생합니다.

 

ClassCastException

 

위의 코드에서 Dog 타입의 dog를 changeDog 메서드의 Animal 타입 파라미터로 던져주고,

changeDog 메서드는 instanceof로 받은 파라미터가 Dog 타입인지 확인합니다.

이후 Dog 타입이면 animal 타입의 파라미터인 dog를 Dog타입으로 강제 형 변환합니다.

 

만약 Cat 타입의 cat 객체가 changeDog 메서드의 파라미터로 던져지게 되면, instanceof에서 필터링하고

changeDog 메서드에서 cat의 강제 형변환이 실행되지 않게 됩니다.

 

만약 cat도 강제 형 변환을 하고 싶다면 다음과 같이 코드를 작성할 수 있습니다.

@Log4j2
public class ClasscastException {

    public static void main(String[] args) {
        Dog dog = new Dog();
        ClasscastException.chageDog(dog);
		
        Cat cat = new Cat();
        ClasscastException.chageDog(cat);
		
    } // main
	
	
    public static void chageDog(Animal animal) {
        if(animal instanceof Dog) {
            Dog dog = (Dog) animal; // ClassCastException 발생 가능
        } else if(animal instanceof Cat) {
            Cat cat = (Cat) animal;
        } else {
            throw new IllegalArgumentException(String.format("값이 잘못 들어왓습니다."));
        }
		
    } // changeDog

} // end class

class Animal{;;}
class Dog extends Animal{;;}
class Cat extends Animal{;;}

 

 

 

4. NumberFormatException

 

NumberFormatException

data1은 문자열 타입의 숫자라서 Integer.parseInt로 타입 변환이 가능하지만,

data2는 문자 a가 섞여 있어서 Int로의 타입 변환이 불가능합니다.

 

위 코드를 실행하면 다음과 같은 오류 메세지가 출력됩니다.

NumberFormatException

 

 

 


 

 

이러한 Eception들은 try-catch-finally 블록을 이용해서 Exception 코드를 처리할 수 있습니다.

'Java' 카테고리의 다른 글

파일 클래스  (0) 2023.08.17
Exception Handling  (0) 2023.07.28
익명구현객체  (0) 2023.07.26
인터페이스 다중 상속  (0) 2023.07.26
패턴 매칭 연산자  (0) 2023.07.26

+ Recent posts