메모리상에서 자료를 구조적으로 처리하는 방법을 자료구조라 일컫는데 컬렉션(Collection)은 자바에서 제공하는 자료구조를 담당하는 프레임워크
추가, 삭제, 정렬 등의 기능처리가 간단하게 해결 되어 자료구조적 알고리즘을 구현할 필요 없음
java.util 패키지에 포함되며, 인터페이스를 통해 정형화된 방법으로 다양한 컬렉션 클래스 이용 가능
컬렉션 : 자바에서 제공하는 자료구조모음(Framework)
데이터(자료)를 메모리에서 구조적으로 처리하는 방법론이다.

한 번 크기를 지정하면 변경할 수 없다.
공간 크기가 부족하면 에러가 발생 → 할당 시 넉넉한 크기로 할당하게 됨 (메모리 낭비) 필요에 따라 공간을 늘리거나 줄일 수 없음
배열에 기록된 데이터에 대한 중간 위치의 추가, 삭제가 불편하다.
추가, 삭제할 데이터부터 마지막 기록된 데이터까지 하나씩 뒤로 밀어내고 추가해야 함(복잡한 알고리즘)
한 타입의 데이터만 저장 가능하다.
저장하는 크기의 제약이 없다.
추가, 삭제, 정렬 등의 기능 처리가 간단하게 해결된다.
자료를 구조적으로 처리 하는 자료구조가 내장되어 있어 알고리즘 구현이 필요 없음
여러 타입의 데이터가 저장 가능하다.
객체만 저장할 수 있기 때문에 필요에 따라 기본 자료형을 저장해야 하는 경우 Wrapper클래스 사용

List : 배열 / 순서 O, 중복 O
Set : 바구니(집합) / 순서 X, 중복 X
Map : 출석부 / key는 중복 X , value는 중복 O

List 계열, Set 계열 : add 기능 O
Map 계열 : put 기능 O
자료들을 순차적으로 나열한 자료구조로 인덱스로 관리되며, 중복해서 객체 저장 가능
구현 클래스로 ArrayList와 Vector, LinkedList가 있음







class Student
package edu.kh.collection.model.vo;
public class Student {
private String name;
private int age;
private String region;
private char gender;
private int score;
public Student() {};
public Student(String name, int age, String region, char gender, int score) {
super();
this.name = name;
this.age = age;
this.region = region;
this.gender = gender;
this.score = score;
}
// toString() 오버라이딩
// alt + shift + s -> s -> enter
public String getName() {
return name;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", region=" + region + ", gender=" + gender + ", score="
+ score + "]";
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
class StudentRun
package edu.kh.collection.run;
import edu.kh.collection.model.service.StudentService;
public class StudentRun {
public static void main(String[] args) {
StudentService service = new StudentService();
service.displayMenu();
//service.ex();
}
}
class StudentService
package edu.kh.collection.model.service;
import java.util.ArrayList;
import java.util.InputMismatchException;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
import edu.kh.collection.model.vo.Student;
public class StudentService {
// 필드
private Scanner sc = new Scanner(System.in);
// 학생 정보를 저장할 List를 생성(객체 배열 Upgrade 버전)
// java.util.List 인터페이스 : List에 반드시 필요한 필수 기능을 모아둔 인터페이스
// ArrayList() 기본 생성자 : 기본 크기 10짜리 리스트 생성
// -> 하지만 리스트는 크기가 늘었다 줄었다 하기 때문에 큰 의미 없다
// ArrayList(용량) : 용량 만큼의 리스트 생성
// -> 너무 큰 값을 작성하면 메모리를 많이 소모한다
// private List<Student> studentList = new ArrayList<Student>(); // 검색(조회)에 효율적
private List<Student> studentList = new LinkedList<Student>(); // 추가, 수정, 삭제에 효율적
// Student로 저장되는 타입이 제한된 리스트 생성
// == Student만 저장 가능 == 모든게 Student
// == Student임을 검사할 필요 없다
// 인터페이스는 모든 메소드가 추상메소드라 미완성 메소드가 포함
// -> 미완성된 부분이 존재하는 클래스라 -> 객체 생성해도 미완성 객체
// -> 객체 생성 불가능하다
public StudentService() {
studentList.add( new Student("박서준", 25, "서울시 중구", 'M', 90) );
studentList.add( new Student("정해인", 23, "경기도 안산시", 'M', 80) );
studentList.add( new Student("박보영", 22, "서울시 강남구", 'F', 90) );
studentList.add( new Student("김민서", 33, "충북 청주시", 'F', 100) );
studentList.add( new Student("오나라", 24, "서울시 서대문구", 'M', 70) );
}
public void ex() {
// List 테스트
// List.add(Object e) : 리스트에 객체를 추가
// * 매개변수 타입이 Object == 모든 객체를 매개변수로 전달할 수 있음
// (매개변수 Object == 최상위 부모 참조 변수 == 다형성 적용 가능)
// studentList.add(sc); // 0번 인덱스
// studentList.add(new Student()); // 1번
// studentList.add(new Object()); // 2번
// studentList.add("문자열"); // 3번
// -> 컬렉션 특징 : 여러 타입의 데이터를 저장할 수 있다
// (반환형)
// Object List.get(index i) : 리스트에서 i번째 인덱스에 있는 객체(Object)를 반환
// 반환형이 Object == 모든 객체를 반환할 수 있다
System.out.println(studentList.toString());
// 실행 전 : java.lang.Object.toString() == 정적 바인딩
// 실행 후 : 알고 보니 1번째는 Student 객체이고, toString()이 오버라이딩 되어있음
// -> Student의 toString()이 수행됨 == 동적 바인딩
// Student의 이름만 얻어오기
// Student 객체가 맞는지 확인하고(instanceof) 다운 캐스팅을 해야
// Student 기능을 사용할 수 있다
if(studentList.get(1) instanceof Student) {
}
System.out.println(((Student)studentList.get(1)).getName());
}
// -> 길고 복잡함...
// -> 컬렉션의 장점인 여러 객체 저장이 코딩에 방해됨
// ************* 그래서 등장 !! -> 제네릭스(Generics) ***************
// (보통 제네릭라고 함) <>
// [가장 중요한 역할]
// -> 컬렉션에 저장되는 객체 타입을 한 가지로 제한

/**
* @author OYJ
*
*/
public void displayMenu() {
int menuNum = 0;
do {
System.out.println("\n======= 학생 관리 프로그램 =======\n");
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("0. 프로그램 종료");
System.out.print("\n메뉴 번호 선택 >> ");
try {
menuNum = sc.nextInt();
switch(menuNum) {
case 1 : System.out.println(addStudent());; break;
case 2 : selectAll(); break;
case 3 : System.out.println(updateStudent()); break;
case 4 : System.out.println(removeStudent()); break;
case 5 : searchName1(); break;
case 6 : searchName2(); break;
case 0 : System.out.println("<프로그램 종료>"); break;
default : System.out.println("메뉴에 작성된 번호만 입력해주세요.");
}
} catch(InputMismatchException e) {
System.out.println("error : 입력 형식이 유효하지 않습니다. 다시 시도해 주세요.");
sc.nextLine(); // 입력 버퍼에 남아있는 잘못 입력된 문자열 제거
menuNum = -1; // 첫 반복 시 잘못 입력하는 경우 menuNum이 0을 가지고 있어서 종료됨
// 이를 방지하기 위해 임의값 -1 대입
}
} while(menuNum != 0);
}

/**
* 1. 학생 정보 추가 메소드
* - 추가 성공 시 "성공", 실패 시 "실패" 문자열 반환
*/
public String addStudent() throws InputMismatchException {
System.out.println("========== 학생 정보 추가 ==========");
System.out.print("이름 : ");
String name = sc.next();
System.out.print("나이 : ");
int age = sc.nextInt();
sc.nextLine(); // 입력 버퍼 개행 문자 제거
System.out.print("사는 곳 : ");
String region = sc.nextLine();
System.out.print("성별(M/F) : ");
char gender = sc.next().toUpperCase().charAt(0);
System.out.print("점수 : ");
int score = sc.nextInt();
// Student 객체 생성 후 List에 추가
if(studentList.add(new Student(name, age, region, gender, score))) {
// boolean java.util.List.add(Student e)
// (반환형) -> 제네릭 <Student> 때문에 List 타입이 Student로 제한됨
// add()는 솔직히 실패하는 경우가 없음
// == 무조건 true 반환
// 대신 예외가 발생해서 add() 수행 전 메소드가 종료될 순 있음
return "성공";
} else {
return "실패";
}
}

/**
* 2. 학생 전체 조회 메소드
*/
private void selectAll() {
// - List는 인덱스가 있다(0번 부터 시작)
// - List에 저장된 데이터의 개수를 얻어오는 방법 : int List.size()
// -> 배열명.length 대신 사용
// - List가 비어있는지 확인하는 방법 :
// boolean List.isEmpty() : 비어있으면 true 반환
System.out.println("=== 학생 전체 조회 ===");
// studentList가 비어 있는 경우 "학생 정보가 없습니다" 출력
// if(studentList.size()==0) {
if(studentList.isEmpty()) {
System.out.println("학생 정보가 없습니다");
return; // 현재 메소드를 종료하고 호출한 곳으로 돌아감
// 단, 반환값은 없음(void)
}
/* 일반 for문
for(int i=0; i<studentList.size(); i++) {
// studentList에서 i번째 인덱스 요소를 얻어와 출력
System.out.println(studentList.get(i));
}*/
// 향상된 for문(for each문)
// - 컬렉션, 배열의 모든 요소를 순차적으로 반복 접근할 수 있는 for문
// (순차적 : 0번 인덱스부터 마지막 요소까지 인덱스를 1씩 증가)
// [작성법]
// for(컬렉션 또는 배열에서 꺼낸 한 개의 요소를 저장할 변수 : 컬렉션명 또는 배열명){}
int index = 0;
for(Student std : studentList) {
// std에는 for문 반복 시 마다 0, 1, 2, ... 인덱스 요소를 한 번씩 저장
System.out.println((index++) + "번 : " + std);
}
}

/**
* 3. 학생 정보 수정 메소드
* */
private String updateStudent() throws InputMismatchException{
// - Student List.set(int index, Student e)
// -> List의 i번째 요소를 전달 받은 e로 변경
// -> 반환값 Student == 변경 전 Student 객체가 담겨있음
//if(studentList.add(new Student(name, age, region, gender, score))) {
// boolean java.util.List.add(Student e)
// (반환형) -> 제네릭 <Student> 때문에 List 타입이 Student로 제한됨
System.out.println("======== 학생 정보 수정 ========");
System.out.print("인덱스 번호 입력 : ");
int index = sc.nextInt();
// 1) 학생 정보가 StudentList에 있는가?
if(studentList.isEmpty()) {
return "학생 정보가 없습니다";
}
// 2) 입력된 숫자가 0보다 작은가?(음수 검사)
else if(index <0) {
return "0보다 작아서 안 된다";
// 3) 만약 문자열을 입력한 경우 -> throws로 예외 처리
// 4) 입력 받은 숫자가 studentList 범위 내 인덱스 번호인가?
} else if(index >= studentList.size()) {
return "범위를 넘어서 값을 입력할 수 없습니다";
} else {
// 수정 코드 작성
System.out.println(index + "번째 인덱스에 저장된 학생 정보");
System.out.println(studentList.get(index));
System.out.print("수정하고 싶은 이름 : ");
String name = sc.next();
System.out.print("수정하고 싶은 나이 : ");
int age = sc.nextInt();
sc.nextLine();
System.out.print("수정하고 싶은 지역 : ");
String region = sc.nextLine();
System.out.print("수정하고 싶은 성별 : ");
char gender = sc.next().charAt(0);
System.out.print("수정하고 싶은 성적 : ");
int score = sc.nextInt();
// index 번째 입력 받은 새로운 학생 정보를 세팅 = 수정
// 이때 index번째에 있던 기존 학생 정보 반환한다
Student temp = studentList.set(index, new Student(name, age, region, gender, score));
return temp.getName();
}
}

/**
* 4. 학생 정보 제거 메소드
*/
private String removeStudent() {
// - Student List.remove(int index)
// 리스트에서 index번째 요소를 제거
// 이때, 제거된 요소가 반환된다
// * List는 중간에 비어있는 인덱스가 없게 하기 위해서
// remove() 동작 시 뒤쪽 요소를 한 칸씩 당겨온다
System.out.println("======== 학생 정보 제거 ========");
System.out.print("인덱스 번호 입력 : ");
int index = sc.nextInt();
// 1) 학생 정보가 StudentList에 있는가?
if(studentList.isEmpty()) {
return "학생 정보가 없습니다";
}
// 2) 입력된 숫자가 0보다 작은가?(음수 검사)
else if(index < 0) {
return "0보다 작아서 안 된다";
// 3) 만약 문자열을 입력한 경우 -> throws로 예외 처리
// 4) 입력 받은 숫자가 studentList 범위 내 인덱스 번호인가?
} else if(index >= studentList.size()) {
return "범위를 넘어서 값을 입력할 수 없습니다";
} else {
// 학생 정보 제거
System.out.print("정말 삭제 하시겠습니까?(Y/N) : ");
char ch = sc.next().toUpperCase().charAt(0);
// String.toUpperCase() : 문자열을 대문자로 변경
if(ch =='Y') {
Student temp = studentList.remove(index);
return temp.getName()+"의 정보가 제거되었습니다";
} else {
return "취소"; }
}
}


/**
* 5. 이름이 일치하는 학생을 찾아서 조회하는 메소드
*/
private void searchName1() {
System.out.println("======== 이름으로 검색(일치) ========");
System.out.print("검색할 이름 입력 : ");
String name = sc.next();
boolean flag = true;
// 향상된 for문
for(Student std : studentList) {
// std에는 for문 반복 시 마다 0, 1, 2, ... 인덱스 요소를 한 번씩 저장
// 이름이 일치하는 경우
if(name.equals(std.getName())) {
System.out.println(std);
flag=false;
// 일치한 학생의 정보 출력
}
}
if(flag) {
// 검색 결과가 없습니다. 출력
System.out.println("검색 결과가 없습니다");
}
}


/**
* 6. 이름에 특정 문자열이 포함되는 학생을 찾아서 조회하는 메소드
*/
private void searchName2() {
// contains : 포함
// boolean String.contains(문자열) : String에 문자열이 포함되어 있으면 true
System.out.println("======== 이름으로 검색(문자열 포함) ========");
System.out.print("검색할 문자열 입력 : ");
String input = sc.next();
boolean flag = true;
// 향상된 for문
for(Student std : studentList) {
// std에는 for문 반복 시 마다 0, 1, 2, ... 인덱스 요소를 한 번씩 저장
if(std.getName().contains(input)) {
// 일치한 학생의 정보 출력
}
}
if(flag) {
// 검색 결과가 없습니다. 출력
System.out.println("검색 결과가 없습니다");
}
}
}


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


class Member
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) {
super();
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 + "]";
}
// shift + alt + s - hashCode : hashCode 자동완성 가능
@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);
}
/*
// Object.equals() 오버라이딩
// - 현재 객체와 매개변수로 전달 받은 객체의 필드가 같은지 비교하는 형태로 오버라이딩
@Override
public boolean equals(Object obj) {
// 매개변수 다운 캐스팅
Member other = (Member)obj;
// 필드 값 비교
return this.id.equals(other.id) && this.pw.equals(other.pw) && this.age==other.age;
}
// Object.hashCode() 오버라이딩
@Override
public int hashCode() {
// hashCode() : 중복되지 않는 값을 만들어주는 메소드
// 필드 값이 같이 아니면 다른 정수를 반환한다
// 즉, 필드 값이 같은 객체는 같은 정수를 반환해야 한다.
// 즉, 필드 값을 이용해서 정수를 만들면 된다.
int result = 1; // 결과 저장 변수
final int PRIME = 31; // 소수
// 왜 31? 31이 연산 속도가 빠른 소수 중 하나라서 사용
result = result * PRIME * age;
result = result * PRIME * (id == null ? 0 : id.hashCode());
result = result * PRIME * (pw == null ? 0 : pw.hashCode());
return result;
} */
}
class SetRun
package edu.kh.collection.run;
import edu.kh.collection.model.service.SetService;
public class SetRun {
public static void main(String[] args) {
SetService service = new SetService();
// service.ex1();
// service.ex2();
// service.ex3();
// service.ex4();
// service.ex5();
service.lotto();
}
}
class SetService
package edu.kh.collection.model.service;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
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<>();
// HashSet : Set의 대표적인 자식 클래스
// 사용조건 1 : 저장되는 객체에 equals() 오버라이딩 필수
// 사용조건 2 : 저장되는 객체에 hashCode() 오버라이딩 필수
// *참고* : Hash라는 단어가 붙은 컬렉션은
// 반드시 저장되는 객체에 equals(), hashCode()를 오버라이딩 해야함
// Set.add(String e) : 추가
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);
// 확인할 것 : 순서 유지 안 되고, 중복 안 된다, null도 중복 안 된다
// size() : 저장된 데이터의 개수 반환
System.out.println("저장된 데이터의 수 : " + set.size());
// remove(String e) : Set에 저장된 객체 중 매개변수 e와 필드 값이 같은 객체를 제거
// + Hash라는 단어가 포함된 Set이면 hashCode()도 같아야함
System.out.println(set.remove("라인")); // 있는 데이터를 지우면 true
System.out.println(set.remove("야놀자")); // 없는 데이터를 지우면 false
System.out.println(set); // 제거 확인
// Set은 순서가 없어서 저장된 객체 하나를 얻어올 수 있는 방법이 없다
// -> 대신에 Set 전체의 데이터를 하나씩 반복적으로 얻어올 순 있다
// 1. Iterator(반복자)
// - 컬렉션에서 제공하는 컬렉션 객체 반복 접근자
// (컬렉션에 저장된 데이터를 임의로 하나씩 반복적으로 꺼내는 역할)
// Iterator가 얻어온 데이터의 타입은 모두 String
Iterator<String> it = set.iterator();
// set.iterator() : set을 Iterator 하나씩 꺼내갈 수 있는 모양으로 변환
System.out.println("--------------------- 1번째 반복 방법 --------------------");
while(it.hasNext()) { // hasNext() : 하나씩 꺼냈을 때 다음 값이 없는 경우 false 있는 경우 true
// false 끝난다
// -> 다음 값이 있으면 반복해야 한다
// it.hasNext() : 다음 값이 있으면 true 반환
// it.next() : 다음 값(객체)를 얻어옴
System.out.println(it.next());
}
System.out.println("--------------------- 2번째 반복 방법 --------------------");
// 2. 향상된 for문 사용
// for(하나씩 꺼내서 저장할 변수 : 컬렉션명){}
for(String s:set) {
System.out.println(s);
}
}

`
public void ex2() {
System.out.println("------------ (ex2) ------------");
// Object의 equals(), hashCode() 오버라이딩
// A.eqauls(B) : A와 B가 가지고 있는 필드 값이 모두 같으면 true, 아니면 false
// Hash 함수 : 입력된 단어를 지정된 길이의 문자열로 변환하는 함수(중복X)
// ex) 입력 : 111 -> "asdfg" (5글자)
// ex) 입력 : 1234567 -> "qwert" (5글자)
// 암호화 할 때 유용함
// hashCode() : 필드 값이 다르면 중복되지 않는 숫자를 만드는 메소드
// -> 왜 만들까? 빠른 데이터 검색을 위해서(객체가 어디에 있는지 빨리 찾기 위해서)
// 어지러운 방에서 물건 찾기 어려운 것처럼, 데이터에 임의의 번호를 줘서 찾기 쉽게 하는 것
// HashSet() : 중복 없이 데이터 저장(Set)하고 데이터 검색이 빠름(Hash)
Member mem1 = new Member("user01", "pass01", 30);
Member mem2 = new Member("user01", "pass01", 30);
Member mem3 = new Member("user02", "pass02", 40);
Member mem4 = new Member("user02", "pass02", 20); // 유저 아이디, 비밀번호가 같아도 나이가 다르면 다른 객체이다
// mem1과 mem2가 같은지 비교
System.out.println(mem1 == mem2); // false로 나옴
// new연산자로 객체를 각각 만들었고, 참조는 주소를 가져오는거라 다른 객체이다
// 얕온 복사 경우가 아니라면 다 false이다
// mem1과 mem2가 가지고 있는 필드 값이 같은지 비교
if(mem1.getId().equals(mem2.getId())) { // 아이디가 같을 경우
if(mem1.getPw().equals(mem2.getPw())) { // 비밀번호도 같을 경우
if(mem1.getAge()==(mem2.getAge())) { // 나이가 같을 경우
System.out.println("같은 객체입니다");
}
}
}
// -> 매번 이렇게 비교하기 힘들다.. 비교 코드를 작성해서 재활용하자!
// == equals() 메소드 오버라이딩 -> Member.java에 @Override 작성
System.out.println(mem1.equals(mem2)); // mem1, mem2의 필드는 같은가? true
System.out.println(mem1.equals(mem3)); // mem1, mem2의 필드는 같은가? false
System.out.println(mem2.equals(mem3)); // mem1, mem2의 필드는 같은가? false
// 서로 다른 객체지만 필드 값이 같다 == 동등하다
// 비교하려는 것이 정말 같은 하나의 객체이다 == 동일하다
}

public void ex3() {
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", 40));
memberSet.add(new Member("user02", "pass02", 20)); // 유저 아이디, 비밀번호가 같아도 나이가 다르면 다른 객체이다
for(Member mem : memberSet) {
System.out.println(mem);
}
Member mem1 = new Member("user01", "pass01", 30);
Member mem2 = new Member("user01", "pass01", 30);
Member mem3 = new Member("user02", "pass02", 40);
Member mem4 = new Member("user02", "pass02", 20);
memberSet.add(mem1);
memberSet.add(mem2);
memberSet.add(mem3);
memberSet.add(mem4);
// hashCode() 오버라이딩 되기 전
// -> equals()가 오버라이딩 되어 있었지만 중복 제거가 되지 않음
// -> 왜? HashSet은 hashCode()도 오버라이딩 해야한다!
System.out.println(mem1.hashCode());
System.out.println(mem2.hashCode());
System.out.println(mem3.hashCode());
System.out.println(mem4.hashCode());
}

public void ex4() {
// Wrapper 클래스 : 기본 자료형 -> 객체로 포장하는 클래스
// - 컬렉션에 기본 자료형 값을 저장할 때 사용한다
// - 기본 자료형에 없던 추가 기능, 값을 이용하고 싶을 때 사용
// <Wrapper 클래스 종류>
// int -> Interger
// Double, Boolean, Byte, Short, Long, Float, Character
int iNum = 10;
double dNum = 2.34;
// 기본 자료형 -> 포장
Integer i1 = new Integer(iNum); // int가 Integer로 포장되었다는 의미
Double d1 = new Double(dNum); // double이 Double로 포장되었다
// Wrapper 클래스 활용
System.out.println("int 최대값 : " + i1.MAX_VALUE);
System.out.println("double 최소값 : " + d1.MIN_VALUE);
// 기울어진 글씨 == static
// static은 클래스명.필드명 / 클래스명.메소드명() 호출 가능
System.out.println("i1의 값 : " + i1);
System.out.println("d1의 값 : " + d1);
System.out.println("=======================");
System.out.println("static 방식으로 Wrapper 클래스 사용하기");
System.out.println("int 최대값 : " + Integer.MAX_VALUE);
System.out.println("double 최소값 : " + Double.MIN_VALUE);
// ****************************************
// parsing : 데이터의 형식을 변환
// !String 데이터를 기본 자료형으로 변환!
int num1 = Integer.parseInt("100"); // 문자열 "100"을 int형식으로 변환
double num2 = Double.parseDouble("1.2345"); // 문자열 "1.2345"을 double 형식으로 변환
System.out.println(num1+num2); // 문자였다면 이어쓰기인데 101.2345로 나옴! 진짜로 int, double로 변경된 것
// ****************************************
}

public void ex5() {
// Wrapper 클래스의 AutoBoxing / AutoUnBoxing
Integer w1 = new Integer(100);
// 삭제선 == deprecated == 해당 구문은 삭제될 예정이다
// ==> 사용을 권장하지 않는다
Integer w2 = 100;
Integer w3 = 100;
// (Integer) (int -> Integer) 자동 포장
// AutoBoxing
// 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() {
// 로또 번호 생성기 v2
// Set<int> lotto = new HashSet<int>();
// int로 타입 제한을 할 수 없다
// 왜? int는 기본 자료형이기 때문에 객체만 저장하는 컬렉션에는 들어갈 수 없다
// -> 해결 방법 : Wrapper Class를 이용해서 기본 자료형을 객체로 포장한다
// Set<Integer> lotto = new HashSet<Integer>();
//Set<Integer> lotto = new LinkedHashSet<Integer>();
Set<Integer> lotto = new TreeSet<Integer>(); // 정렬해줌
// Integer는 equals(), hashCode() 오버라이딩 완료 상태
while(lotto.size()<6) {
int random = (int)(Math.random() * 45 + 1);
lotto.add(random);
// int 값이 자동으로 Integer로 포장(AutoBoxing)되어 lotto에 추가
}
System.out.println("로또 번호 : " + lotto);
}
}

컬렉션에 저장된 요소를 접근하는데 사용되는 인터페이스
[그림 1]

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


키 객체 만드는 법은 HashMap과 동일하나 Hashtable은 스레드 동기화가 된 상태이기 때문에,
복수의 스레드가 동시에 Hashtable에 접근해 객체를 추가, 삭제 하더라도 스레드에 안전 (Thread safe)

키와 값을 String타입으로 제한한 Map컬렉션
주로 Properties는 프로퍼티(*.properties)파일을 읽어 들일 때 주로 사용
검색 기능을 강화시킨 컬렉션으로, 계층 구조를 활용해 이진 트리 자료구조를 구현하여 제공

이진 트리를 기반으로 한 Set컬렉션으로, 왼쪽과 오른쪽 자식 노드를 참조하기 위한 두 개의 변수로 구성

이진 트리를 기반으로 한 Map 컬렉션으로, 키와 값이 저장된 Map.Entry를 저장하고
왼쪽과 오른쪽 자식 노드를 참조하기 위한 두 개의 변수로 구성



class MapRun
package edu.kh.collection.run;
import edu.kh.collection.model.service.MapService;
public class MapRun {
public static void main(String[] args) {
MapService service = new MapService();
service.ex1();
service.ex2();
service.ex3();
}
}
class MapService
package edu.kh.collection.model.service;
import java.util.*;
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(값 저장용 객체)는 특정 데이터 묶음의 재사용이 많은 경우 주로 사용
// -> 재사용이 적은 CO는 오히려 코드 낭비이다.
// -> Map을 이용해서 VO와 비슷한 코드를 작성할 수 있다
// 1) VO 버전
Member mem = new Member();
// 값 세팅
mem.setId("user01");
mem.setPw("pass01");
mem.setAge(34);
// 값 출력
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<>();
// value가 Object 타입 == 어떤 객체든 value에 들어올 수 있다
// 값 세팅
map.put("id", "user02");
map.put("pw", "pass02");
map.put("age", 28);
// int -> Integer(AutoBoxing) - 대입 -> Object
// 값 출력
System.out.println(map.get("id").toString());
// String java.lang.Object.toString() -> 정적 바인딩
// 실행 중 확인해보니깐 String 자식 객체 -> 자식 toString() 호출
// -> 동적 바인딩
System.out.println(map.get("pw"));
System.out.println(map.get("age"));
// *** Map에 저장된 데이터 순차적으로 접근하기 ***
// Map에서 Key만 모아두면 Set의 특징을 가진다
// -> 이를 활용할 수 있도록 Map에서
// keySet() 메소드 제공
// --> key만 모아서 Set으로 반환
Set<String> set = map.keySet(); // id, pw, age가 저장된 Set 반환
System.out.println(set);
// 향상된 for문
for(String key : set) {
System.out.println(map.get(key));
// key가 반복될 때마다 변경
// -> 변경된 key에 맞는 map의 value가 출력
}
// map에 저장된 데이터가 많거나 어떤 key가 있는지 불분명할 때 또는 map에 저장된 모든 데이터에 접근해야 할 때
// keySet() + 향상된 for문 코드를 사용
}

public void ex3() {
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
for(int i=0; i<10; i++) {
// Map 생성
Map<String, Object> map = new HashMap<>();
// Map 데이터 추가
map.put("id","user0"+i);
map.put("pw","pass0"+i);
// map을 List에 추가
list.add(map);
}
// for문 종료 시 List에는 10개의 Map 객체가 추가 되어있다
// * List에 저장된 Map에서 key가 "id"인 경우의 value를 모두 출력
// 향상된 for문
for(Map<String, Object> temp : list) {
System.out.println(temp.get("pw"));
}
}
}

class Member
package kh.edu.updown.model.vo;
public class Member {
private String memberId; // 아이디
private String memberPw; // 비밀번호
private String memberName; // 이름
private int highScore; // 최고 점수
// 생성자
public Member() { }
public Member(String memberId, String memberPw, String memberName) {
super();
this.memberId = memberId;
this.memberPw = memberPw;
this.memberName = memberName;
}
public Member(String memberId, String memberPw, String memberName, int highScore) {
super();
this.memberId = memberId;
this.memberPw = memberPw;
this.memberName = memberName;
this.highScore = highScore;
}
// getter / setter
public String getMemberId() {
return memberId;
}
public void setMemberId(String memberId) {
this.memberId = memberId;
}
public String getMemberPw() {
return memberPw;
}
public void setMemberPw(String memberPw) {
this.memberPw = memberPw;
}
public String getMemberName() {
return memberName;
}
public void setMemberName(String memberName) {
this.memberName = memberName;
}
public int getHighScore() {
return highScore;
}
public void setHighScore(int highScore) {
this.highScore = highScore;
}
@Override
public String toString() {
return "Member [memberId=" + memberId + ", memberPw=" + memberPw + ", memberName=" + memberName + ", highScore=" + highScore
+ "]";
}
}
class UpDownRun
package kh.edu.updown.run;
import kh.edu.updown.model.service.MainService;
public class UpDownRun {
public static void main(String[] args) {
MainService service = new MainService();
service.displayMenu();
}
}
class MainService
package kh.edu.updown.model.service;
import java.util.InputMismatchException;
import java.util.Scanner;
import kh.edu.updown.model.vo.Member;
public class MainService {
private Scanner sc = new Scanner(System.in);
// 회원 가입된 회원의 정보를 저장할 배열 선언 및 할당
private Member[] members = new Member[3];
private int memberCount = 0; // 현재 가입한 회원 수 (회원 가입 시 members 인덱스 지정에 사용 가능)
// 현재 로그인한 회원의 정보를 참조할 변수 선언.
private Member loginMember = null; // 로그인 X == null, 로그인 O != null
// LoginService 생성
// -> 로그인 시 이용할 수 있는 기능만을 모아둔 객체
private LoginService loginService = new LoginService();
// 메뉴 출력 메소드
// * 메뉴 출력 메소드를 분석해보세요!
public void displayMenu() {
int sel = 0; // 메뉴 선택용 변수
do {
try {
System.out.println();
System.out.println("=== UP/DOWN 게임 ===");
if(loginMember == null) { // 로그인이 되어있지 않은 경우
System.out.println("[메인 메뉴]");
System.out.println("1. 회원가입");
System.out.println("2. 로그인");
System.out.println("0. 종료");
System.out.print("메뉴 선택 >> ");
sel = sc.nextInt();
sc.nextLine(); // 입력 버퍼 개행문자 제거
System.out.println(); // 줄바꿈
switch(sel) {
case 1 : signUp(); break;
case 2 : login(); break;
case 0 : System.out.println("[프로그램 종료]"); break;
default : System.out.println("잘못 입력하셨습니다. 메뉴를 다시 선택해주세요.");
}
}else { // 로그인이 되어있는 경우
System.out.println("[로그인 메뉴]");
System.out.println("1. 업/다운 게임 start");
System.out.println("2. 내 정보 조회");
System.out.println("3. 전체 회원 조회");
System.out.println("4. 비밀번호 변경");
System.out.println("9. 로그아웃");
System.out.println("메뉴 선택 >> ");
sel = sc.nextInt();
sc.nextLine();
System.out.println();
// 선택된 메뉴 번호에 따라 LoginService에서 알맞은 기능을 호출
switch (sel) {
case 1: loginService.startGame(loginMember); break;
case 2: loginService.selectMyInfo(loginMember); break;
case 3: loginService.selectAllMember(members); break;
case 4: loginService.updatePassword(loginMember); break;
case 9 : System.out.println("[로그아웃 되었습니다.]");
loginMember = null; // loginMember 필드에 아무것도 참조하고 있지 않음을 의미하는 null을 대입
break;
default: System.out.println("잘못 입력하셨습니다. 메뉴를 다시 선택해주세요.");
sel = -1;
}
}
}catch (InputMismatchException e) {
System.out.println("정수만 입력해주세요.");
sc.nextLine(); // 버퍼에 남아있는 문자열 제거
}
}while(sel != 0);
}


// [회원 가입]
// 아이디, 비밀번호, 이름을 입력 받고
// Member객체를 생성하여 members 배열에 추가
// 단, 이미 중복되는 아이디가 존재하는 경우 가입 불가
public void signUp() {
System.out.println("[회원 가입]");
// memberCount가 members 배열 길이 이상인 경우 == 회원 가입 정원이 다 찬 상태
if(memberCount >= members.length) {
System.out.println("회원 정원이 가득차 더 이상 가입을 할 수 없습니다.");
} else { // 회원 가입이 가능한 경우
String memberId = null; // 아이디를 입력 받아 저장할 변수
while(true) {
System.out.print("아이디 : ");
memberId = sc.next();
boolean flag = true; // 중복 아이디 체크용 변수
for(int i=0; i<members.length; i++) {
if(members[i] != null) { // members 배열 요소가 null이 아닌 경우 == 회원 정보가 있는 경우
// 입력 받은 아이디와 같은 아이디가 members에 존재하는 경우
if(memberId.equals(members[i].getMemberId())) {
System.out.println("중복되는 아이디 입니다. 다시 입력해주세요.");
flag = false;
break; // 중복검사 for문 종료
}
}else { // members 배열 요소가 null인 경우 == 회원 정보가 없는 경우
break; // 중복검사 for문 종료
}
} // end for
if(flag) { // 중복 아이디가 아닌 경우
break; // 아이디를 반복적으로 입력 받는 while문 종료
}
} // end while
System.out.print("비밀번호 : ");
String memberPw = sc.next();
System.out.print("이름 : ");
String memberName = sc.next();
// 입력 받은 값을 이용해 새로운 Member 객체를 생성하고 members 배열의 요소 중
// memberCount번째 요소가 참조할 수 있도록 주소를 저장
members[memberCount] = new Member(memberId, memberPw, memberName);
// 새로운 회원이 가입되었으므로 memberCount를 1 증가
memberCount++;
System.out.println("***** 회원 가입이 완료되었습니다. *****");
} // end else (회원 가입이 가능한 경우)
} // end signUp()

// [로그인]
// 아이디, 비밀번호를 입력 받아 일치하는 회원이 members에 있을 경우 로그인
// 없으면 "아이디 또는 비밀번호가 일치하지 않습니다." 출력
public void login() {
System.out.println("[로그인]");
System.out.print("아이디 : ");
String inputId = sc.next();
System.out.print("비밀번호 : ");
String inputPw = sc.next();
for(int i=0; i<members.length; i++) {
if(members[i] != null) { // members 배열 요소가 null이 아닌경우 == 회원 정보가 있는 경우
// members 배열 요소 중 입력 받은 아이디, 비밀번호가 일치하는 회원이 있을 경우
if(members[i].getMemberId().equals(inputId) && members[i].getMemberPw().equals(inputPw)) {
System.out.println(members[i].getMemberName() + "님 환영합니다." );
loginMember = members[i]; // loginMember 필드에 일치하는 회원 객체의 주소를 얕은 복사
}
} else { // members 배열 요소가 null인 경우 == 회원 정보가 없는 경우
break;
}
} // end for
// 아이디, 비밀번호를 비교한 후에도 loginMember필드가 null인 경우 == 로그인 실패
if(loginMember == null) {
System.out.println("아이디 또는 비밀번호가 일치하지 않습니다.");
}
}
}


class LoginService
package kh.edu.updown.model.service;
import java.util.InputMismatchException;
import java.util.List;
import java.util.Scanner;
import kh.edu.updown.model.vo.Member;
public class LoginService {
private Scanner sc = new Scanner(System.in);
// 업다운 게임 시작
// 1 ~ 100 사이 숫자 중 랜덤하게 한 숫자를 지정하고 업/다운 게임을 진행
// 맞춘 횟수가 현재 로그인한 회원의 최초 또는 최고 기록인 경우 회원의 highScore 필드 값을 변경
public void startGame(Member loginMember) {
System.out.println("[Game Start...]");
// 1 ~ 100 사이 난수 발생
int random = (int)(Math.random() * 100 + 1);
int count = 0; // 입력 시도 횟수 카운트
try {
while(true) {
count++; // while문이 반복 될 때 마다 count를 1씩 증가시킴
System.out.print( count + "번째 입력 : ");
int input = sc.nextInt();
sc.nextLine();
if(random == input) { // 입력된 값이 발생한 난수와 같다면
System.out.println("정답!!");
System.out.println("입력 시도 횟수 : " + count);
// 입력 시도 횟수가 최초 또는 최고 기록인 경우
if(loginMember.getHighScore() == 0 || loginMember.getHighScore() > count ) {
System.out.println("*****최고 기록 달성******");
// 매개변수로 전달 받은 loginMember의 highScore 필드에 시도 횟수 저장
loginMember.setHighScore(count);
}
break; // while문 종료
} else { // 입력된 값이 발생한 난수와 같지 않다면
if(random < input) {
System.out.println("-- DOWN --");
}else {
System.out.println("-- UP --");
}
}
}
}catch(InputMismatchException e) {
System.out.println("1~100 사이 정수만 입력해주세요.");
sc.nextLine();
}
}

// 내 정보 조회
// 로그인한 멤버의 정보 중 비밀번호를 제외한 나머지 정보만 화면에 출력
public void selectMyInfo(Member loginMember) {
System.out.println("[내 정보 조회]");
System.out.println("아이디 : " + loginMember.getMemberId());
System.out.println("이름 : " + loginMember.getMemberName());
System.out.println("최고점수 : " + loginMember.getHighScore() + "회");
}

// 전체 회원 조회
// 전체 회원의 아이디, 이름, 최고점수를 출럭
public void selectAllMember(Member[] members) {
System.out.println("[전체 회원 조회]");
System.out.printf("%6s %6s %7s\n", "[아이디]", "[이름]", "[최고점수]");
for(int i=0; i<members.length; i++) {
if(members[i] != null) { // members 배열 요소에 저장된 회원 정보가 있는 경우
System.out.printf("%7s %6s %6d\n", members[i].getMemberId(), members[i].getMemberName(),
members[i].getHighScore() );
}
}
}

// 비밀번호 변경
// 현재 비밀번호를 입력 받아
// 같은 경우에만 새 비밀번호를 입력 받아 비밀번호 변경
public void updatePassword(Member loginMember) {
System.out.println("[비밀번호 변경]");
System.out.print("현재 비밀번호 입력 : ");
String currentPw = sc.next();
if(currentPw.equals(loginMember.getMemberPw())) {
System.out.print("새 비밀번호 : ");
String newPw = sc.next();
loginMember.setMemberPw(newPw);
System.out.println("비밀번호가 변경되었습니다.");
} else {
System.out.println("현재 비밀번호가 일치하지 않습니다.");
}
}
}


