
도서 목록 프로그램 BookService
저장 순서가 유지되지 않고, 중복 객체도 저장하지 못하게 하는 자료 구조
null도 중복을 허용하지 않기 때문에 1개의 null만 저장
구현 클래스로 HashSet, LinkedHashSet, TreeSet이 있음




키(key)와 값(value)으로 구성되어 있으며, 키와 값은 모두 객체
키는 중복 저장을 허용하지 않고(Set방식), 값은 중복 저장 가능(List방식)
키가 중복되는 경우, 기존에 있는 키에 해당하는 값을 덮어 씌움
구현 클래스로 HashMap, HashTable, LinkedHashMap, Properties, TreeMap이 있음


package edu.kh.collection.model.vo;
import java.util.Objects;
public class Member {
private String id;
private String pw;
private int age;
public Member() {}
public Member(String id, String pw, int age) {
this.id = id;
this.pw = pw;
this.age = age;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPw() {
return pw;
}
public void setPw(String pw) {
this.pw = pw;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Member [id=" + id + ", pw=" + pw + ", age=" + age + "]";
}
@Override
public int hashCode() {
return Objects.hash(age, id, pw);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Member other = (Member) obj;
return age == other.age && Objects.equals(id, other.id) && Objects.equals(pw, other.pw);
}
}
package edu.kh.collection.model.vo;
public class Book {
private int bookNum;
private String name;
private String author;
private int price;
private String publisher;
public Book() {}
public Book(int bookNum, String name, String author, int price, String publisher) {
this.bookNum = bookNum;
this.name = name;
this.author = author;
this.price = price;
this.publisher = publisher;
}
public int getbookNum() {
return bookNum;
}
public void setNum(int bookNum) {
this.bookNum = bookNum;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getPublisher() {
return publisher;
}
public void setPublisher(String publisher) {
this.publisher = publisher;
}
@Override
public String toString() {
return "[도서번호 : " + bookNum + " / 도서제목 : " + name + " / 도서작가 : " + author + " / 도서가격 : " + price + " / 출판사 : "
+ publisher + "]";
}
}
package edu.kh.collection.model.service;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
import edu.kh.collection.model.vo.Member;
public class SetService {
// Set(집합)
// - 순서를 유지하지 않음(== 인덱스 없음)
// - 중복을 허용하지 않는다. ( null 중복 X, 1개만 저장 가능 )
// *** Set이 중복을 확인하는 방법 ***
// -> 객체가 가지고 있는 필드값이 모두 같으면 중복으로 판단
// --> 이 때, 필드값이 모두 같은지 비교하기 위해서
// 객체에 "equals()"가 반드시! 오버라이딩 되어있어야 한다.
public void ex1() {
Set<String> set = new HashSet<String>();
// HashSet : Set의 대표적인 자식 클래스
// 사용 조건 1 : 저장되는 객체에 equals() 오버라이딩 필수
// 사용 조건 2 : 저장되는 객체에 hashCode() 오버라이딩 필수
// ** Hash 라는 단어가 붙은 컬렉션은 반드시 저장되는 객체에
// equals(), hashCode() 오버라이딩 해야함.
set.add("네이버");
set.add("카카오");
set.add("쿠팡");
set.add("당근마켓");
set.add("배민");
set.add("배민");
set.add("배민");
set.add(null);
set.add(null);
set.add(null);
System.out.println(set);
// 확인된것 : 순서 X / 중복 X / null 중복 X
// size() : 저장된 데이터의 개수 반환
System.out.println("저장된 데이터 수 : " + set.size());
// remove(String e) : Set에 저장된 객체 중 매개변수 e와
// 필드값이 같은 객체를 제거
System.out.println(set.remove("당근마켓"));
System.out.println(set.remove("라인"));
System.out.println(set);
// Set은 순서가 없어서 저장된 객체 하나를 얻어올 수 있는 방법이 없다!
// -> 대신에 Set 전체의 데이터를 하나씩 반복적으로 얻어올 순 있다!
// 1. Iterator(반복자)
// - 컬렉션에서 제공하는 컬렉션 객체 반복 접근자
// ( 컬렉션에 저장된 데이터를 임의로 하나씩 반복적으로 꺼내는 역할 )
Iterator<String> it = set.iterator();
// Iterator가 얻어온 데이터의 타입은 모두 String임을 알려줌
// set.iterator() : Set을 Iterator로 하나씩 꺼내갈 수 있는 모양으로 변환
while(it.hasNext()) { // hasNext() : 다음 값이 있으면 true 반환
// it.next() : 다음 값(객체)을 얻어옴
System.out.println(it.next());
}
System.out.println("-----------------------------------");
// 2. 향상된 for문 사용
// for ( 하나씩 꺼내서 저장할 변수 : 컬렉션 )
for( String temp : set ) {
System.out.println(temp);
}
}
public void ex2() {
Set<Member> memberSet = new HashSet<Member>();
memberSet.add( new Member("user01", "pass01", 30) );
memberSet.add( new Member("user01", "pass01", 30) );
memberSet.add( new Member("user02", "pass02", 25) );
for(Member mem : memberSet) {
System.out.println(mem);
}
// Object 의 equals(), hashCode() 오버라이딩
// A.equals(B) : A와 B가 가지고 있는 필드 값이 모두 같으면 true, 아니면 false
// Hash 함수 : 입력된 단어를 지정된 길이의 문자열로 변환하는 함수 (중복 X)
// ex) 입력 : 111 -> "asdfg" (5글자)
// ex) 입력 : 1233242 -> "qwezg" (5글자)
// hashCode() : 필드 값이 다르면 중복되지 않는 숫자를 만드는 메서드
// -> 왜 만들까? 빠른 데이터 검색을 위해서 (객체가 어디에 있는지 빨리 찾기 위해서)
// HashSet() : 중복 없이 데이터 저장하고, 데이터 검색이 빠름(Hash)
}
public void ex3() {
// Wrapper 클래스 : 기본 자료형 -> 객체로 포장하는 클래스
// - 컬렉션에 기본 자료형값을 저장할 때 사용
// - 기본 자료형에 없던 추가 기능, 값을 이용하고 싶을 때 사용
// <Wrapper 클래스 종류>
// int -> Integer
// char -> Character
// Double, Boolean, Float, Byte, Short, Long
int iNum = 10;
double dNum = 3.14;
// 기본 자료형 -> 포장
Integer w1 = new Integer(iNum); // int가 Integer로 포장
Double w2 = new Double(dNum); // double이 Double로 포장
// 삭제선 == 해당 구문은 삭제될 예정이다.
// ==> 사용을 권장하지 않는다.
// Wrapper 클래스 활용
System.out.println("int 최대값 : " + w1.MAX_VALUE);
System.out.println("double 최소값 : " + w2.MIN_VALUE);
// 기울어진 글씨 ? == static
// static은 클래스명.필드명 / 클래스명.메소드명() 호출 가능
System.out.println("static 방식으로 Wrapper 클래스 사용하기");
System.out.println("int 최대값 : " + Integer.MAX_VALUE);
System.out.println("double 최소값 : " + Double.MIN_VALUE);
//******************************************************************
// parsing : 데이터 형식을 변환
int num1 = Integer.parseInt("100");// 문자열 "100"을 int 형식으로 변환
double num2 = Double.parseDouble("1.23456");// 문자열 "1.23456"을 double형식으로 변환
System.out.println( num1 + num2 );
}
public void ex4() {
// Wrapper 클래스의 AutoBoxing / AutoUnboxing
Integer w1 = new Integer(100);
Integer w2 = 100;
Integer w3 = 200;
// (Integer) (int => Integer) 자동 포장
// w2와 100은 원래 연산이 안되어야 하지만
// Integer는 int의 포장 형식이라는 것을 Java가 인식하고 있어서
// 위와 같은 경우 int를 Integer로 자동 포장해준다.
System.out.println("w2 + w3 = " + (w2 + w3));
// w2 (Integer 객체)
// w3 (Integer 객체)
// w2 + w3 == 객체 + 객체 --> 원래는 불가능
// Integer는 int의 포장형태라는걸 Java인식하고 있어서
// + 연산 시 포장을 자동으로 벗겨냄
// Integer + Integer -> int + int (자동 포장 해제) (AutoUnboxing)
}
public void lotto() {
// 로또 번호 생성기 Version.2
// 6개 난수 1 ~ 45 사이
// 중복 X
//Set<Integer> lotto = new HashSet<Integer>();
Set<Integer> lotto = new TreeSet<Integer>(); // 자동 정렬 Set
//Set<Integer> lotto = new LinkedHashSet<Integer>(); // 순서 유지 Set
// Integer는 equals(), hashcode() 이미 오버라이딩 완료 상태
while (lotto.size() < 6) {
// lotto에 저장된 값이 개수가 6개 미만이면 반복
int random = (int)(Math.random() * 45 + 1);
System.out.println(random);
lotto.add(random);
// int값이 자동으로 Integer로 포장되어 lotto에 추가
}
System.out.println("로또 번호 : " + lotto);
}
}
package edu.kh.collection.model.service;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import edu.kh.collection.model.vo.Member;
public class MapService {
// Map : Key와 Value 한 쌍이 데이터가 되어 이를 모아둔 객체
// - Key를 모아두면 Set의 특징 (중복 X)
// - Value를 모아두면 List의 특징 (중복 O)
public void ex1() {
// HashMap<K, V> : Map의 자식 클래스 중 가장 대표되는 Map
Map<Integer, String> map = new HashMap<Integer, String>();
// Map.put(Integer Key, String Value) : 추가
map.put(1, "홍길동");
map.put(2, "고길동");
map.put(3, "김길동");
map.put(4, "박길동");
map.put(5, "이길동");
map.put(6, "최길동");
// key 중복
map.put(1, "홍홍홍"); // 중복 허용 X, 대신 value 덮어쓰기
// value 중복
map.put(7, "최길동");
System.out.println( map ); // map.toString() 오버라이딩 되어있음.
}
public void ex2() {
// Map 사용 예제
// VO(값 저장용 객체)는 특정 데이터 묶음의 재사용이 많은 경우 주로 사용
// -> 재사용이 적은 VO는 오히려 코드 낭비
// -> Map을 이용해서 VO와 비슷한 코드를 작성할 수 있다.
// 1) VO 버전
Member mem = new Member();
// 값 세팅
mem.setId("user01");
mem.setPw("pass01");
mem.setAge(30);
// 값 출력
System.out.println( mem.getId() );
System.out.println( mem.getPw() );
System.out.println( mem.getAge() );
System.out.println("----------------------------");
// 2) Map 버전
Map<String, Object> map = new HashMap<String, Object>();
// value가 Object 타입 == 어떤 객체든 Value에 들어올 수 있다.
// 값 세팅
map.put("id", "user02");
map.put("pw", "pass02");
map.put("age", 25);
// 값 출력
System.out.println( map.get("id") );
System.out.println( map.get("pw") );
System.out.println( map.get("age") );
System.out.println("-------------------------------");
// **** Map에 저장된 데이터 순차적으로 접근하기 ****
// Map에서 Key만 모아두면 Set의 특징을 가진다.
// -> 이를 활용할 수 있도록 Map에서
// keySet() 메서드 제공
// -> Key만 모아서 Set으로 반환
Set<String> set = map.keySet(); // id, pw, age가 저장된 Set반환
System.out.println( "keySet() : " + set );
// 향상된 for문
for( String key : set ) {
System.out.println( map.get(key) );
}
// map에 저장된 데이터가 많거나
// 어떤 key가 있는지 불분명할 때
// 또는 map에 저장된 모든 데이터에 접근해야할 때
// keySet() + 향상된 for문 코드 사용
package edu.kh.collection.model.service;
import java.util.ArrayList;
import java.util.InputMismatchException;
import java.util.List;
import java.util.Scanner;
import edu.kh.collection.model.vo.Book;
public class BookService {
private Scanner sc = new Scanner(System.in);
private List<Book> bookList = new ArrayList<Book>();
private List<Book> favList = new ArrayList<Book>();
public BookService() {
bookList.add(new Book(1111, "세이노의 가르침", "세이노", 6480, "데이원"));
bookList.add(new Book(2222, "문과남자의 과학공부", "유시민", 15750, "돌베개"));
bookList.add(new Book(3333, "역행자", "자청", 17550, "웅진지식하우스"));
bookList.add(new Book(4444, "꿀벌의 예언", "베르나르 베르베르", 15120, "열린책들"));
bookList.add(new Book(5555, "도둑맞은 집중력", "요한 하리", 16920, "어크로스"));
}
public void displayMenu() {
int menuNum = 0;
do {
System.out.println("<도서 목록 프로그램>");
System.out.println("1. 도서등록");
System.out.println("2. 도서조회");
System.out.println("3. 도서수정");
System.out.println("4. 도서삭제");
System.out.println("5. 즐겨찾기 추가");
System.out.println("6. 즐겨찾기 삭제");
System.out.println("7. 추천도서");
System.out.println("0. 프로그램 종료");
System.out.println("\n메뉴 번호 선택 >>");
try {
menuNum = sc.nextInt();
System.out.println();
switch(menuNum) {
case 1 : System.out.println( addBook() ); break;
case 2 : selectAll(); break;
case 3 : System.out.println( updateBook() ); break;
case 4 : System.out.println( removeBook() ); break;
case 5 : System.out.println( addFavorite() ); break;
case 6 : System.out.println( deleteFavorite() ); break;
case 7 : randomBook(); break;
case 0 : System.out.println("프로그램 종료..."); break;
default : System.out.println("메뉴에 작성된 번호만 입력해주세요.");
}
} catch(InputMismatchException e) {
System.out.println("\nerror : 입력형식이 유효하지 않습니다. 다시 시도해주세요.");
sc.nextLine();
menuNum = -1;
}
} while(menuNum != 0 );
}
public String addBook() throws InputMismatchException {
System.out.println("=======도서등록=======");
System.out.println("도서 번호 : ");
int bookNum = sc.nextInt();
sc.nextLine();
System.out.println("제목 : ");
String name = sc.nextLine();
System.out.println("작가 : ");
String author = sc.nextLine();
System.out.println("가격 : ");
int price = sc.nextInt();
sc.nextLine();
System.out.println("출판사 : ");
String publisher = sc.nextLine();
if( bookList.add(new Book(bookNum, name, author, price, publisher)) ){
return "성공";
} else {
return "실패";
}
}
public void selectAll() {
System.out.println("=======도서조회=======");
if(bookList.isEmpty()) {
System.out.println("도서 정보가 없습니다");
return;
}
for(Book bk : bookList) {
System.out.println(bk);
}
}
public String updateBook() throws InputMismatchException {
System.out.println("=======도서수정=======");
System.out.println("도서 번호 입력 : ");
int num = sc.nextInt();
boolean bookFound = false;
for (Book book : bookList) {
if (book.getbookNum() == num) {
bookFound = true;
System.out.println(num + "번 도서 정보");
System.out.println(book);
System.out.println("도서 번호: ");
int bookNum = sc.nextInt();
sc.nextLine();
System.out.println("제목: ");
String name = sc.nextLine();
System.out.println("작가: ");
String author = sc.nextLine();
System.out.println("가격: ");
int price = sc.nextInt();
sc.nextLine();
System.out.println("출판사: ");
String publisher = sc.nextLine();
int index = bookList.indexOf(book);
Book temp = bookList.set(index, new Book(bookNum, name, author, price, publisher));
return temp.getName() + "의 정보가 수정되었습니다";
}
}
if (!bookFound) {
return num + "번 도서를 찾을 수 없습니다.";
} return "수정완료";
}
public String removeBook() throws InputMismatchException {
System.out.println("=======도서삭제=======");
System.out.println("인덱스 번호 입력 : ");
int index = sc.nextInt();
if(bookList.isEmpty()) {
return "등록된 도서가 없습니다";
} else if( index < 0 ){
return "음수는 입력할 수 없습니다";
} else if( index >= bookList.size() ) {
return "범위를 넘어선 값을 입력할 수 없습니다";
} else {
System.out.println(bookList.get(index));
System.out.print("정말 삭제하시겠습니까?(Y/N) :");
char ch = sc.next().toUpperCase().charAt(0);
if(ch == 'Y') {
Book temp = bookList.remove(index);
return temp.getName() + "의 정보가 삭제되었습니다.";
} else {
return "취소";
}
}
}
public String addFavorite() {
System.out.println("===== 즐겨찾기 등록 =====");
System.out.println("등록할 도서 번호를 입력하세요 : ");
int input = sc.nextInt();
boolean flag = true;
for(Book temp : bookList) {
if(temp.getbookNum() == input) {
favList.add(temp);
flag = false;
}
}
if(flag) {
return "찾는 번호가 없습니다.";
} else {
return "등록 성공";
}
}
public String deleteFavorite() {
System.out.println("=====즐겨 찾기 삭제=====");
System.out.print("즐겨찾기 삭제할 도서 번호를 입력하세요 : ");
int input = sc.nextInt();
boolean flag = true;
for(Book temp : favList) {
if(temp.getbookNum() == input) {
int index = favList.indexOf(temp);
favList.remove(index);
flag = false;
break;
}
}
if (flag) {
return "찾는 번호가 없습니다.";
} else {
return "삭제 성공";
}
}
public void randomBook() {
int max = bookList.size();
int random = (int)(Math.random() * max);
System.out.println(bookList.get(random).getName());
}
}