서로 연관된 상수들의 집합
enum 열거형이름 { 상수명1, 상수명2, 상수명3, ...}
enum Seasons { SPRING, SUMMER, FALL, WINTER }
enum Frameworks { DJANGO, SPRING, NEST, EXPRESS }
상수는 대소문자로 모두 작성이 가능하지만 관례적으로 대문자로 작성
각각의 상수들에는 따로 값을 지정해주지 않아도 자동적으로 0부터 시작하는 정수값이 할당되어 각각의 상수를 가리키게 됨
선언 -> 열거형이름.상수명
으로 호출
(static변수를 참조하는 것과 동일)
enum Level {
LOW, // 0
MEDIUM, // 1
HIGH // 2
}
public class Main {
public static void main(String[] args) {
Level level = Level.MEDIUM;
switch(level) {
case LOW:
System.out.println("낮은 레벨");
break;
case MEDIUM:
System.out.println("중간 레벨");
break;
case HIGH:
System.out.println("높은 레벨");
break;
}
}
}
//출력값
중간 레벨
아래 메서드들은 모든 열거형의 조상인 java.lang.Enum 에 정의되어있는 것으로, 클래스에서 최상위 클래스 Object에 정의된 메서드들을 사용할 수 있었던 것과 동일하다
리턴 타입 | 메소드(매개변수) | 설명 |
---|---|---|
String | name() | 열거 객체가 가지고 있는 문자열을 리턴하며, 리턴되는 문자열은 열거타입을 정의할 때 사용한 상수 이름과 동일하다. |
int | ordinal() | 열거 객체의 순번(0부터 시작)을 리턴한다. |
int | compareTo(비교값) | 주어진 매개값과 비교해서 순번 차이를 리턴한다. |
열거 타입 | valueOf(String name) | 주어진 문자열의 열거 객체를 리턴한다. |
열거 배열 | values() | 모든 열거 객체들을 배열로 리턴한다. |
enum Level {
LOW, // 0
MEDIUM, // 1
HIGH // 2
}
public class EnumTest {
public static void main(String[] args) {
Level[] allLevels = Level.values();
for(Level x : allLevels) {
System.out.printf("%s=%d%n", x.name(), x.ordinal());
}
Level findLevel = Level.valueOf("LOW");
System.out.println(findLevel); // LOW
System.out.println(Level.LOW == Level.valueOf("LOW")); // true
}
}
//출력값
LOW=0
MEDIUM=1
HIGH=2
LOW
true
클래스나 메서드의 코드를 작성할 때, 타입을 구체적으로 지정하는 것이 아니라, 추후에 지정할 수 있도록 일반화해두는 것을 의미
즉, 작성한 클래스 또는 메서드의 코드가 특정 데이터 타입에 얽매이지 않게 해둔 것
class Basket<T> {
private T item;
public Basket(T item) {
this.item = item;
}
public T getItem() {
return item;
}
public void setItem(T item) {
this.item = item;
}
}
Basket<String> basket1 = new Basket<String>("기타줄");
Basket<Boolean> basket3 = new Basket<Boolean>(true);
Basket<Double> basket4 = new Basket<Double>(3.14);
기존의 방식으로는 다양한 타입의 데이터를 저장하기 위해 각 타입별로 별도의 클래스를 만들어야 했다. 하지만 제네릭을 사용하면 단 하나의 Basket 클래스만으로 모든 타입의 데이터를 저장할 수 있는 인스턴스를 만들 수 있다. (코드가 단순해짐)
제네릭이 사용된 클래스 (위 예시 같은거)
타입 매개변수를 여러 개 사용해야 한다면, 아래와 같이 선언하면 된다.
class Basket<K, V> { ... }
타입 매개변수는 임의의 문자로 지정 가능 (T,K,V 아니여도 됨)
클래스(static) 변수에는 타입 매개변수를 사용할 수 없다.
class Basket<T> {
private T item1; // O
static T item2; // X
}
제네릭 클래스를 인스턴스화할 때에는 의도하고자 하는 타입을 지정해 줘야함
단, 타입 매개변수에 치환될 타입으로 기본 타입을 지정할 수 없다.
래퍼클래스를 활용해야 한다.
Basket<String> basket1 = new Basket<>("Hello");
Basket<Integer> basket2 = new Basket<>(10);
Basket<Double> basket2 = new Basket<>(3.14);
아래와 같이 다향성 적용도 가능하다.
class Flower { ... }
class Rose extends Flower { ... }
class RosePasta { ... }
class Basket<T> {
private T item;
public T getItem() {
return item;
}
public void setItem(T item) {
this.item = item;
}
}
public static void main(String[] args) {
Basket<Flower> flowerBasket = new Basket<>();
flowerBasket.setItem(new Rose()); // 다형성 적용
flowerBasket.setItem(new RosePasta()); // 에러
}
class Basket<T extends Flower> {
private T item;
...
}
바로 위 예시와 같이 Basket 클래스를 인스턴스화할 때 타입으로 Flower 클래스의 하위 클래스만 지정하도록 제한하거나 특정 인터페이스를 구현한 클래스만 타입으로 지정할 수 있도록 제한할 수 있다.
interface Plant { ... }
class Flower implements Plant { ... }
class Rose extends Flower implements Plant { ... }
class Basket<T extends Plant> {
private T item;
...
}
class Main {
public static void main(String[] args) {
// 인스턴스화
Basket<Flower> flowerBasket = new Basket<>();
Basket<Rose> roseBasket = new Basket<>();
}
}
class Basket<T extends Flower & Plant> { // (1)
private T item;
...
}
특정 클래스를 상속받으면서 동시에 특정 인터페이스를 구현한 클래스만 타입으로 지정할 수 있게도 할 수 있다.
(다만, 이러한 경우에는 클래스를 인터페이스보다 앞에 위치시켜야 한다. )
클래스 내부의 특정 메서드만 제네릭으로 선언할 수 있다.
class Basket {
...
public <T> void add(T element) {
...
}
}
class Basket<T> { // 1 : 여기에서 선언한 타입 매개변수 T와
...
public <T> void add(T element) { // 2 : 여기에서 선언한 타입 매개변수 T는 서로 다른 것이다.
...
}
}
-> 제네릭 클래스의 타입 매개변수는 클래스가 인스턴스화될 때 타입이 지정 됨.
-> 제네릭 메서드의 타입 매개변수는 메서드가 호출될 때 타입이 지정 됨.
클래스 타입 매개변수와 달리 메서드 타입 매개변수는 static 메서드에서도 선언하여 사용할 수 있다.
length()와 같은 String 클래스의 메서드는 제네릭 메서드를 정의하는 시점에 사용할 수 없다.
(제네릭 메서드를 정의하는 시점에서는 실제 어떤 타입이 입력 되는지 알 수 없기 때문)
equals()
, toString()
등)어떠한 타입으로든 대체될 수 있는 타입 파라미터를 의미
일반적으로 와일드카드는 extends와 super 키워드를 조합하여 사용한다.
<? extends T> // 상한 제한
<? super T> // 하한 제한
extends 및 super 키워드와 조합하지 않은 와일드카드(<?>
)는 모든 클래스 타입을 타입 파라미터로 받을 수 있음을 의미한다.
class PhoneFunction {
public static void call(User<? extends Phone> user) {
System.out.println("-----------------------------");
System.out.println("user.phone = " + user.phone.getClass().getSimpleName());
System.out.println("모든 Phone은 통화를 할 수 있다.");
}
public static void faceId(User<? extends IPhone> user) {
System.out.println("-----------------------------");
System.out.println("user.phone = " + user.phone.getClass().getSimpleName());
System.out.println("IPhone만 Face ID를 사용할 수 있다. ");
}
public static void samsungPay(User<? extends Galaxy> user) {
System.out.println("-----------------------------");
System.out.println("user.phone = " + user.phone.getClass().getSimpleName());
System.out.println("Galaxy만 삼성 페이를 사용할 수 있다. ");
}
public static void recordVoice(User<? super Galaxy> user) {
System.out.println("-----------------------------");
System.out.println("user.phone = " + user.phone.getClass().getSimpleName());
System.out.println("안드로이드 폰에서만 통화 녹음이 가능하다. ");
}
}
public class Example {
public static void main(String[] args) {
PhoneFunction.call(new User<Phone>(new Phone()));
PhoneFunction.call(new User<IPhone>(new IPhone()));
PhoneFunction.call(new User<Galaxy>(new Galaxy()));
PhoneFunction.call(new User<IPhone12Pro>(new IPhone12Pro()));
PhoneFunction.call(new User<IPhoneXS>(new IPhoneXS()));
PhoneFunction.call(new User<S22>(new S22()));
PhoneFunction.call(new User<ZFlip3>(new ZFlip3()));
System.out.println("\n######################################\n");
// PhoneFunction.faceId(new User<Phone>(new Phone())); // X
PhoneFunction.faceId(new User<IPhone>(new IPhone()));
PhoneFunction.faceId(new User<IPhone12Pro>(new IPhone12Pro()));
PhoneFunction.faceId(new User<IPhoneXS>(new IPhoneXS()));
// PhoneFunction.faceId(new User<Galaxy>(new Galaxy())); // X
// PhoneFunction.faceId(new User<S22>(new S22())); // X
// PhoneFunction.faceId(new User<ZFlip3>(new ZFlip3())); // X
System.out.println("\n######################################\n");
// PhoneFunction.samsungPay(new User<Phone>(new Phone())); // X
// PhoneFunction.samsungPay(new User<IPhone>(new IPhone())); // X
// PhoneFunction.samsungPay(new User<IPhone12Pro>(new IPhone12Pro())); // X
// PhoneFunction.samsungPay(new User<IPhoneXS>(new IPhoneXS())); // X
PhoneFunction.samsungPay(new User<Galaxy>(new Galaxy()));
PhoneFunction.samsungPay(new User<S22>(new S22()));
PhoneFunction.samsungPay(new User<ZFlip3>(new ZFlip3()));
System.out.println("\n######################################\n");
PhoneFunction.recordVoice(new User<Phone>(new Phone()));
// PhoneFunction.recordVoice(new User<IPhone>(new IPhone())); // X
// PhoneFunction.recordVoice(new User<IPhone12Pro>(new IPhone12Pro())); // X
// PhoneFunction.recordVoice(new User<IPhoneXS>(new IPhoneXS())); // X
PhoneFunction.recordVoice(new User<Galaxy>(new Galaxy()));
// PhoneFunction.recordVoice(new User<S22>(new S22())); // X
// PhoneFunction.recordVoice(new User<ZFlip3>(new ZFlip3())); // X
}
}
주석 처리한 부분은 오류(사용불가)가 나는 케이스다.
에러에 대응할 수 있는 코드를 미리 사전에 작성하여 프로그램의 비정상적인 종료를 방지하고, 정상적인 실행 상태를 유지하는 방법
“컴파일 할 때" 발생하는 에러
ex) 세미콜론 생략, 오탈자, 잘못된 자료형, 잘못된 포맷 등
런타임(실행) 시에 발생하는 에러
컴퓨터가 수행할 수 없는 작업을 요청할 때 발생
ex) print(4/0);, 길이가3인 배열의 5인덱스 값을 출력하라 등
try - catch 블럭을 통해 예외 처리 구현이 가능하다.
try {
// 예외가 발생할 가능성이 있는 코드를 삽입
}
catch (ExceptionType1 e1) {
// ExceptionType1 유형의 예외 발생 시 실행할 코드
}
catch (ExceptionType2 e2) {
// ExceptionType2 유형의 예외 발생 시 실행할 코드
}
finally {
// finally 블럭은 옵셔널
// 예외 발생 여부와 상관없이 항상 실행
}
catch 블럭이 여러 개인 경우, 일치하는 하나의 catch 블럭만이 실행되고 예외처리 코드가 종료되거나 finally 블럭으로 넘어간다.
만약에 일치하는 블럭을 찾지 못하는 경우에는 예외는 처리되지 못한다.
try-catch 문 외에 예외를 호출한 곳으로 다시 예외를 떠넘기는 방법
반환타입 메서드명(매개변수, ...) throws 예외클래스1, 예외클래스2, ... {
...생략...
}
모든 종류의 예외가 발생할 가능성이 있는 경우 아래와 같이 작성
void ExampleMethod() throws Exception {
}
아래와 같이 throws 키워드를 사용하여 해당 예외를 발생한 메서드 안에서 처리하지 않고 메서드를 호출한 곳으로 다시 떠넘길 수 있다.
ublic class ThrowExceptionTest {
public static void main(String[] args) {
try {
throwException();
} catch (ClassNotFoundException e) {
System.out.println(e.getMessage());
}
}
static void throwException() throws ClassNotFoundException, NullPointerException {
Class.forName("java.lang.StringX");
}
}
//출력값
java.lang.StringX
throws 키워드와 유사한 throw 키워드를 사용하면 의도적으로 예외를 발생시킬 수 있다
public class ExceptionTest {
public static void main(String[] args) {
try {
Exception intendedException = new Exception("의도된 예외 만들기");
throw intendedException;
} catch (Exception e) {
System.out.println("고의로 예외 발생시키기 성공!");
}
}
}
특정 자료 구조에 데이터를 추가하고, 삭제하고, 수정하고, 검색하는 등의 동작을 수행하는 편리한 메서드들을 미리 정의해놓은 것
List
데이터의 순서가 유지되며, 중복 저장이 가능한 컬렉션을 구현하는 데에 사용
ArrayList, Vector, Stack, LinkedList 등
Set
데이터의 순서가 유지되지 않으며, 중복 저장이 불가능한 컬렉션을 구현하는 데에 사용
HashSet, TreeSet 등
Map
키(key)와 값(value)의 쌍으로 데이터를 저장하는 컬렉션을 구현하는 데에 사용
데이터의 순서가 유지되지 않으며, 키는 값을 식별하기 위해 사용되므로 중복 저장이 불가능하지만, 값은 중복 저장이 가능하다.
HashMap, HashTable, TreeMap, Properties 등
List와 Set은 서로 공통점이 많아 위 그림과 같이 Collection이라는 인터페이스로 묶인다. 즉, 이 둘의 공통점이 추출되어 추상화된 것이 바로 Collection이라는 인터페이스다.
기능 | 리턴 타입 | 메소드 | 설명 |
---|---|---|---|
객체 추가 | boolean | add(Object o) / addAll(Collection c) | 주어진 객체 및 컬렉션의 객체들을 컬렉션에 추가 |
객체 검색 | boolean | contains(Object o) / containsAll(Collection c) | 주어진 객체 및 컬렉션이 저장되어 있는지 여부를 리턴 |
Iterator | iterator() | 컬렉션의 iterator를 리턴 | |
boolean | equals(Object o) | 컬렉션이 동일한지 여부를 확인 | |
boolean | isEmpty() | 컬렉션이 비어있는지 여부를 확인 | |
int | size() | 저장되어 있는 전체 객체 수를 리턴 | |
객체 삭제 | void | clear() | 컬렉션에 저장된 모든 객체를 삭제 |
boolean | remove(Object o) / removeAll(Collection c) | 주어진 객체 및 컬렉션을 삭제하고 성공 여부를 리턴 | |
boolean | retainAll(Collection c) | 주어진 컬렉션을 제외한 모든 객체를 컬렉션에서 삭제하고, 컬렉션에 변화가 있는지의 여부를 리턴 | |
객체 변환 | Object[] | toArray() | 컬렉션에 저장된 객체를 객체배열(Object [])로 반환 |
Object[] | toArray(Object[] a) | 주어진 배열에 컬렉션의 객체를 저장해서 반환 |
List<E>
기능 | 리턴 타입 | 메서드 | 설명 |
---|---|---|---|
객체 추가 | void | add(int index, Object element) | 주어진 인덱스에 객체를 추가 |
boolean | addAll(int index, Collection c) | 주어진 인덱스에 컬렉션을 추가 | |
Object | set(int index, Object element) | 주어진 위치에 객체를 저장 | |
객체 검색 | Object | get(int index) | 주어진 인덱스에 저장된 객체를 반환 |
int | indexOf(Object o) / lastIndexOf(Object o) | 순방향 / 역방향으로 탐색하여 주어진 객체의 위치를 반환 | |
ListIterator | listIterator() / listIterator(int index) | List의 객체를 탐색할 수 있는ListIterator 반환 / 주어진 index부터 탐색할 수 있는 ListIterator 반환 | |
List | subList(int fromIndex, int toIndex) | fromIndex부터 toIndex에 있는 객체를 반환 | |
객체 삭제 | Object | remove(int index) | 주어진 인덱스에 저장된 객체를 삭제하고 삭제된 객체를 반환 |
boolean | remove(Object o) | 주어진 객체를 삭제 | |
객체 정렬 | void | sort(Comparator c) | 주어진 비교자(comparator)로 List를 정렬 |
ArrayList<타입 매개변수> 객체명 = new ArrayList<타입 매개변수>(초기 저장 용량);
ArrayList<String> container1 = new ArrayList<String>();
// String 타입의 객체를 저장하는 ArrayList 생성
// 초기 용량이 인자로 전달되지 않으면 기본적으로 10으로 지정
ArrayList<String> container2 = new ArrayList<String>(30);
// String 타입의 객체를 저장하는 ArrayList 생성
// 초기 용량을 30으로 지정
ArrayList<Integer> container3 = new ArrayList<>(container1);
// 다른 Collection값으로 초기화
ArrayList<Integer> container4 = new ArrayList<>(Arrays.asList(배열명);
// Arrays.asList()
// 배열의 요소와 같은 객체로 구성 된 ArrayList 만들기
ArrayList에 String 객체를 추가, 검색, 삭제해보았다.
public class ArrayListExample {
public static void main(String[] args) {
// ArrayList를 생성하여 list에 할당
ArrayList<String> list = new ArrayList<String>();
// 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);
//삭제 후 list
for (String str: list) {
System.out.println( "AfterDeleteList :"+str);
}
}
}
//출력
0:Java
1:egg
2:tree
Java
egg
tree
AfterDeleteList :egg
AfterDeleteList :tree
공식문서에서 다양한 메서드를 찾을 수 있다. 암기x 필요할 때 찾아 쓰면서 익히자.
LinkedList의 각 요소(node)들은 자신과 연결된 이전 요소 및 다음 요소의 주소값과 데이터로 구성되어 있다.
요소(데이터) 하나를 삭제하면, 삭제하고자 하는 요소의 이전 요소가 삭제하고자 하는 요소의 다음 요소를 참조하도록 변경 된다. (링크를 끊어주는 방식)
이 밑에 작성한거 다 날아가서 두번째 쓰는 중...하...
public static void main(String[] args) {
// Linked List를 생성하여 list에 할당
LinkedList<String> list = new LinkedList<>();
// String 타입의 데이터를 LinkedList에 추가
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);
for (String str: list) {
System.out.println("AD :" + str);
}
}
}
//출력
0:Java
1:egg
2:tree
Java
egg
tree
AD :egg
AD :tree
ArrayList예제와 결과는 같다.
ArrayList
LinkedList
결론적으로, 데이터의 잦은 변경이 예상된다면 LinkedList를, 데이터의 개수가 변하지 않는다면 ArrayList를 사용하는 것이 좋다.
컬렉션에 저장된 요소들을 순차적으로 읽어오는 역할
Collection 인터페이스에는 Iterator 인터페이스를 구현한 클래스의 인스턴스를 반환하는 메서드인 iterator()가 정의되어져 있다.
Iterator 인터페이스에 정의된 메서드
(iterator()를 통해 만들어진 인스턴스는 아래의 메서드를 사용할 수 있다.)
메서드 | 설명 |
---|---|
hasNext() | 읽어올 객체가 남아 있으면 true를 리턴하고, 없으면 false를 리턴한다. |
next() | 컬렉션에서 하나의 객체를 읽어온다. 이 때, next()를 호출하기 전에 hasNext()를 통해 읽어올 다음 요소가 있는지 먼저 확인해야 한다. |
remove() | next()를 통해 읽어온 객체를 삭제한다. next()를 호출한 다음에 remove()를 호출해야 한다. |
ArrayList<String> list = ...;
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) { // 읽어올 다음 객체가 있다면
String str = iterator.next(); // next()를 통해 다음 객체를 읽어옵니다.
...
}
위아래 코드가 같은거다.
ArrayList<String> list = ...;
for(String str : list) {
...
}
remove도 적용해 보았다.
ArrayList<String> list = ...;
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){ // 다음 객체가 있다면
String str = iterator.next(); // 객체를 읽어오고,
if(str.equals("str과 같은 단어")){ // 조건에 부합한다면
iterator.remove(); // 해당 객체를 컬렉션에서 제거합니다.
}
}
개인적으로 아직 까지는 ArrayList로 다 할 수 있는 기능들이라 크게 필요성이 느껴지진 않는다.
Set<E>
요소의 중복을 허용하지 않고, 저장 순서를 유지하지 않는 컬렉션
기능 | 리턴 타입 | 메서드 | 설명 |
---|---|---|---|
객체 추가 | boolean | add(Object o) | 주어진 객체를 추가하고, 성공하면 true를, 중복 객체면 false를 반환한다. |
객체 검색 | boolean | contains(Object o) | 주어진 객체가 Set에 존재하는지 확인한다. |
boolean | isEmpty() | Set이 비어있는지 확인한다. | |
Iterator | Iterator() | 저장된 객체를 하나씩 읽어오는 반복자를 리턴한다. | |
int | size() | 저장되어 있는 전체 객체의 수를 리턴한다. | |
객체 삭제 | void | clear() | Set에 저장되어져 있는 모든 객체를 삭제한다. |
boolean | remove(Object o) | 주어진 객체를 삭제한다. |
public static void main(String[] args) {
HashSet<String > languages = new HashSet<String>();
// HashSet에 객체 추가
languages.add("Java");
languages.add("Python");
languages.add("Javascript");
languages.add("C++");
languages.add("Kotlin");
languages.add("Ruby");
languages.add("Java"); // 중복
// 반복자 생성하여 it에 할당
Iterator it = languages.iterator();
// 반복자를 통해 HashSet을 순회하며 각 요소들을 출력
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
//출력
Java
C++
Javascript
Ruby
Python
Kotlin
“Java”를 두 번 추가했지만 한 번만 저장됨.
이진 탐색 트리 형태로 데이터를 저장한다.
이진 탐색 트리(Binary Search Tree)
최상위 노드를 ‘루트'라고 한다.
이진 탐색 트리는 모든 왼쪽 자식의 값이 루트나 부모보다 작고, 모든 오른쪽 자식의 값이 루트나 부모보다 큰 값을 가지는 특징이 있다.
위 그림의 각 노드들은 아래 Node 클래스를 인스턴스화한 인스턴스에 해당한다.
이러한 자료 구조를 구현하기 위한 의사 코드는 아래와 같다.
class Node {
Object element; // 객체의 주소값을 저장하는 참조변수 입니다.
Node left; // 왼쪽 자식 노드의 주소값을 저장하는 참조변수입니다.
Node right; // 오른쪽 자식 노드의 주소값을 저장하는 참조변수입니다.
}
적용 예제
import java.util.TreeSet;
public class TreeSetExample {
public static void main(String[] args) {
// TreeSet 생성
TreeSet<String> workers = new TreeSet<>();
// TreeSet에 요소 추가
workers.add("Lee Java");
workers.add("Park Hacker");
workers.add("Kim Coding");
System.out.println(workers);
System.out.println(workers.first());
System.out.println(workers.last());
System.out.println(workers.higher("Lee")); //입력값보다 큰 데이터중 최소값 출력 없으면 null
System.out.println(workers.subSet("Kim", "Park")); // Kim부터 Part까지의 객체들을 반환 (Park는 미포함)
}
}
//출력
[Kim Coding, Lee Java, Park Hacker]
Kim Coding
Park Hacker
Lee Java
[Kim Coding, Lee Java]
자동으로 사전 편찬 순에 따라 오름차순으로 정렬됐다.
자료 출처
키(key)와 값(value)으로 구성된 객체를 저장하는 구조
키(key)와 값(value)으로 구성된 객체 = Entry객체
키는 중복 저장될 수 없지만, 값은 중복 저장이 가능하다
(List는 인덱스를 기준으로 관리되는 반면, Map은 키(key)로 객체들을 관리하기 때문에 키를 매개값으로 갖는 메서드가 많다.)
기능 | 리턴 타입 | 메서드 | 설명 |
---|---|---|---|
객체 추가 | Object | put(Object key, Object value) | 주어진 키로 값을 저장합니다. 해당 키가 새로운 키일 경우null을 리턴하지만, 동일한 키가 있을 경우에는 기존의 값을대체하고 대체되기 이전의 값을 리턴한다. |
객체 검색 | boolean | containsKey (Object key) | 주어진 키가 있으면 true, 없으면 false를 리턴한다. |
boolean | containsValue (Object value) | 주어진 값이 있으면 true, 없으면 false를 리턴한다. | |
Set | entrySet() | 키와 값의 쌍으로 구성된 모든 Map.Entry 객체를 Set에 담아서 리턴한다. | |
Object | get(Object key) | 주어진 키에 해당하는 값을 리턴한다. | |
boolean | isEmpty() | 컬렉션이 비어 있는지 확인한다. | |
Set | keySet() | 모든 키를 Set 객체에 담아서 리턴한다. | |
int | size() | 저장된 Entry 객체의 총 갯수를 리턴한다. | |
Collection | values() | 저장된 모든 값을 Collection에 담아서 리턴한다. | |
객체 삭제 | void | clear() | 모든 Map.Entry(키와 값)을 삭제한다. |
Object | remove(Object key) | 주어진 키와 일치하는 Map.Entry를 삭제하고 값을 리턴한다. |
리턴 타입 | 메서드 | 설명 |
---|---|---|
boolean | equals(Object o) | 동일한 Entry 객체인지 비교합니다. |
Object | getKey() | Entry 객체의 Key 객체를 반환합니다. |
Object | getValue() | Entry 객체의 Value 객체를 반환합니다. |
int | hashCode() | Entry 객체의 해시코드를 반환합니다. |
Object | setValue(Object value) | Entry 객체의 Value 객체를 인자로 전달한 value 객체로 바꿉니다. |
HashMap을 생성시 키의 타입(String), 값의 타입(Integer)을 따로 지정해야 한다.
HashMap<String, Integer> hashmap = new HashMap<>();
예시
import java.util.*;
public class HashMapExample {
public static void main(String[] args) {
// HashMap 생성
HashMap<String, Integer> map = new HashMap<>();
// Entry 객체 저장
map.put("피카츄", 85);
map.put("꼬부기", 95);
map.put("야도란", 75);
map.put("파이리", 65);
map.put("피존투", 15);
// 저장된 총 Entry 수 얻기
System.out.println("총 entry 수: " + map.size());
// 객체 찾기
System.out.println("파이리 : " + map.get("파이리"));
System.out.println("-".repeat(60));
// key를 요소로 가지는 Set을 생성 -> 아래에서 순회하기 위해 필요
Set<String> keySet = map.keySet();
// keySet을 순회하면서 value를 읽기
Iterator<String> keyIterator = keySet.iterator();
while(keyIterator.hasNext()) {
String key = keyIterator.next();
Integer value = map.get(key);
System.out.println(key + " : " + value);
}
System.out.println("-".repeat(60));
// 객체 삭제
map.remove("피존투");
System.out.println("총 entry 수: " + map.size());
System.out.println("-".repeat(60));
// Entry 객체를 요소로 가지는 Set을 생성 -> 아래에서 순회하기 위해 필요
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
// entrySet을 순회하면서 value를 읽기
Iterator<Map.Entry<String, Integer>> entryIterator = entrySet.iterator();
while(entryIterator.hasNext()) {
Map.Entry<String, Integer> entry = entryIterator.next();
String key = entry.getKey(); // Map.Entry 인터페이스의 메서드
Integer value = entry.getValue(); // Map.Entry 인터페이스의 메서드
System.out.println(key + " : " + value);
}
// 객체 전체 삭제
map.clear();
}
}
//출력
총 entry 수: 5
파이리 : 65
------------------------------------------------------------
야도란 : 75
꼬부기 : 95
파이리 : 65
피카츄 : 85
피존투 : 15
------------------------------------------------------------
총 entry 수: 4
------------------------------------------------------------
야도란 : 75
꼬부기 : 95
파이리 : 65
피카츄 : 85
종료 코드 0(으)로 완료된 프로세스
Map은 키와 값을 쌍으로 저장하기 때문에 iterator()를 직접 호출할 수 없다.
그 대신 keySet() 이나 entrySet() 메서드를 이용해 Set 형태로 반환된 컬렉션에 iterator()를 호출하여 반복자를 만든 후, 반복자를 통해 순회할 수 있다.
Hashtable은 HashMap과 내부 구조가 동일하며, 사용 방법 또한 매우 유사하다.
(cf. 둘의 차이점은 스레드와 관련 있는데 아직 안배웠으니 일단 skip)
로그인 기능 예제
import java.util.*;
public class HashtableExample {
public static void main(String[] args){
Hashtable<String, String> map = new Hashtable<String, String>();
map.put("Spring", "345");
map.put("Summer", "678");
map.put("Fall", "91011");
map.put("Winter", "1212");
System.out.println(map);
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("아이디와 비밀번호를 입력해 주세요");
System.out.println("아이디");
String id = scanner.nextLine();
System.out.println("비밀번호");
String password = scanner.nextLine();
if (map.containsKey(id)) {
if (map.get(id).equals(password)) {
System.out.println("로그인 되었습니다.");
break;
}
else System.out.println("비밀번호가 일치하지 않습니다. ");
}
else System.out.println("입력하신 아이디가 존재하지 않습니다.");
}
}
}
Enum의 기본적인 개념
Enum이 등장하게 된 배경
Enum의 장점
제네릭의 장점
제네릭 클래스의 정의
class Basket<T> {
private T item;
...
}
class Basket {
...
public <T> void add(T element) {
...
}
}
프로그래밍에서 예외 처리란?
컴파일 에러와 런타임 에러의 차이
try-catch 문과 예외 전가
throws 키워드와 throw 키워드의 차이
인스턴스 타입이 제한되는 경우 때문에 Interator을 쓰는건가?