개발자 꼬부기의 성장일기
15일차 - Java 컬렉션 (열거형, 제너릭, 예외처리, 컬렉션프레임워크) 본문
컬렉션
열거형
제너릭
예외처리
컬렉션 프레임워크
2일 동안 OOP개념 실습을 마치고 컬렉션 파트로 들어왔다.
어렴풋이 사용만 해봤던 터라 개념강의 들으니 좀 더 알것 같다.
열거형(Enum)
: 서로 연관된 상수들의 집합
상수 명이 중복되는 경우가 발생한다.
클래스를 구분하면 중복 문제는 해결 가능하지만 타입 안정성이라는 새로운 문제가 생긴다.
=> 객체를 생성해 주면, 상수 명 중복과 타입 안정성 문제를 모두 해결할 수 있다.
더 간결하고 가독성이 좋은 코드를 작성가능.
enum 열거형이름 { 상수명1, 상수명2, 상수명3, ...}
상수는 대소문자로 모두 작성이 가능하지만, 관례적으로 대문자로 작성
각각의 상수들에는 따로 값을 지정해주지 않아도 자동적으로 0부터 시작하는 정수값이 할당됨.
enum Seasons { SPRING, SUMMER, FALL, WINTER }
class EnumExample {
public static void main(String[] args)
{
// Seasons.SUMMER의 주소를 할당
Seasons favoriteSeason = Seasons.SUMMER;
// 주소값
System.out.println(favoriteSeason);
//Season 중 주어진 문자열 "FALL"의 열거 객체 리턴
System.out.println(favoriteSeason.valueOf("FALL"));
//Seasons 열거 객체들을 배열로 리턴
System.out.println(favoriteSeason.values());
//객체들을 배열에 담아 출력
Seasons[] allSeasons = favoriteSeason.values();
for(Seasons x : allSeasons) {
//name(): 열거 객체가 가지고 있는 문자열을 리턴
//ordinal(): 열거 객체의 순번(0부터 시작)을 리턴
System.out.printf("%s=%d%n", x.name(), x.ordinal());
}
//실행결과:
//SPRING=0
//SUMMER=1
//FALL=2
//WINTER=3
favoriteSeason = Seasons.WINTER; //WINTER 주소로 바꿈.
switch(favoriteSeason){
case SPRING:
System.out.println("나는 봄을 좋아합니다");
break;
case SUMMER:
System.out.println("나는 여름을 좋아합니다");
break;
case FALL:
System.out.println("나는 가을을 좋아합니다");
break;
case WINTER:
System.out.println("나는 겨울을 좋아합니다");
break;
}
//실행결과 : 나는 겨울을 좋아합니다
}
}
제너릭
: 타입을 구체적으로 지정하는 것이 아니라, 추후에 지정할 수 있도록 일반화해 둔다. 즉, 작성한 클래스 또는 메서드의 코드가 특정 데이터 타입에 얽매이지 않게 해 둔 것을 의미한다.
제네릭을 사용하면 단 하나의 클래스만으로 모든 타입의 데이터를 저장할 수 있는 인스턴스를 만들 수 있다.
* 참조 int와 Integer의 차이
Integer는 int의 레퍼클래스
기본형을 객체로 다루기 위해 사용하는 클래스들을 래퍼 클래스(wrapper class)라고 한다.
int 는 기본자료형(primitive type)
integer는 int를 표현해야 하는 경우
- 매개변수로 객체를 필요로 할 때
- 기본형 값이 아닌 객체로 저장해야할 때
- 객체 간 비교가 필요할 때
int vs Integer
int : 산술연산 가능. null로 초기화 불가
Integer : Unboxing하지 않을 시 산술 연산 불가능함. null값 처리 가능
래퍼 클래스(Wrapper class)는 산술 연산을 위해 정의된 클래스가 아니기 때문에
인스턴스에 저장된 값을 변경할 수 없다.
단지 값을 참조하기 위해 새로운 인스턴스를 생성하고, 생성된 인스턴스의 값만을 참조할 수 있다
기본 타입의 데이터를 wrapper 클래스의 인스턴스로 변환하는 과정을 박싱(Boxing)이라고 하고, 래퍼 클래스의 인스턴스에 저장된 값을 다시 기본 타입의 데이터로 꺼내는 과정을 언박싱(UnBoxing)이라고 한다
결정적으로 컬렉션 프레임 워크의 데이터 구조는 기본 타입이 아닌 객체만 저장할 수 있으며, 래퍼 클래스를 사용해 자동 박싱과 언박싱이 일어난다.
제네릭 클래스 정의
class Basket<T> {
}
class Basket<T> {
private T item;
...
}
두 개이상 타입이 필요할 때
class Basket<K, V> { ... }
static은 사용할 수 없다.
제한된 제네릭 클래스
모든 타입을 지정할 수 있지만 제한할 수도 있다.
class Flower { ... }
class Rose extends Flower { ... }
class RosePasta { ... }
class Basket<T extends Flower> {
private T item;
...
}
위의 경우는 Flower클래스를 상속받는 자식클래스에 한해서 가능하다
interface Plant { ... }
class Flower implements Plant { ... }
class Rose extends Flower implements Plant { ... }
class Basket<T extends Plant> {
private T item;
특정 인터페이스를 구현한 클래스만 타입으로 지정할 수 있도록 제한할 수 있다.
class Basket<T extends Flower & Plant> { // (1)
private T item;
...
}
특정 클래스를 상속받으면서 동시에 특정 인터페이스를 구현한 클래스만 타입으로 지정할 수도 있다.(&사용)
제네릭 메서드
class Basket<T> { //1.
...
public <T> void add(T element) { //2.
...
}
}
//클래스에 있는 T와 메서드에 있는 T는 다른 것으로 봄.
Basket<String> basket = new Bakset<>(); // 위 예제의 1의 T가 String으로 지정됩니다.
basket.<Integer>add(10); // 위 예제의 2의 T가 Integer로 지정됩니다.
basket.add(10); // 타입 지정을 생략할 수도 있습니다.
클래스 타입 매개 변수와 달리 메서드 타입 매개 변수는 static 메서드 사용가능.
제네릭 메서드는 메서드가 호출되는 시점에서 제네릭 타입이 결정되므로, 제네릭 메서드를 정의하는 시점에서는 실제 어떤 타입이 입력되는지 알 수 없다. 따라서 length()와 같은 String 클래스의 메서드는 제네릭 메서드를 정의하는 시점에 사용할 수 없다.
모든 자바 클래스의 최상위 클래스인 Object 클래스의 메서드는 사용 가능
와일드카드
: 어떠한 타입으로든 대체될 수 있는 타입 파라미터
<? extends T>
<? super T>
extends는 자기 포함 하위클래스
super는 자기 포함 상위클래스
예외처리
프로그램의 비정상적인 종료를 방지하고, 정상적인 실행 상태를 유지하기 위해 사용.
발생 시점에 따라 에러를 컴파일 에러(Compile Time Error)와 런타임 에러(Run Time Error)로 구분한다.
컴파일 에러 : 자바 컴파일러가 사전에 발생할 수 있는 에러를 발견하여 미리 주의를 준 것. 신택스 에러(Systax Errors)라고도 한다.
런타임 에러 : 코드를 실행하는 과정, 즉 런타임 시에 발생하는 에러 ( 자바 가상 머신(JVM)에 의해 감지)
코드 실행(run-time) 시 잠재적으로 발생할 수 있는 프로그램 오류를 크게 에러(error)와 예외(exception)으로 구분한다.
에러란 한번 발생하면 복구하기 어려운 수준의 심각한 오류이고 대표적으로 메모리 부족, 스택 오버플로우 등 이 있다.
예외는 잘못된 사용 또는 코딩으로 인한 상대적으로 미약한 수준의 오류로서 코드 수정 등을 통해 수습이 가능한 오류이다.
에러 클래스와 예외클래스
java.lang -> Object -> Throwable -> Errors
java.lang -> Object -> Throwable -> Exception -> 실행예외클래스(Runtime Exception )
java.lang -> Object -> Throwable -> Exception -> 일반예외클래스(Other Exception)
일반예외클래스
런타임 시 발생하는 RuntimeException 클래스와 그 하위 클래스를 제외한 모든 Exception 클래스와 그 하위 클래스들
실행예외클래스
런타임 시 발생하는 RuntimeException 클래스와 그 하위클래스
ex) 클래스 간 형변환 오류(ClassCastException), 벗어난 배열 범위 지정(ArrayIndexOutOfBoundsException), 값이 null인 참조변수 사용(NullPointerException) 등
예외처리 방법 : try - catch문
try {
// 예외가 발생할 가능성이 있는 코드를 삽입
throwException(); //20번줄로 떨어짐 강제로 예외처리
Exception intendedException = new Exception("의도된 예외 만들기");
throw intendedException;
}
catch (NullPointerException e1) {
// NullPointerException 유형의 예외 발생 시 실행할 코드
}
...
catch (Exception e2) {
// 기타 예외 발생 시 또는 고의로 예외발생 실행할 코드
}
finally {
// finally 블록은 옵셔널
// 예외 발생 여부와 상관없이 항상 실행
}
static void throwException() throws ClassNotFoundException, NullPointerException {
Class.forName("java.lang.StringX");
}
컬렉션 프레임워크
컬렉션이란 여러 데이터의 집합.
컬렉션 프레임워크 컬렉션을 다루는 데에 있어 편리한 메서드들을 미리 정의해 놓은 것.
컬렉션 프레임워크는 주요 인터페이스로 List, Set, Map을 제공한다.
List : 데이터 순서가 유지되며 중복 저장이 가능. ArrayList, Vector, Stack, LinkedList 등
Set : 순서는 없고 중복저장도 불가능.
Map : 키와 값 (key:value) 형식의 데이터 저장. 순서는 없고, 키값 기준으로 저장되기에 키값은 중복 저장 불가능 . 값은 가능.
List와 Set은 서로 공통점이 많아 위 그림과 같이 Collection이라는 인터페이스로 묶인다.
List
ArrayList : List 인터페이스를 구현한 클래스. 기능적으로는 Vector와 동일하지만, 기존의 Vector를 개선한 것.
ArrayList는 저장 용량을 초과하여 객체들이 추가되면, 자동으로 저장용량이 늘어나게 된다. (데이터의 순서 유지됨)
ArrayList<타입 매개변수> 객체명 = new ArrayList<타입 매개변수>(초기 저장 용량);
ArrayList<String> list = new ArrayList<String>();
// String 타입의 객체를 저장하는 ArrayList 생성
// 초기 용량이 인자로 전달되지 않으면 기본적으로 10으로 지정됩니다.
ArrayList<String> list2 = new ArrayList<String>(30);
// String 타입의 객체를 저장하는 ArrayList 생성
// 초기 용량을 30으로 지정하였습니다.
빈번한 객체 삭제와 삽입이 일어나는 곳에서는 ArrayList보다는 LinkedList를 사용하는 것이 좋다.
// String 타입의 데이터를 ArrayList에 추가
list.add("Java");
list.add("egg");
list.add("tree");
// 저장된 총 객체 수 얻기
int size = list.size();
// 0번 인덱스의 객체 얻기
String skill = list.get(0);
// 저장된 총 객체 수 만큼 조회
for(int i = 0; i < list.size(); i++){
String str = list.get(i);
System.out.println(i + ":" + str);
}
// for-each문으로 순회
for (String str: list) {
System.out.println(str);
}
// 0번 인덱스 객체 삭제
list.remove(0);
LinkedList
데이터를 효율적으로 추가, 삭제, 변경하기 위해 사용. 서로 연결(link)되어 있다.
각 요소(node)들은 자신과 연결된 이전 요소 및 다음 요소의 주소값과 데이터로 구성
ArrayList VS LinkedList
- ArrayList에 객체를 순차적으로 저장할 때는 데이터를 이동하지 않아도 되므로 작업 속도가 빠르지만,
중간에 위치한 객체를 추가 및 삭제할 때는 데이터 이동이 많이 일어나므로 속도가 저하됨.
- 데이터를 순차적으로 추가하거나 삭제하는 경우
- 데이터를 불러오는 경우 (인덱스로 바로 접근할 수 있으므로 검색이 빠름)
- 따라서 데이터를 중간에 추가하거나 삭제하는 경우, 주소값 변경만 해주면 되기 때문에
LinkedList는 ArrayList보다 빠른 속도
- 검색에 있어서는 ArrayList보다 상대적으로 속도가 느리다. 주소값으로 가서 확인해야하기 때문에
Iterator / iterator()
반복자라는 의미가 있으며, 컬렉션에 저장된 요소들을 순차적으로 읽어오는 역할
ArrayList<String> list = ...;
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) { // 읽어올 다음 객체가 있다면
String str = iterator.next(); // next()를 통해 다음 객체를 읽어옵니다.
if(str.equals("str과 같은 단어")){ // 조건에 부합한다면
iterator.remove(); // 해당 객체를 컬렉션에서 제거합니다.
}
}
Set<E>
Set은 요소의 중복을 허용하지 않고, 저장 순서를 유지하지 않는 컬렉션 (집합이랑 비슷한 개념)
HashSet은 Set 인터페이스를 구현한 가장 대표적인 컬렉션 클래스
Java를 넣었지만 중복이므로 추가되지 않았음.
TreeSet : 이진 탐색 트리 형태로 데이터를 저장. 데이터의 중복을 허용하지 않고 저장 순서 유지 하지 않음.
하나의 부모 노드가 최대 두 개의 자식 노드와 연결되는 이진트리(Binary Tree)의 일종으로, 정렬과 검색에 특화된 자료 구조
모든 왼쪽 자식의 값이 루트나 부모보다 작고, 모든 오른쪽 자식의 값이 루트나 부모보다 큰 값을 가지는 특징
class Node {
Object element; // 객체의 주소값을 저장하는 참조변수 입니다.
Node left; // 왼쪽 자식 노드의 주소값을 저장하는 참조변수입니다.
Node right; // 오른쪽 자식 노드의 주소값을 저장하는 참조변수입니다.
}
Map<K,V>
Map 인터페이스는 키(key)와 값(value)으로 구성된 객체를 저장하는 구조
이 객체를 Entry 객체라고 하는데, 이 Entry 객체는 키와 값을 각각 Key 객체와 Value 객체로 저장
HashMap
이름 그대로 해싱(Hashing)을 사용하기 때문에 많은 양의 데이터를 검색하는 데 있어서 뛰어난 성능
HashMap<String, Integer> hashmap = new HashMap<>();
Map은 순서가 없기 때문에 반복문을 돌릴수가 없다. => Set을 이용해서 어떤기준으로 담아서 돌린다.
* HashMap에서 키값이 중복되지 않아서 똑같은 키값을 넣으면 value값을 덮어쓴다.
Hashtable HashMap과 내부 구조가 동일하며, 사용 방법 또한 매우 유사하다.
'언어공부 > [코드스테이츠] 백엔드부트캠프' 카테고리의 다른 글
19일차 Java 심화(Effective) (2) : 스레드, JVM (0) | 2023.05.08 |
---|---|
16일차 - 코플릿 문제풀기(컬렉션) (0) | 2023.05.02 |
12일차 - Java 객체지향 심화(2) : 다형성, 추상화 (0) | 2023.04.26 |
11일차 - Java 객체지향 심화(1) : 상속, 캡슐화 (0) | 2023.04.25 |
10일차 - Java 객체지향 기초(2) : 생성자, 내부클래스 (0) | 2023.04.24 |