이전 내용
[java] 스레드 제어
이전 내용 [java] java 스레드(thread) - 멀티, 메인, 작업 / 동기화 메소드이전 내용 [java] java.lang 패키지이전 내용 [java] 예외 (클래스, 처리), 다중 catch, 예외 떠넘기기, String args[]이전 내용 [java] 인
puppy-foot-it.tistory.com
컬렉션 프레임워크
자료 구조 종류의 형태들을 자바 클래스로 구현한 모음집
◆ 컬렉션 프레임워크(Collection Framework)
자료구조를 사용해서 객체들을 효율적으로 추가, 삭제, 검색할 수 있도록 인터페이스와 구현 클래스를 java.util 패키지에서 제공하는 것. ▶ 여러 개의 값을 저장하고, 그 값을 쉬우면서도 효율적으로 처리해주는 표준화 클래스의 집합.
- 컬렉션: 객체의 저장
- 프레임워크: 사용 방법을 정해놓은 라이브러리
<컬렉션 프레임워크 구현 클래스>
대표적인 컬렉션 프레임워크에는 리스트 ArrayList, 스택 Stack, 큐 Queue, 데크 ArrayDeque, 해시맵 HashMap 등이 있다.
<주요 인터페이스>
- Collection: 모든 컬렉션 클래스의 최상위 인터페이스. 리스트, 세트, 큐 등 다양한 하위 인터페이스가 포함.
- List: 순서가 있는 컬렉션으로, 중복 요소를 허용. 예: ArrayList, LinkedList 등.
- Set: 중복 요소를 허용하지 않는 컬렉션. 예: HashSet, TreeSet 등.
- Map: 키-값 쌍으로 데이터를 저장하는 컬렉션. 중복된 키를 허용하지 않으며, 예: HashMap, TreeMap 등.
<주요 클래스>
- ArrayList: 동적 배열로서 요소의 추가, 삭제 용이. 그러나 배열보다 요소 접근 속도가 느릴 수 있다.
- LinkedList: 요소들이 노드로 연결된 형태로, 요소의 추가 및 삭제가 빈번할 경우 유리.
- HashSet: 해시 함수를 사용하여 요소를 저장하며, 빠른 검색 속도를 가진다.
- TreeSet: 이진 트리 구조로 데이터를 정렬하여 저장.
- HashMap: 키-값 쌍으로 데이터를 저장하며, 빠른 검색 속도 제공.
[컬렉션 프레임워크 장점]
- 인터페이스와 다형성을 이용한 객체지향적 설계를 통해 표준화되어 있어, 사용법을 익히기에도 편리하고 재사용성이 높다.
- 데이터 구조 및 알고리즘의 고성능 구현을 제공하여 프로그램의 성능과 품질 향상
- 관련 없는 API 간의 상호 운용성 제공
- 새로운 API 익히고 설계하는 시간 단축 (이미 구현되어있는 API 사용)
- 소프트웨어 재사용 촉진
※ 컬렉션 프레임워크에 저장할 수 있는 데이터는 객체(Object) 뿐으로, int 형이나 double 형 같은 자바의 기본 타입은 적재를 못한다. 따라서 기본 타입을 wrapper 타입으로 변환 후 박싱하여 저장하여야 한다. 또한, 주소값을 담을 수 있기 때문에 null도 저장 가능하다.
List 컬렉션
[배열과 리스트의 차이]
- 크기:
- 배열: 고정된 크기를 가지며, 생성 시 크기를 설정해야 합니다. 크기를 변경할 수 없다.
- 리스트: 동적 크기를 가지며, 요소가 추가되거나 삭제될 때 자동으로 크기가 조정된다. (ArrayList, LinkedList 등)
- 메모리 할당:
- 배열: 메모리가 연속적으로 할당되며, 요소의 접근이 빠르다.
- 리스트: 메모리가 분산될 수 있으며, 노드 기반의 구현(특히 LinkedList)은 요소 간의 연결로 인해 추가 및 삭제가 빠르다.
- 요소 접근:
- 배열: 인덱스를 통해 즉시 접근이 가능하며, O(1)의 시간 복잡도를 가진다.
- 리스트: ArrayList는 인덱스를 통한 접근이 가능하지만, LinkedList는 노드를 따라가야 하므로 O(n)의 시간 복잡도를 가진다.
- 기능:
- 배열: 기본적인 기능만 제공하며, 크기 변경이나 복잡한 작업을 하려면 수동으로 구현해야 한다.
- 리스트: 다양한 메서드와 기능을 제공하여 정렬, 검색, 추가, 삭제 등을 쉽게 처리할 수 있다.
- 데이터 타입:
- 배열: 동일한 데이터 타입의 요소만 저장할 수 있다.
- 리스트: 제네릭을 사용하여 서로 다른 데이터 타입을 저장할 수 있는 유연성을 제공한다.
◆ List 컬렉션 종류
- ArrayList: List 인터페이스의 대표적인 구현 클래스
// ArrayList 생성
List<자료형> list 변수명 = new ArrayList<자료형>();
List<자료형> list 변수명 = new ArrayList<>();
import java.util.ArrayList;
import java.util.List;
public class ArrayListExample {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("Java"); // String 객체 저장
list.add("JDBC");
list.add("Servlet/JSP");
list.add(2, "Database");
list.add("iBATIS");
list.add("React");
System.out.println("set: " + list.set(2, "Python"));
int size = list.size(); // 저장된 총 객체 수 얻기
System.out.println("총 객체수: " + size);
System.out.println();
String skill = list.get(2); // 2번 인덱스 객체 얻기
System.out.println("2: " + skill);
System.out.println();
for (int i = 0; i < list.size(); i++) {
String str = list.get(i);
System.out.println(i + ": " + str); // 저장된 총 객체수 만큼 루프
}
System.out.println();
list.remove(2); // 2번 인덱스 객체 삭제됨
list.remove(2); // 변경된 리스트 중 2번 인덱스 객체 삭제됨
list.remove("iBATIS");
for (int i = 0; i < list.size(); i++) {
String str = list.get(i);
System.out.println(i + ": " + str); // 저장된 총 객체수 만큼 루프
}
System.out.println();
}
}
- Vector: Vector는 ArrayList와 동일한 내부 구조를 가지고 있는데, ArrayList와 다른 점은 Vector는 동기화된 메소드로 구성되어 있어 멀티 스레드가 동시에 Vector의 메소드들을 실행할 수 없고, 하나의 스레드가 메소드를 실행을 완료해야만 다른 스레드가 메소드를 실행할 수 있다.
List<객체 타입> list = new Vector<객체 타입>();
List<객체 타입> list = new Vector<>();
- LinkedList: ArrayList와 사용 방법은 똑같으나, 내부 구조는 완전히 다르다.
- ArrayList: 내부 배열에 객체 저장하여 관리
- LinkedList: 인접 참조를 링크해서 체인처럼 관리
List<객체 타입> list = new LinkedList<객체 타입>();
List<객체 타입> list = new LinkedList<>();
▶ ArrayList는 중간 인덱스의 객체를 제거하면 뒤에 있는 객체의 인덱스가 1씩 앞으로 당겨지고, LinkedList에서는 특정 인덱스의 객체를 제거하면 앞뒤 링크만 변경되고 나머지 링크는 변경되지 않는다. 특정 인덱스에 객체를 삽입할 때도 마찬가지다.
구분 | 순차적 추가/삭제 | 중간 추가/삭제 | 검색 |
ArrayList | 빠름 | 느림 | 빠름 |
LinkedList | 느림 | 빠름 | 느림 |
[List 인터페이스 메소드]
List 컬렉션에서 공통적으로 사용 가능한 메소드: add(), remove(), get() 등
기능 | 메소드 | 설명 |
객체 추가 | add(요소) | 주어진 객체를 맨 끝에 추가 |
add(int 인덱스, 요소) | 주어진 인덱스에 객체 추가 | |
set (int 인덱스, 요소) | 주어진 인덱스에 저장된 객체를 주어진 객체로 바꿈 | |
객체 검색 | contains(객체) | 주어진 객체가 저장되어 있는지 조사 |
get(int 인덱스) | 주어진 인덱스에 저장된 객체를 리턴 | |
isEmpty() | 컬렉션이 비어있는지 조사 | |
int size() | 저장되어 있는 전체 객체 수 리턴 | |
객체 삭제 | clear() | 저장된 모든 객체 삭제 |
remove(int 인덱스) | 주어진 인덱스에 저장된 객체 삭제 | |
remove(객체) | 주어진 객체 삭제 |
Set 컬렉션
[Set 컬렉션 vs List 컬렉션]
기준 | Set 컬렉션 | List 컬렉션 |
저장 순서 | 비유지 | 유지 |
중복 여부 | 불가 | 가능 |
◆ Set 컬렉션 종류
- HashSet: Set 인터페이스의 구현 클래스. 객체들을 순서 없이 저장하고 동일한 객체는 중복 저장하지 않음.
HashSet은 객체를 저장하기 전에 먼저 객체의 hashCode() 메소드를 호출해서 해시코드를 얻어내고, 이미 저장되어 있는 객체들의 해시코드와 비교한다. 만약 동일 해시코드가 있다면 다시 eqauls() 메소드로 두 객체를 비교한다.
Set<타입> set = new HashSet<타입>();
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class HashSetExample {
public static void main(String[] args) {
Set<String> set = new HashSet<String>();
set.add("Java");
set.add("JDBC");
set.add("Servlet/JSP");
set.add("Java");
set.add("iBATIS");
int size = set.size();
System.out.println("총 객체수: " + size);
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
System.out.println("\t" + element);
}
set.remove("JDBC");
set.remove("iBATIS");
System.out.println("총 객체수: " + size);
iterator = set.iterator();
for (String element : set) {
System.out.println("\t" + element);
}
set.clear();
if (set.isEmpty()) {
System.out.println("비어 있음");
}
}
}
- LinkedHashSet: LinkedHashSet은 요소의 삽입 순서를 유지한다. 즉, 데이터를 삽입한 순서대로 요소를 반복 ▶ 일반적인 삽입/삭제/검색에 더 빠름
- TreeSet: TreeSet은 자연 순서(Comparable)을 따르거나 사용자가 제공하는 Comparator에 따라 요소를 정렬한다. ▶ 정렬이 필요한 경우에 유리하며, 정렬된 결과가 필요할 때 주로 사용한다.
[Set 인터페이스 메소드]
기능 | 메소드 | 설명 |
객체 추가 | add(요소) | 주어진 객체를 맨 끝에 추가 (객체 저장: true, 중복인 경우: false) |
객체 검색 |
contains(객체) | 주어진 객체가 저장되어 있는지 조사 |
isEmpty() | 컬렉션이 비어있는지 조사 | |
int size() | 저장되어 있는 전체 객체 수 리턴 | |
Iterator<타입> iterator() | 저장된 객체를 한 번씩 가져오는 반복자 리턴 | |
객체 삭제 | clear() | 저장된 모든 객체 삭제 |
remove(객체) | 주어진 객체 삭제 |
▶ Set 컬렉션은 저장 순서가 유지되지 않기 때문에, 인덱스로 객체를 검색해서 가져오는 메소드가 없다. 대신에 전체 객체를 대상으로 한 번씩 반복해서 가져오는 반복자인 Iterator를 제공한다.
◆ 반복자(Iterator)
// Set 컬렉션 생성: Set<타입> 컬렉션 변수명 = new Set<타입>();
Set<String> set = new Set<String>();
//Iterator<타입> 반복자명 = Set 컬렉션 변수명.반복자명();
Iterator<String> it = set.it();
[Iterator 인터페이스에 선언된 메소드]
리턴 타입 | 메소드 | 설명 |
boolean | hasNext() | 가져올 객체 있음: true / 없음: false |
객체 | next() | 컬렉션에서 하나의 객체 가져옴 |
void | remove() | Set 컬렉션에서 객체 제거 |
Map 컬렉션
Map 컬렉션은 자바에서 제공하는 데이터 구조로, 키(Key)와 값(Value) 쌍으로 데이터를 저장한다. 이 컬렉션은 키를 사용하여 값을 빠르게 검색할 수 있도록 설계되어 있다.
Map 컬렉션은 키와 값으로 구성된 Map.Entry 객체를 저장하는 구조를 가지는데, Entry는 Map 인터페이스 내부에 선언된 중첩 인터페이스이다.
[주요 특징]
- 키-값 쌍: 각 데이터 항목은 하나의 키와 그에 대응하는 값으로 구성.
- 고유한 키: Map에서 사용하는 키는 중복될 수 없으며, 동일한 키에 대해서는 오직 하나의 값만 존재. ▶ 값은 중복 저장될 수 있다.
- 빠른 검색: 키를 통해 값을 빠르게 찾아낼 수 있다.
- 정렬되지 않음: 기본적으로 Map은 순서가 유지되지 않기 때문에, 키의 순서는 영향받지 않는다.
◆ Map 컬렉션 종류
- HashMap: Map 인터페이스를 구현한 대표적인 Map 컬렉션. HashMap의 키로 사용할 객체는 hashCode()와 equals() 메소드를 재정의해서 동등 객체가 될 조건을 정해야 한다. ▶ 객체가 달라도 동등 객체(hashCode()의 리턴값이 같고, equals() 메소드가 true) 라면 같은 키로 간주하고 중복 저장하지 않기 위함.
Map<K, V> map = new HashMap(K, V>();
키와 값은 타입은 기본 타입을 사용할 수 없고, 클래스 및 인터페이스 타입만 사용 가능하다.
(byte, short, int, float, double, boolean, char 등의 기본 타입 사용 불가)
※ 키: String 타입, 값: Integer 타입을 사용하는 HashMap
Map<String, Integer> map = new HashMap<String, Integer>();
Map<String, Integer> map = new HashMap<>();
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class HashMapExample {
public static void main(String[] args) {
// Map 컬렉션 생성
Map<String, Integer> map = new HashMap<>();
// 객체 저장
map.put("김철수", 85);
map.put("홍길동", 92);
map.put("이영희", 75);
map.put("오미자", 65);
System.out.println("총 Entry 수: " + map.size());
// 객체 찾기
System.out.println("\t 이영희: " + map.get("이영희"));
System.out.println();
// 객체를 하나씩 처리하기
Set<String> keySet = map.keySet(); // Key Set 얻기
Iterator<String> keyIterator = keySet.iterator();
while (keyIterator.hasNext()) {
String key = keyIterator.next();
Integer value = map.get(key);
System.out.println("\t" + key + ": " + value);
}
System.out.println();
// 객체 삭제
map.remove("홍길동");
System.out.println("총 Entry 수: " + map.size());
// 객체를 하나씩 처리하기
Set<Map.Entry<String, Integer>> entrySet = map.entrySet(); // Map.Entry Set 얻기
Iterator<Map.Entry<String, Integer>> entryIterator = entrySet.iterator();
while (entryIterator.hasNext()) {
Map.Entry<String, Integer> entry = entryIterator.next();
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println("\t" + key + ": " + value);
}
System.out.println();
// 객체 전체 삭제
map.clear();
System.out.println("총 Entry 수: " + map.size());
}
}
- Hashtable: HashMap과 동일한 내부 구조를 가지고 있으나, Hashtable은 동기화된 메소드로 구성되어 있기 때문에 멀티 스레드가 동시에 Hashtable의 메소드들을 실행할 수 없고, 하나의 스레드가 실행을 완료해야만 다른 스레드를 실행할 수 있다.
또한, null 키와 null 값을 허용하지 않는다.
Map<K, V> map = new Hashtable<K, V>();
- LinkedHashMap: 키-값 쌍으로 데이터를 저장하며, 삽입 순서를 유지하며, null 키와 null 값을 허용.
- Properties: Hashtable을 상속받으며, 문자열 키와 문자열 값을 사용하는 특수한 형태의 Map. 주로 구성(Configuration) 데이터를 저장하는 데 사용. load()와 store() 메서드가 있어 파일 입출력이 간편하게 이루어질 수 있다.
- TreeMap: 키-값 쌍으로 데이터를 저장하며, 자연 순서(Comparable) 또는 사용자 정의 Comparator에 따라 정렬된 상태로 유지된다. null 키를 허용치 않으나, null 값은 허용한다.
[Map 인터페이스 메소드]
V: Value(값), K: Key(키)
기능 | 메소드 | 설명 |
객체 추가 | V put(K key, V value) | 주어진 키로 값 저장. 새로운 키: null 리턴 / 동일한 키: 기존 값 대체하고 이전 값 리턴 |
객체 검색 | boolean containsKey(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 삭제하고 값 리턴 |
제네릭(Generics)
◆ 제네릭: 클래스와 인터페이스, 메서드를 정의할 때 데이터 타입을 파라미터로 받을 수 있게 해주는 기능
- 제네릭을 사용하면 코드에서 사용할 데이터 타입을 미리 정의할 수 있다.
- 제네릭을 이용하면 여러 데이터 타입에 대해 동일한 알고리즘이나 코드를 재사용할 수 있다.
List<타입> list = new ArrayList<타입>();
예제1
Q. 학생 성적 관리 시스템
[요구 사항]
1. Student 클래스
- 속성: name(문자열), score(정수)
- 생성자: 이름과 성적 초기화
- getter: 이름과 성적 반환
- toString(): 객체 정보를 문자열로 반환 (예: "이름: 홍길동, 성적: 85")
2. StudentManager 클래스
- ArrayList를 제네릭으로 사용해 Student 객체 저장
- 기능
- 학생 추가: 새로운 학생을 리스트에 추가
- 모든 학생 출력: 리스트에 저장된 모든 학생 정보 출력
- 평균 성적 계산: 모든 학생의 성적 평균을 계산해 출력
3. 메인 메서드
- 최소 3명의 학생을 추가하고, 모든 학생 정보와 평균 성적을 출력
public class Student {
// 필드
private String name;
private int score;
// 생성자
Student(String name, int score) {
this.name = name;
this.score = score;
}
// getter
public String getSname() {
return name;
}
public int getScore() {
return score;
}
// toString
@Override
public String toString() {
return "이름: " + name + ", 성적: " + score;
}
}
import java.util.ArrayList;
import java.util.List;
public class StudentManager {
public static void main(String[] args) {
Student s1 = new Student("홍길동", 85);
Student s2 = new Student("김영희", 92);
Student s3 = new Student("이철수", 78);
List<Student> list = new ArrayList<Student>();
list.add(s1);
list.add(s2);
list.add(s3);
System.out.println("=== 모든 학생 정보===");
for (Student s : list) {
System.out.println(s);
}
// 평균 성적 정보
double totalScore = 0;
for (Student s : list) {
totalScore += s.getScore();
}
System.out.println("평균 성적: " + (totalScore / list.size()));
}
}
★ toString() 오버라이딩과 참조 변수, print의 관계
@Override
public String toString() {
return "이름: " + name + ", 성적: " + score;
}
for (Student s : list) {
System.out.println(s);
}
▶ toString() 메소드는 Student 클래스에 오버라이드되어 학생 객체의 이름과 성적을 담은 문자열을 반환하도록 설정되어 있다.
이렇게 하면, System.out,println(s); 는 실제로 s.toString()을 자동으로 호출한다.
- 학생 클래스: Student 클래스는 학생의 이름과 성적을 가지고 있다.
- toString() 메서드: 이 메서드는 학생의 정보를 출력할 형식으로 바꾼다.
- 학생 리스트: list라는 리스트에 학생 객체들을 추가한다.
- 출력: System.out.println(s);는 s라는 학생 객체의 toString()을 호출하여 정보를 출력한다.
★ 제네릭
Student s1 = new Student("홍길동", 85);
Student s2 = new Student("김영희", 92);
Student s3 = new Student("이철수", 78);
List<Student> list = new ArrayList<Student>();
ArrayList를 Student 타입으로 지정함으로써, 리스트 안에 학생 이름과 성적이 같이 들어갈 수 있다.
솔직히, 필자는 제네릭에 관한 개념이 부족했어서, 아래와 같이 학생과 성적 각각의 리스트를 만들어서 따로 집어넣고
List<String> list1 = new ArrayList<String>();
List<int> list2 = new ArrayList<int>();
나중에 합쳐야 하나? 이런 식으로 잘못 접근했었다. 제네릭이라는 개념에 대해 알고 나니, 그 이후 과정은 매우 쉽게 진행되었다.
내가 알고 있던 자바에서의 배열은 크기 변경이 안 되고, 다른 타입을 하나의 리스트에 집어넣을 수 없기 때문이었고, 이러한 단점을 개선해주는 개념이 리스트와 제네릭 이라는 것이 었는데, 해당 개념이 잘 이해되지 않았던 거 같다.
이로인해 제네릭이라는 개념이 잘 이해되었다.
예제2
Q. 도서관 책 대여 관리 시스템
도서관에서 책 대여를 관리하려고 한다. 각 책은 고유한 ID(String)와 대여 상태(대여자 이름 String, 없으면 nul)를 가진다. 다음 요구사항을 만족하는 프로그램을 작성해 본다.
[요구 사항]
- 책의 ID와 대여 상태를 저장하는 Map 생성
- 구현 메소드
- 책을 추가하는 기능: 책 ID와 초기 대여 상태(대여자 없음, null) 추가
- 책을 대여하는 기능: 책 ID와 대여자 이름을 받아 대여 상태를 업데이트. 이미 대여 중이면 대여 불가 메시지 출력.
- 책을 반납하는 기능: 책 ID를 받아 대여 상태를 null로 변경. 대여 중이 아니면 반납 불가 메시지 출력
- 모든 책의 대여 상태를 출력하는 기능: 책 ID와 대여자(또는 "대여 가능" 상태) 출력
- Map의 적절한 구현체(HashMap, TreeMap 등)를 선택하고, 이유를 주석으로 설명
- 메인 메서드에서 위 기능을 테스트
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Library {
private String id;
private String name;
// 생성자
public Library(String id, String name) {
this.id = id;
this.name = name;
}
// getter
public String getId() {
return id;
}
public String getName() {
return name;
}
Map<String, String> map = new HashMap<>();
// 책 추가하기
public synchronized void addBook(String id) {
if (map.containsKey(id)) {
System.out.println("책" + id + "는 이미 존재합니다.");
} else {
map.put(id, null);
System.out.println("책" + id + "가 추가되었습니다.");
}
}
// 책 대여하기 (수정 필요)
public synchronized void borrowBook(String id, String name) {
if (map.containsKey(id)) {
if (map.get(id) == null) {
map.put(id, name);
System.out.println("책 ID" + id + "가 " + name + "에게 대여되었습니다.");
} else {
System.out.println("책 ID" + id + "는 이미 " + map.get(id) + "님에게 대여중입니다.");
}
} else {
System.out.println("책 ID" + id + "는 존재하지 않습니다.");
}
}
public synchronized void returnBook(String id) {
if (map.containsKey(id)) {
if (map.get(id) != null) {
System.out.println("책ID " + id + "가 " + map.get(id) + "으로부터 반납되었습니다.");
map.put(id, null);
} else {
System.out.println(id + "가 대여 중이 아닙니다.");
}
} else {
System.out.println("책ID " + id + "가 존재하지 않습니다.");
}
}
public synchronized void statusBooklist() {
System.out.println("=== 책 대여 상태===");
Set<String> keySet = map.keySet();
for (String id : keySet) {
if (map.get(id) != null) {
System.out.println("책 ID: " + id + " - " + "대여 중 (" + map.get(id) + ")");
} else {
System.out.println("책 ID: " + id + " - " + "대여 가능");
}
}
}
}
[속성]
- private String id: 도서관의 고유 ID를 저장하는 필드.
- private String name: 도서관의 이름을 저장하는 필드.
[HashMap]
- Map<String, String> map: 책의 ID를 키로 하고, 대여자의 이름을 값으로 저장하는 HashMap. 대여 중이지 않은 책은 값이 null로 설정 된다. ▶ HashMap을 사용하는 이유는, null 값을 저장할 수 있고, key는 중복이 허용 안 되나, value는 중복이 허용되기 때문이다. 또한, 키를 통해 값을 빠르게 찾아낼 수 있다는 장점이 있다.
[생성자]
- 도서관의 ID와 이름을 초기화
[getter]
- getId()와 getName() 메서드는 각각 도서관의 ID와 이름을 반환하여 외부에서 도서관 정보를 접근할 수 있도록 해준다.
※ 각 메소드는 synchronized 키워드를 사용하여 멀티스레드 환경에서 안전하게 사용할 수 있도록 한다.
HashMap에서 특정 key가 존재하는지 확인하는 방법
1. Map.containsKey(key): 파라미터로 입력 받은 값과 일치하는 값 있으면 true, 없으면 false 리턴
2. Map.get(key): key값으로 Map의 value 찾을 때 사용
[책 추가 메소드]
- 주어진 ID로 책을 추가. 이미 존재하는 ID라면 추가하지 않고 경고 메시지를 출력한다. 새 책은 null 값을 대여자 이름으로 가진다.
[책 대여 메소드]
- 주어진 ID의 책을 대여. 책이 이미 대여 중인지 확인하고, 대여가 가능한 경우 대여자의 이름을 저장한다.
[책 반납 메소드]
- 주어진 ID의 책을 반납. 반납 시 책이 대여 중인지 확인 후, 대여자의 이름을 null로 설정하여 대여 가능 상태로 변경한다.
[책 대여 상태 조회]
- 모든 책의 대여 상태를 조회하여 출력한다. 대여 중인 책과 대여 가능한 책을 구분하여 표시한다.
public class LibraryExample {
public static void main(String[] args) {
Library lib = new Library(null, null);
// 책 추가하기
lib.addBook("B001");
lib.addBook("B002");
lib.addBook("B003");
lib.addBook("B001"); // 이미 존재
// 책 대여하기
lib.borrowBook("B001", "김철수"); // 대여 완료
lib.borrowBook("B002", "이영희"); // 대여 완료
lib.borrowBook("B001", "왕서방"); // 대여 불가 (이미 대여)
lib.borrowBook("B004", "박장수"); // 대여 불가(존재X)
// 책 반납하기
lib.returnBook("B001"); // 반납 완료
lib.returnBook("B004"); // 반납 불가(책 존재 x)
// 책 대여상태
lib.statusBooklist(); // 1: 대여 가능, 2: 이영희, 3: 대여 가능
}
}
[참고]
혼자 공부하는 자바
다음 내용
'프로그래밍 및 기타 > Java, Spring Boot' 카테고리의 다른 글
[Java] Spring Boot: 인텔리제이 새 프로젝트 생성하기 (0) | 2025.04.14 |
---|---|
[Java] Spring Boot: 인텔리제이(커뮤니티) 다운로드 (0) | 2025.04.14 |
[java] 스레드 제어 (0) | 2025.04.10 |
[java] java 스레드(thread) - 멀티, 메인, 작업 / 동기화 메소드 (1) | 2025.04.10 |
[java] java.lang 패키지 (0) | 2025.04.09 |