28일차 메모 참조
package com.test.memo;
public class Practice2 {
static <T> void method(T[] arr) {
for (T t : arr) {
System.out.println(t);
}
}
public static void main(String[] args) {
String[] strArr = { "안녕", "오늘은", "백암순댓국", "먹으러갈거야" };
method(strArr);
}
}
package com.test.memo;
class Alchol {
void showYou() {
System.out.println("나는 술");
}
}
class Beer extends Alchol {
@Override
void showYou() {
System.out.println("나는 술 중에서 맥주");
}
}
class AlcholBox<T> {
T categ;
void store(T categ) {
this.categ = categ;
}
T pullOut() {
return categ;
}
}
public class Practice2 {
static void openAndShow(AlcholBox<? extends Alchol> box) {
Alchol alchol = box.pullOut();
alchol.showYou();
}
public static void main(String[] args) {
AlcholBox<Alchol> aBox = new AlcholBox<>();
aBox.store(new Alchol());
openAndShow(aBox);
AlcholBox<Beer> bBox = new AlcholBox<>();
bBox.store(new Beer());
openAndShow(bBox);
}
}

package com.test.memo;
class DBox<L, R> {
private L left;
private R right;
public void set(L o, R r) {
left = o;
right = r;
}
public String toString() {
return left + " & " + right;
}
}
class DDBox<U, D> {
private U box1;
private D box2;
void set(U box1, D box2) {
this.box1 = box1;
this.box2 = box2;
}
@Override
public String toString() {
return box1 + "\n" + box2;
}
}
public class Practice1 {
public static void main(String[] args) {
DBox<String, Integer> box1 = new DBox<>();
box1.set("Apple", 25);
DBox<String, Integer> box2 = new DBox<>();
box2.set("Orange", 33);
DDBox<DBox<String, Integer>, DBox<String, Integer>> ddbox = new DDBox<>();
ddbox.set(box1, box2); // 두 개의 상자를 하나의 상자에 담음
System.out.println(ddbox); // 상자의 내용물 출력
}
}

3-1. 위의 내용에 해당하는 프로그램은 사실 별도의 클래스를 정의하지 않고 DBox 하나로 충분히 편성할 수 있다. 따라서 이번에는 문제 1의 내용과 결과를 보이는 프로그램을 작성하되 DBox 클래스 하나만 활용하여 작성해보자.(상자에 담긴 내용물의 출력 형태는 달라도 괜찮다. 내용물만 전부 출력이 되면 된다.)
package com.test.memo;
class DBox<L, R> {
private L left;
private R right;
public void set(L o, R r) {
left = o;
right = r;
}
public String toString() {
return (left + " & " + right) + System.lineSeparator();
}
}
public class Practice1 {
public static void main(String[] args) {
DBox<String, Integer> box1 = new DBox<>();
box1.set("Apple", 25);
DBox<String, Integer> box2 = new DBox<>();
box2.set("Orange", 33);
DBox<DBox<String, Integer>, DBox<String, Integer>> boxx = new DBox<>();
boxx.set(box1, box2);
System.out.println(boxx);
}
}

단 이때 Box 인스턴스의 T는 Number 또는 이를 상속하는 하위 클래스만 올 수 있도록 제한된 매개변수 선언을 하자.
class Box {
private T ob;
public void set(T o) {
ob = o;
}
public T get() {
return ob;
}
}
class BoxSwapDemo {
// 이 위치에 swapBox 메소드 정의하자.
public static void main(String[] args) {
Box<Integer> box1 = new Box<>();
box1.set(99);
Box<Integer> box2 = new Box<>();
box2.set(55);
System.out.println(box1.get() + " & " + box2.get());
swapBox(box1, box2); // 정의해야 할 swapBox 메소드
System.out.println(box1.get() + " & " + box2.get());
}
}
99 & 55
55 & 99
package com.test.memo;
class Box<T> {
private T ob;
public void set(T o) {
ob = o;
}
public T get() {
return ob;
}
}
public class Practice1 {
static <T extends Number> void swapBox(Box<T> box1, Box<T> box2) {
T temp = box1.get();
box1.set(box2.get());
box2.set(temp);
}
public static void main(String[] args) {
Box<Integer> box1 = new Box<>();
box1.set(99);
Box<Integer> box2 = new Box<>();
box2.set(55);
System.out.println(box1.get() + " & " + box2.get());
swapBox(box1, box2); // 정의해야 할 swapBox 메소드
System.out.println(box1.get() + " & " + box2.get());
}
}
class Box {
private T ob;
public void set(T o) { ob = o; }
public T get() { return ob; }
}
class BoundedWildcardDemo {
public static void addBox(Box b1, Box b2, Box b3) {
b3.set(b1.get() + b2.get()); // 프로그래머의 실수가 있는 부분
}
public static void main(String[] args) {
Box box1 = new Box<>();
box1.set(24);
Box box2 = new Box<>();
box2.set(37);
Box result = new Box<>();
result.set(0);
addBox(result, box1, box2); // result에 24 + 37의 결과 저장
System.out.println(result.get()); // 61 출력
}
}
컴파일에서 실수 발견되도록
package com.test.memo;
class Box<T> {
private T ob;
public void set(T o) {
ob = o;
}
public T get() {
return ob;
}
}
public class Practice1 {
public static void addBox(Box<? super Integer> b1, Box<? extends Integer> b2, Box<? extends Integer> b3) {
b3.set(b1.get() + b2.get()); // 프로그래머의 실수가 있는 부분 >> 컴파일 오류 발생
}
public static void main(String[] args) {
Box<Integer> box1 = new Box<>();
box1.set(24);
Box<Integer> box2 = new Box<>();
box2.set(37);
Box<Integer> result = new Box<>();
result.set(0);
addBox(result, box1, box2); // result에 24 + 37의 결과 저장
System.out.println(result.get()); // 61 출력
}
}
인자값에서 와일드카드를 이용해 제한을 주면 b1은 Integer를 포함한 상위 클래스만 저장될 수 있게되고, b2, b3은 Integer의 하위 클래스만 저장될 수 있게 된다.
그러므로 현재 개발자는 b3에 더해해서 set하고 있기때문에 b1에서는 상위클래스로 제한했기 때문에 Object타입을 반환할것이므로 오류가 나는것이다.
이제 개발자가 실수한 부분을 고쳐주면 아래와 같다.
b1.set(b2.get() + b3.get()); // 프로그래머의 실수가 있는 부분
class Box {
private T ob;
public void set(T o) { ob = o; }
public T get() { return ob; }
}
class BoundedWildcardGeneric {
// box에 con과 동일한 내용물이 들었는지 확인
public static boolean compBox(Box box, T con) {
T bc = box.get();
box.set(con); // 프로그래머의 실수로 삽입된 문장, 때문에 내용물이 바뀐다.
return bc.equals(con);
}
public static void main(String[] args) {
Box box1 = new Box<>();
box1.set(24);
Box box2 = new Box<>();
box2.set("Poly");
if(compBox(box1, 25))
System.out.println("상자 안에 25 저장");
if(compBox(box2, "Moly"))
System.out.println("상자 안에 Moly 저장");
System.out.println(box1.get());
System.out.println(box2.get());
}
}
package com.test.memo;
class Box<T> {
private T ob;
public void set(T o) {
ob = o;
}
public T get() {
return ob;
}
}
public class Practice2 {// compBox메소드는 비교만을 수행하는 메서드로 내용물을 변경하는 내용이 개발자의 실수로 들어간것을 가정
public static <T> boolean compBox(Box<? extends T> box, T con) {
T bc = box.get();
box.set(con); // 프로그래머의 실수로 삽입된 문장, 때문에 내용물이 바뀐다. >> 현재 컴파일 에러
return bc.equals(con);
}
public static void main(String[] args) {
Box<Integer> box1 = new Box<>();
box1.set(24);
Box<String> box2 = new Box<>();
box2.set("Poly");
if (compBox(box1, 25))
System.out.println("상자 안에 25 저장");
if (compBox(box2, "Moly"))
System.out.println("상자 안에 Moly 저장");
System.out.println(box1.get());
System.out.println(box2.get());
}
}
자료구조(데이터를 효율적으로 저장, 조작하기 위한 방법) 와 데이터를 처리하는 알고리즘을 구조화하여 클래스로 구현한 모음집
데이터를 저장하는 자료 구조와 데이터를 처리하는 알고리즘을 구조화하여 클래스로 구현해 놓은 것
Tip
컬렉션 프레임워크에 저장할 수 있는 데이터는 오로지 객체 (Object)뿐이다.
primitive타입을 wrapper타입으로 변환하여 객체를 박싱(Boxing)하여 저장하여야 한다.
객체를 담는 다는 것은 곧 주소값을 담는것이니 null도 저장이 가능하다.

컬렉션 프레임워크는 크게 Collection 인터페이스 / Map 인터페이스(두개의 데이터를 묶어 한쌍으로 다룸)로 나뉜다.
Tip
대부분의 컬렉션 클래스들은 List, Set , Map 중의 하나를 구현하고 있으며, 구현한 인터페이스의 이름이 클래스 이름에 포함되는 특징이 있다. (ArrayList, HashSet, HashMap ... 등)
그러나 Vector, Stack, Hashtable등 의 클래스들은 컬렉션 프레임워크가 만들어지기 이전부터 존재하던 것이기 때문에 명명법을 따르지않고, Verctor(ArrayList의 구형버전)나 HashTable과 같은 기존의 컬렉션 클래스들은 호환을 위해 남겨진 것이므로 가급적 사용하지 않는 것이 좋다.
컬렉션 인터페이스들의 가장 최상위 인터페이스
Iterator객체를 관리하는 인터페이스
List, Set, Queue에 상속하는 실질적인 최상위 컬렉션 타입
업캐스팅으로 다양한 종류의 컬렉션 자료형을 받아 자료를 삽입하거나 삭제, 탑색 기능을 할 수 있다.

Info
Collection인터페이스의 메서드를 보면 요소(객체)에 대한 추가, 삭제, 탐색은 다형성 기능으로 사용이 가능하지만, 데이터를 get하는 메서드는 보이지 않는다.
왜냐하면 각 컬렉션 자료형 마다 구현하는 자료구조가 제각각 이기 때문에 최상위 타입으로 조회하기 까다롭기 때문이다.
List, Set, Map
| 순서 | 중복 | 특징 | |
|---|---|---|---|
| List | O | O | 순서가 보장되고 중복을 허용하는 데이터의 집합 |
| Set | X | X | 순서를 유지하지 않고 중복 또한 허용하지 않는 데이터의 집합 |
| Map | X | key: X / value: O | 키(key)와 값(value)의 쌍으로 이루어진 데이터의 집합 |

저장 순서가 유지되는 컬렉션을 구현하는데 사용
같은 요소의 중복 저장을 허용
배열처럼 인덱스를 사용하여 요소에 접근할 수 있다. >> get() , set()
자료형 크기가 고정이 아닌, 양에 따라 동적으로 늘었다 줄었다 할 수 있다(가변적)
요소 사이네 빈공간을 허용하지 않아 삽입/삭제 할때마다 배열 이동이 일어난다.
메소드


가장 많이 사용하는 List 구현 클래스(전체 컬렉션 클래스 중에서도 가장 많이 사용)
배열을 이용하여 만든 리스트
데이터의 저장순서가 유지되고 중복을 허용
데이터량에 따라 공간(capacity)이 자동으로 늘어나거나 줄어든다.
장점
구조가 간단하고 데이터를 읽는데 걸리는 시간(접근시간, access time)이 짧다.
단방향 포인터 구조로 자료에 대한 순차적인 접근에 강점이 있어 조회가 빠르다
단점
비순차적인 데이터의 추가, 삭제에 시간이 많이 걸린다.
순차적인 데이터 추가(끝에 추가)와 삭제(끝부터 삭제)는 빠르다.
데이터를 추가하거나 삭제하기 위해 다른 데이터를 옮겨야 한다.
List<String> arrayList = new ArrayList<>();
arrayList.add("Hello");
arrayList.add("World");
arrayList.get(0) // "Hello"
//ArrayList 선언
ArrayList list = new ArrayList();//타입 미설정 Object로 선언된다.
ArrayList<Student> members = new ArrayList<Student>();//타입설정 Student객체만 사용가능
ArrayList<Integer> num = new ArrayList<Integer>();//타입설정 int타입만 사용가능
ArrayList<Integer> num2 = new ArrayList<>();//new에서 타입 파라미터 생략가능
ArrayList<Integer> num3 = new ArrayList<Integer>(10);//초기 용량(capacity)지정
ArrayList<Integer> list2 = new ArrayList<Integer>(Arrays.asList(1,2,3));//생성시 값추가
ArrayList list = new ArrayList();는 추천하지 않는다.메소드
boolean add(Object o) : ArrayList의 마지막에 객체를 추가 > 성공하면 truevoid add(int indexm Object element) : 지정된 위치(index)에 객체를 저장boolean addAll(Collection c) : 주어진 컬렉션의 모든 객체를 저장한다.boolean addAll(int index, Collection c) : 지정된 위치부터 주어진 컬렉션의 모든 객체를 저장한다. void clear() : ArrayList를 완전히 비운다.Object clone() : ArrayList를 복제한다.boolean contains(Object o) : 지정된 객체(o)가 ArrayList에포함되어 있는지 확인int indexOf(Object o) : 지정된 객체가 저장된 위치를 찾아 반환한다.int lastindexOf(Object to) : 객체(o)가 저장된 위치를 끝부터 역방향으로 검색해서 반환한다.boolean isEmpty() : ArrayList가 비어있는지 확인한다.iterator iterator() : ArrayList의 iterator객체를 반환
예제 1
import java.util.*;
class ArrayListEx1{
public static void main(String[] args) {
ArrayList list1 = new ArrayList(10);
list1.add(Integer.valueOf(5)); //valueOf()메소드는 해당 값을 객체로 반환한다.
list1.add(Integer.valueOf(4));
list1.add(Integer.valueOf(2));
list1.add(Integer.valueOf(0));
list1.add(Integer.valueOf(1));
list1.add(Integer.valueOf(3));
ArrayList list2 = new ArrayList(list1.subList(1,4)); //list1의 인덱스 1~3까지 반환해 복사하는 것
print(list1, list2);
// list1:[5, 4, 2, 0, 1, 3]
// list2:[4, 2, 0]
Collections.sort(list1); // list1과 list2를 오름차순(기본)정렬한다.
Collections.sort(list2); // Collections.sort(List l)
print(list1, list2);
// list1:[0, 1, 2, 3, 4, 5]
// list2:[0, 2, 4]
System.out.println("list1.containsAll(list2):" + list1.containsAll(list2));
// list1.containsAll(list2):true > list1이 list2의 요소를 모두 포함하고 있는지 여부를 확인하는 메소드다.
list2.add("B");
list2.add("C");
list2.add(3, "A");
print(list1, list2); //list2의 인덱스 3위치에 "A"요소를 추가하는 것 > 덮어쓰기가 아닌 해당 인덱스에 넣고 그 뒤 배열은 뒤로 밀리는 형태
// list1:[0, 1, 2, 3, 4, 5]
// list2:[0, 2, 4, A, B, C]
list2.set(3, "AA");//list2의 인덱스 3위치의 값을 "AA"로 변경하는 작업 > 덮어쓰기의 형태
print(list1, list2);
// list1:[0, 1, 2, 3, 4, 5]
// list2:[0, 2, 4, AA, B, C]
// list1에서 list2와 겹치는 부분을 제외한 나머지는 삭제한다.
System.out.println("list1.retainAll(list2):" + list1.retainAll(list2));
// list1.retainAll(list2):true
// reatinAll의 호출결과로 list1이 변했다면 true를 반환한다.
print(list1, list2);
// list1:[0, 2, 4]
// list2:[0, 2, 4, AA, B, C]
// list2에서 list1에 포함된 객체들을 삭제한다.
for(int i= list2.size()-1; i >= 0; i--) {
if(list1.contains(list2.get(i)))
list2.remove(i);
}
print(list1, list2);
// list1:[0, 2, 4]
// list2:[AA, B, C]
} // main의 끝
static void print(ArrayList list1, ArrayList list2) {
System.out.println("list1:"+list1);
System.out.println("list2:"+list2);
System.out.println();
}
} // class
/*
// 실행 결과
list1:[5, 4, 2, 0, 1, 3]
list2:[4, 2, 0]
list1:[0, 1, 2, 3, 4, 5]
list2:[0, 2, 4]
list1.containsAll(list2):true
list1:[0, 1, 2, 3, 4, 5]
list2:[0, 2, 4, A, B, C]
list1:[0, 1, 2, 3, 4, 5]
list2:[0, 2, 4, AA, B, C]
list1.retainAll(list2):true
list1:[0, 2, 4]
list2:[0, 2, 4, AA, B, C]
list1:[0, 2, 4]
list2:[AA, B, C]
*/
Integer.valueOf(5)는 정수 값 5를 'Integer'객체로 변환해 넣는것이다. ArrayList의 경우, 제네릭으로 객체를 저장하기 때문에 래퍼 클래스로 래핑해서 추가 해야한다.중간중간 데이터를 insert를 해야할 경우가 많다면 LinkedList를 활용하는게 더 좋다.

ArrayList의 단점을 보완 : ArrayList와 달리 불연속적으로 존재하는 데이터를 연결(link)한다.
노드를 연결하여 리스트처럼 만든 컬렉션 >> 배열이 아님
자바의 LinkedList는 Doubly LinkedList(양방향 포인터 구조)로 이루어져 있다.
장점
: 데이터의 중간 삽입, 삭제가 빈번할 경우 빠른 성능을 보장한다.

단점
: 임의의 요소에 대한 접근 성능은 좋지 않다.
* 참고
① Doubly Linked List
: LinkedList의 단점을 보완하는 이중 연결리스트, 접근성 향상 (앞뒤로 이동)
② Doubly Circular Linked List
: 이중 원형 연결리스트 (앞뒤로 이동 + 맨앞과 맨뒤도 연결되어 있음)
List<String> linkedList = new LinkedList<>();
linkedList.add("Hello");
linkedList.add("World");
linkedList.get(0); // "Hello"
//LinkedList선언
LinkedList list = new LinkedList();//타입 미설정 Object로 선언된다.
LinkedList<Student> members = new LinkedList<Student>();//타입설정 Student객체만 사용가능
LinkedList<Integer> num = new LinkedList<Integer>();//타입설정 int타입만 사용가능
LinkedList<Integer> num2 = new LinkedList<>();//new에서 타입 파라미터 생략가능
LinkedList<Integer> list2 = new LinkedList<Integer>(Arrays.asList(1,2));//생성시 값추가
LinkedList 값 추가
LinkedList<Integer> list = new LinkedList<Integer>();
list.addFirst(1);//가장 앞에 데이터 추가
list.addLast(2);//가장 뒤에 데이터 추가
list.add(3);//데이터 추가
list.add(1, 10);//index 1에 데이터 10 추가
LinkedList 값 검색
LinkedList<Integer> list = new LinkedList<Integer>(Arrays.asList(1,2,3));
System.out.println(list.contains(1)); //list에 1이 있는지 검색 : true
System.out.println(list.indexOf(1)); //1이 있는 index반환 없으면 -1
위에서 LinkedList는 앞에 'Single'이 생량되어 있는 셈이다.
✔️ 이중연결리스트(Doubly Linked List)
단일연결리스트는 삽입/삭제 시 반드시 이전 노드를 가리키는 레퍼런스를 추가로 알아내야 하고 역방향으로 노드 탐색이 불가함이중연결리스트는 이러한 단점을 보완
각 노드마다 한 개의 레퍼런스를 추가로 저장해야 한다는 단점이 있음
참조 변수를 하나 더 추가하여 다음 요소에 대한 참조 뿐 아니라 이전 요소에 대한 참조가 가능하도록 한다.
💡 이중연결리스트의 노드 구조
각 노드가 두 개의 레퍼런스로 각각 이전 노드와 다음 노드를 가리킴

1. 기본적인 이중연결리스트 클래스
💡 header와 trailer 초기화하는 이유
두 노드는 실제로 데이터 저장하지 않는 Dummy 노드, 빈 노드들을 의도적으로 남겨두어 리스트가 비어있는 경우가 발생하지 않게 함
public class DoublyLinkedList<E> {
private static class Node<E> {
private E element;
private Node<E> prev; // 연결리스트의 이전 노드
private Node<E> next; // 연결리스트의 다음 노드
public Node(E e, Node<E> p, Node<E> n) {
element = e;
prev = p;
next = n;
}
public E getElement() { return element; }
public Node<E> getPrev() { return prev; }
public Node<E> getNext() { return next; }
public void setPrev(Node<E> p) { prev = p; }
public void setNext(Node<E> n) { next = n; }
private Node<E> header;
private Node<E> trailer;
private int size = 0;
public void DoublyLinkedList() {
header = new Node<E> (null, null, null);
trailer = new Node<E> (null, header, null);
header.setNext(trailer);
}
public int size() { return size; }
public boolean isEmpty() { return size == 0;}
// 첫번째 노드 값 리턴
public E first() {
if(isEmpty()) return null;
return header.getNext().getElement();
}
// 마지막 노드 값 리턴
public E last() {
if(isEmpty()) return null;
return trailer.getNext().getElement();
}
}
}
맨 앞에 노드 삽입
public void addFirst(E e) {
addBetween(e, header, header.getNext());
}
맨 뒤에 노드 삽입
public void addLast(E e) {
addBetween(e, trailer.getPrev(), trailer);
}
첫 번째 노드 삭제
public E deleteFirst() {
if(isEmpty()) return null;
return remove(header.getNext());
}
마지막 노드 삭제
public E deleteLast() {
if(isEmpty()) return null;
return remove(trailer.getPrev());
}
💡 addBetween/remove 메소드를 reuse하여 제일 앞과 뒤의 노드 추가 및 삭제 구현
💡 addBetween은 실제로는 앞 노드만 인자로 가지는 addNext로 remove는 removeNext로 바꾸는 것을 권장
두 노드 사이에 값 추가
private void addBetween(E e, Node<E> predecessor, Node<E> successor) {
Node<E> newest = new Node(e, predecessor, successor);
predecessor.setNext(newest);
successor.setPrev(newest);
size++;
}
특정 노드 뒤의 노드 삭제
private E remove(Node<E> node) {
Node<E> predecessor = node.getPrev();
Node<E> successor = node.getNext();
predecessor.setNext(successor);
successor.setPrev(predecessor);
size--;
return node.getElement();
}

ArrayList vs LinkedList 성능 비교
| 컬렉션 | 읽기(접근시간) | 추가/삭제 | 비고 |
|---|---|---|---|
| ArrayList | 빠름 | 느림 | 순차적인 추가삭제는 더 빠름. 비효율적인 메모리사용 |
| LinkedList | 느림 | 빠름 | 데이터가 많을수록 접근성이 떨어짐 |
1. 순차적으로 데이터를 추가/삭제 - ArrayList가 빠름
2. 비순차적으로 데이터를 추가/삭제 - LinkedList가 빠름
3. 접근시간(access time) == 읽기 - ArrayList가 빠름

데이터의 중복을 허용하지 않고 순서를 유지하지 않는 데이터의 집합 리스트
순서 자체가 없으므로 인덱스로 객체를 검색해서 가져오는 get(index) 메서드도 존재하지 않는다.
중복 저장이 불가능하다. >> 심지어 null값도 하나만 저장할 수 있다.
| 메서드 | 설명 |
|---|---|
| boolean add(E e) | 주어진 객체를 저장 한 후 성공적이면 true, 중복 객체면 false를 리턴한다. |
| boolean contains(Object o) | 주어진 객체가 저장되어있는지 여부를 리턴한다. |
| Iterator\ iterator() | 저장된 객체를 한번씩 가져오는 반복자를 리턴한다. |
| isEmpty() | 컬렉션이 비어있는지 조사한다. |
| int Size() | 저장되어 있는 전체 객체 수를 리턴한다. |
| void clear() | 저장된 모든 객체를 삭제한다. |
| boolean remove(Object o) | 주어진 객체를 삭제한다. |
Set 컬렉션은 인덱스를 통해 객체를 검색해서 가져오는 메소드가 없기 때문에, 전체 객체를 대상으로 한번씩 반복하여 가져오는 Iterator(반복자)를 제공한다.
Iterator 생성 예제
Set<String> set = new HashSet<String>();
Iterator<String> iterator = set.iterator();
Iterator인터페이스에 선언된 메소드
| 타입 | 메소드 | 설명 |
|---|---|---|
| boolean | hasNext() | 가져올 객체가 있다면 true를 return, 없다면 false를 return 한다 |
| E | next() | 하나의 객체를 가져온다 |
| void | remove() | 객체를 제거한다 |
Iterator에서 하나의 객체를 가져올 떄는hasNext()를 통해서 가져올 객체가 있는지 확인한 후 next()메소드를 사용한다.
Set <String> set = new HashSet<String>();
Iterator<String> iterator = set.iterator();
while(iterator.hasNext()){
String str = iterator.next();
if(str.equals("AA"){
//실제 set에서 "AA"객체가 삭제된다.
iterator.remove();
}
}
가장 많이 사용하는 Set 구현 클래스 - 순서x, 중복x
순서를 유지하려면 LinkedHashSet 클래스를 사용하면 된다.
순서o, 중복 x
입력된 순서대로 데이터를 관리
이진 탐색 트리(binary search tree) : 부모보다 작은 값은 왼쪽, 큰 값은 오른쪽에 저장
저장되는 요소가 오름차순으로 정렬되어서 저장된다.
단점 : 데이터가 많아 질수록 추가, 삭제에 시간이 더 걸린다. >> 비교횟수 증가
Hashing 을 이용해서 구현한 컬렉션
데이터를 중복 저장할 수 없고, 순서를 보장하지 않는다.
equals()나 hashCode()를 오버라이딩해, 인스턴스가 달라도 동일 객체를 구분해 중복저장을 막을 수 있다. >> Object클래스의 메소드
TreeSet이나LinkedHashSet보다 성능이 더 빠르고, 메모리를 적게 사용한다.

배열과 연결 노드를 결합한 자료구조 형태
가장 빠른 임의 검색 접근 속도를 가진다.
추가, 삭제, 검색, 접근성이 모두 뛰어나지만 >> 순서를 예측할 수 없다.
Set<Integer> hashSet = new HashSet<>(); //HashSet생성 방
hashSet.add(10);
hashSet.add(20);
hashSet.add(30);
hashSet.add(10); // 중복된 요소 추가
hashSet.size(); // 3 - 중복된건 카운트 X
hashSet.toString(); // [20, 10, 30] - 자료 순서가 뒤죽박죽
//HashSet사용법
HashSet<Integer> set1 = new HashSet<Integer>();//HashSet생성
HashSet<Integer> set2 = new HashSet<>();//new에서 타입 파라미터 생략가능
HashSet<Integer> set3 = new HashSet<Integer>(set1);//set1의 모든 값을 가진 HashSet생성
HashSet<Integer> set4 = new HashSet<Integer>(10);//초기 용량(capacity)지정
HashSet<Integer> set5 = new HashSet<Integer>(10, 0.7f);//초기 capacity,load factor지정
HashSet<Integer> set6 = new HashSet<Integer>(Arrays.asList(1,2,3));//초기값 지정
HashSet에 객체를 저장하고 가져오기 예제
package com.test.memo;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
class Person { // Person클래스
private String name; // 필드
private String id;
Person(String name, String id) {
this.name = name;
this.id = id;
}
String getName() {
return name;
}
String getId() {
return id;
}
}
public class Practice2 {
public static void main(String[] args) {
Set<Person> person = new HashSet<>(); // 제네릭타입이 Person인 HashSet
person.add(new Person("토니 스타크", "ironman")); // 객체 생성
person.add(new Person("피터 파커", "spiderman")); // 객체 생성
Iterator<Person> it = person.iterator(); // 반복자 생성
while (it.hasNext()) {
Person ps = it.next(); // person에 저장된 객체의 참조값 저장
System.out.println("아이디: " + ps.getId()); // private으로 선언되어있어 메서드를 통해야 한다.
System.out.println("이름: " + ps.getName());
System.out.println();
}
}
}

HashSet은 같은 인스턴스가 아니더라도 동일 객체를 구분하여 중복 저장을 막을 수 있다.
HashSet에 객체를 저장하기 전에, hashCode()를 호출해 해시코드를 얻어낸다.
저장되어있는 객체들의 해시코드와 비교해서, 동일한 해시코드가 있다면 다시 equlas()로 두 객체를 비교해 true가 나오면 동일한 객체로 판단해 중복 저장을 하지 않는 방식이다.
Lotto프로그램 만들때 등등 사용 가능
중복을 허용하지 않고, 순서를 가지지 않는다. >> 대신 데이터를 오름차순 정렬하여 저장하고 있다는 특징이 있다.
데이터 추가, 삭제에는 시간이 더 걸리지만, 검색과 정렬이 뛰어나다.
TreeSet 은 기본적으로 nature ordering을 지원하며 Comparator인터페이스를 구현하여 정렬방법을 임의로 지정해 줄 수 있다.

이진 탐색 트리(binary search tree) 자료구조의 형태로 데이터를 저장
정렬, 검색, 범위 검색에 높은 성능을 지닌다.
Set<Integer> treeSet = new TreeSet<>();
treeSet.add(7);
treeSet.add(4);
treeSet.add(9);
treeSet.add(1);
treeSet.add(1); //입력되는 값이 TreeSet내부에 존재하지 않는다면 그 값을 추가한 뒤 true를 반환하고 내부에 값이 존재하면 false를 반
treeSet.toString(); // [1, 4, 5, 7, 9] - 자료가 알아서 정렬됨
//TreeSet선언
TreeSet<Integer> set1 = new TreeSet<Integer>();//TreeSet생성
TreeSet<Integer> set2 = new TreeSet<>();//new에서 타입 파라미터 생략가능
TreeSet<Integer> set3 = new TreeSet<Integer>(set1);//set1의 모든 값을 가진 TreeSet생성
TreeSet<Integer> set4 = new TreeSet<Integer>(Arrays.asList(1,2,3));//초기값 지정
TreeSet을 생성하기 위해서는 저장할 객체 타입을 파라미터로 표기하고 기본 생성자를 호출하면 된다.
HashSet은 중복을 허용하지 않기 때문에 equals(), hashCode()메서드를 이용해 객체의 존재여부를 확인해 객체에 저장하는 것이다.
Comparable과 Comparator인터페이스는 객체의 정렬기준을 방식을 정의할 때 사용하는 인터페이스로 위와는 각각 다른 기능으로 존재한다.
순서를 가지는 Set 자료
추가된 순서 또는 가장 최근에 접근한 순서대로 접근 가능
만일 중복을 제거하는 동시에 저장한 순서를 유지하고 싶다면, HashSet대신 LinkedHashSet을 사용하면 된다.
Set<Integer> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add(10);
linkedHashSet.add(20);
linkedHashSet.add(30);
linkedHashSet.add(10); // 중복된 수 추가
linkedHashSet.size(); // 3 - 중복된건 카운트 X
linkedHashSet.toString(); // [10, 20, 30] - 대신 자료가 들어온 순서대로 출력
Java에서 제공하는 정렬 가능한 클래스들은 모두 Comparable인터페이스를 구현하고 있다.
기본 정렬기준을 구현하는데 사용
int/long오름차순, String 사전순 정렬
정렬할 객체 클래스에 Comparable인터페이스를 구현(implements)한 후, compareTo()메서드를 오버라이딩하여 구현한다.
Integer, Double, String 모두 Comparable 인터페이스를 구현하고 있다.
사용자 정의 클래스도 Comparable을 구현하면 자동 정렬이 가능하다.
int caompareTo(T o) : 자기 자신을 기준으로 주어진 객체와 같으면 0 리턴Arrays.sort( )는 String의 Comparable구현에 의해 정렬된 것이다.
Comparable을 구현하고 있는 클래스들은 같은 타입의 인스턴스끼리 서로 비교할 수 있어, 기본적으로 오름차순 형태로 구현된다.
Comparable을 구현한 클래스의 인스턴스들은 Collections.sort()메서드를 사용해 정렬할 수 있다.
예제 1 - 나이 오름차순으로 정렬
package com.test.memo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
class Person implements Comparable<Person> {
private String name;
private int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
String getName() {
return name;
}
int getAge() {
return age;
}
String getPrint() {
return name + " " + age;
}
@Override
public int compareTo(Person o) {
return this.age - o.age;
}
}
public class Practice2 {
public static void main(String[] args) {
ArrayList<Person> list = new ArrayList<>();
list.add(new Person("마우스", 23));
list.add(new Person("노트", 28));
list.add(new Person("물", 14));
list.add(new Person("의자", 35));
list.add(new Person("스크린", 54));
list.add(new Person("가시오", 17));
list.add(new Person("김유빈", 19));
list.add(new Person("정현지", 24));
Collections.sort(list); // 적용하지 않으면 compareTo()가 실행되지 않아 정렬 되지 않는다.
// Arrays.sort(list);> 에러 >> 배열의 요소가 원시 자료형인경우 정렬 가능하기 때문에 Wrapper클래스는 사용 X
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i).getPrint());
}
}
}

예제 2 - 이름 사전순 정렬
package com.test.memo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
class Person implements Comparable<Person> {
private String name;
private int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
String getName() {
return name;
}
int getAge() {
return age;
}
String getPrint() {
return name + " " + age;
}
@Override
public int compareTo(Person o) {
return this.name.compareTo(o.name);
}
}
public class Practice2 {
public static void main(String[] args) {
ArrayList<Person> list = new ArrayList<>();
list.add(new Person("가우스", 23));
list.add(new Person("노트", 28));
list.add(new Person("다", 14));
list.add(new Person("사자", 35));
list.add(new Person("마크린", 54));
list.add(new Person("바시오", 17));
list.add(new Person("아유빈", 19));
list.add(new Person("정현지", 24));
Collections.sort(list); // 적용하지 않으면 compareTo()가 실행되지 않아 정렬 되지 않는다.
// Arrays.sort(list);> 에러 >> 배열의 요소가 원시 자료형인경우 정렬 가능하기 때문에 Wrapper클래스는 사용 X
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i).getPrint());
}
}
}

예제 3 - 이름 순으로 정렬, 이름이 같을 경우, 나이 순으로 정렬 >> 보통 이렇게 정렬 조건을 추가하면 Comparator을 더 많이 사용
package com.test.memo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
class Person implements Comparable<Person> {
private String name;
private int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
String getName() {
return name;
}
int getAge() {
return age;
}
String getPrint() {
return name + " " + age;
}
@Override
public int compareTo(Person o) {
int result = this.name.compareTo(o.name);
if (result == 0)
result = this.age - o.age;
return result;
}
}
public class Practice2 {
public static void main(String[] args) {
ArrayList<Person> list = new ArrayList<>();
list.add(new Person("가시오", 23));
list.add(new Person("나비야", 28));
list.add(new Person("마", 14));
list.add(new Person("가시오", 35));
list.add(new Person("다이슨", 54));
list.add(new Person("마", 17));
list.add(new Person("아야", 19));
list.add(new Person("자자 윀업", 24));
Collections.sort(list); // 적용하지 않으면 compareTo()가 실행되지 않아 정렬 되지 않는다.
// Arrays.sort(list);> 에러 >> 배열의 요소가 원시 자료형인경우 정렬 가능하기 때문에 Wrapper클래스는 사용 X
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i).getPrint());
}
}
}

뺼셈 과정에서 자료형의 범위를 넘어버리는 경우가 발생할 수 있다.
권장사항은 if문을 통한 대소 비교
mport java.util.*;
public class Test {
public static void main(String[] args) {
Student hong = new Student("홍길동", 20);
Student kim = new Student("김철수", 22);
// 우리가 재정의 했던 compareTo() 메서드를 호출하고
// 매개변수로 kim 객체를 전달해주면
// compareTo() 의 내부에 작성한 비교 코드를 통해
// 양수, 음수, 0 값중 하나를 반환해준다.
// 1: 자기 자신, 즉 hong이 더 크다.
// 0: hong과 kim의 크기는 같다.
// -1: kim의 크기가 더 크다.
int compareValue = hong.compareTo(kim);
if (compareValue > 0)
System.out.println("hong 객체가 kim 객체보다 크다.");
else if (compareValue < 0)
System.out.println("hong 객체가 kim 객체보다 작다.");
else
System.out.println("hong 객체 kim 객체의 크기는 같다.");
}
}
class Student implements Comparable<Student>{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Student o) {
if (this.age > o.age)
return 1;
else if (this.age < o.age)
return -1;
else
return 0;
}
}
정리
- 자기 자신과
compareTo()의 매개변수로 받은 객체를 비교
compareTo()를 반드시 구현해야 함
기본 정렬기준 외에 다른 기준으로 정렬하고자 할때 사용
int/long 내림차순, String사전역순, 여러개의 기준으로 정렬
Comparable인터페이스를 구현하지 않은 클래스의 인스턴스들을 비교하거나, 기존의 정렬 기준과 다른 정렬 기준을 사용하고자 할 때 사용된다.
📌 매개변수로 받은 두개의 객체를 서로 비교
// Student 클래스는 Comparator<T> 인터페이스를 구현 받는다.
class Student implements Comparator<Student>{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// Comparator<T>에서 정의된 compare 재정의
@Override
public int compare(Student o1, Student o2) {
if (o1.age > o2.age)
return 1;
else if (o1.age == o2.age)
return 0;
else
return -1;
/*
return o1.age - o2.age;
*/
}
}
Comparable 과 비교 방법도 같다. 차이점은 자기 자신과의 비교compare() 또한 주석처리된 부분처럼 작성 가능import java.util.Comparator;
public class Main {
public static void main(String[] args) {
Student hong = new Student("홍길동", 20);
Student kim = new Student("김철수", 22);
int compareValue = hong.compare(hong, kim);
if (compareValue > 0)
System.out.println("hong 객체가 kim 객체보다 크다.");
else if (compareValue < 0)
System.out.println("hong 객체가 kim 객체보다 작다.");
else
System.out.println("hong 객체 kim 객체의 크기는 같다.");
}
}
class Student implements Comparator<Student>{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compare(Student o1, Student o2) {
if (o1.age > o2.age)
return 1;
else if (o1.age == o2.age)
return 0;
else
return -1;
}
}
hong.compare(hong, kim) 을 보면 객체에 상관 없이 독립적으로 두 객체를 매개변수로 전달에 비교한다.특정 클래스 내부에 Comparator인터페이스를 상속받아, compare() 를 구현 해준다면, 해당 compare()를 사용하기 위해선 해당 클래스 타입의 객체를 생성해 주어야한다.
public class Main {
public static void main(String[] args) {
Student hong = new Student("홍길동", 20);
Student kim = new Student("김철수", 22);
Student lee = new Student("이발수", 24);
int compareValue = hong.compare(hong, lee);
int compareValue = hong.compare(kim, kim);
int compareValue = hong.compare(kim, lee);
}
}
예제 1
public class Main {
public static void main(String[] args) {
Student hong = new Student("홍길동", 20);
Student kim = new Student("김철수", 22);
Student choi = new Student("최치김", 35);
Student lee = new Student("이문복", 28);
Student jung = new Student("정순하", 50);
// 리스트에 학생 객체들 저장.
Student[] list = new Student[5];
list[0] = hong;
list[1] = jung;
list[2] = choi;
list[3] = lee;
list[4] = kim;
// 정렬 전
System.out.println(Arrays.toString(list));
Comparator<Student> comp = new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
if (o1.age > o2.age)
return 1;
else if (o1.age == o2.age)
return 0;
else
return -1;
}
};
// 정렬
Arrays.sort(list, comp);
// 정렬 후
System.out.println(Arrays.toString(list));
}
}
class Student {
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return this.name + " " + this.age;
}
}
main() 내부에 Comparator<Student>를 구현한 익명 클래스를 생성한뒤 Arrays.sort()의 두번째 매개변수로 전달sort()를 통해 Student 클래스 내부에 정의된 Comparable<>의 추상 메서드 compareTo 존재 유무 상관 없이, Comparator 익명 클래스의 compare() 메서드를 통해 배열 요소의 정렬 수행.예제 2
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Main {
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("Alice", 30));
personList.add(new Person("Bob", 25));
personList.add(new Person("Charlie", 35));
// Comparator 인터페이스를 직접 구현하여 나이를 기준으로 내림차순으로 정렬합니다.
Comparator<Person> ageComparator = new Comparator<Person>() {
@Override
public int compare(Person p1, Person p2) {
return Integer.compare(p2.getAge(), p1.getAge()); // 내림차순으로 정렬
} //오름차순으로 하려면 p1,p2로
};
// Collections.sort() 메서드를 사용하여 리스트를 정렬합니다.
Collections.sort(personList, ageComparator);
// 정렬된 리스트를 출력합니다.
for (Person person : personList) {
System.out.println(person);
}
}
}

Integer.compare를 사용하여 두 정수를 비교하는 것이 일반적이고 편리하다.Comparable / Comparator
클래스의 정렬 기준이 고유하게 존재하고, 클래스 자체에 논리적으로 포함되어 있는 경우 >> Comparable
String클래스는 이미 Comparable을 구현하여 알파벳 순서로 정렬된다.정렬 기준이 클래스 외부에서 정의되어야 하거나 다양한 정렬 기준이 필요한 경우 >> Comparator
Comparable을 사용하다가, 특별한 정렬이 필요하다면 그때 쓰이는 경우가 많다.예제 1
import java.util.ArrayList;
class IntroArrayList
{
public static void main(String[] args)
{
ArrayList<Integer> list=new ArrayList<Integer>();
/* 데이터의 저장 */
list.add(11); //오토박싱 오토언박싱 진
list.add(Integer.valueOf(22));
list.add(Integer.valueOf(33));
//list의 길이는 현재 3인
/* 데이터의 참조 */
System.out.println("1차 참조");
for(int i=0; i<list.size(); i++)
System.out.println(list.get(i));
/* 데이터의 삭제 */
list.remove(0);//0번째 인덱스를 삭제
System.out.println("2차 참조");
for(int i=0; i<list.size(); i++)
System.out.println(list.get(i));
}
}
예제 2
import java.util.LinkedList;
class IntroLinkedList
{
public static void main(String[] args)
{
List<Integer> list = new LinkedList<Integer>(); //상위클래스로 사용하는 이유는 상
//LinkedList<Integer> list=new LinkedList<Integer>();
// ArrayList<Integer> list = new ArrayList<Integer>();
/* 데이터의 저장 */
list.add(Integer.valueOf(11));
list.add(Integer.valueOf(22));
list.add(Integer.valueOf(33));
/* 데이터의 참조 */ //> 아래와 같이 반복문을 써서 출력하는것보다 foreach나 변수에 담아서 출력하는게 좋다.
System.out.println("1차 참조");
for(int i=0; i<list.size(); i++)
System.out.println(list.get(i));
for(Integer num : list)//이걸 더 선호 간단
System.out.println(num);
/* 데이터의 삭제 */
list.remove(0);
System.out.println("2차 참조");
for(int i=0; i<list.size(); i++)
System.out.println(list.get(i)); //0번째를 삭제했기 때문에 0번쨰에 22가 들어가있는
}
}
상위클래스 List로 사용하는 이유는, 호환성 때문이다.
List list = new LinkedList(); 이와같이 상위클래스에서 하위클래스를 사용할 수 있기 때문이다.예제 3 SingleLinkedList
단점 : 단방향 - 참조가 어렵다. >> 선형 Linked List == SingleLinkedList
장점: 삽입 삭제가 쉽다.
class Box<T>
{
public Box<T> nextBox;
T item;
public void store(T item) { this.item=item; }
public T pullOut() { return item; }
}
class SoSimpleLinkedListImpl
{
public static void main(String[] args)
{
Box<String> boxHead=new Box<String>();
boxHead.store("First String");
boxHead.nextBox=new Box<String>();
boxHead.nextBox.store("Second String");
boxHead.nextBox.nextBox=new Box<String>();
boxHead.nextBox.nextBox.store("Third String");
Box<String> tempRef;
/* 두 번째 박스에 담긴 문자열 출력 과정 */
tempRef=boxHead.nextBox;
System.out.println(tempRef.pullOut());
/* 세 번째 박스에 담긴 문자열 출력 과정 */
tempRef=boxHead.nextBox;
tempRef=tempRef.nextBox;
System.out.println(tempRef.pullOut());
}
}
boxHead.nextBox는 Box<String> 형식의 새로운 상자 객체를 생성하게 됩니다. 그 다음에 store() 메서드를 사용하여 해당 상자에 문자열 "Second String"을 저장합니다. 그 후에 다음 상자를 위해 boxHead.nextBox.nextBox에 새로운 Box<String> 객체를 생성하고, 이를 이용하여 다음 문자열을 저장할 수 있습니다.

예제 4
Iterable인터페이스 안에 iterator()메서드를 가지고 있다.(collection이 이를 상속)
import java.util.Iterator;
import java.util.LinkedList;
class IteratorUsage
{
public static void main(String[] args)
{
LinkedList<String> list=new LinkedList<String>();
list.add("First");
list.add("Second");
list.add("Third");
list.add("Fourth");
Iterator<String> itr=list.iterator();//iterator();는 일회용, 커서가 마지막이되면 끝
System.out.println("반복자를 이용한 1차 출력과 \"Third\" 삭제");
while(itr.hasNext())
{
String curStr=itr.next();//First객체를 반환하고 다음으로 커서가 이동한다.
System.out.println(curStr);
if(curStr.compareTo("Third")==0)
itr.remove();
}
System.out.println("\n\"Third\" 삭제 후 반복자를 이용한 2차 출력 ");
itr=list.iterator();//위에서 끝났으니까 다시 가져와야한
while(itr.hasNext())
System.out.println(itr.next());
}
}
예제 5
List는 저장순서가 존재
import java.util.Iterator;
import java.util.LinkedList;
class PrimitiveCollection
{
public static void main(String[] args)
{
LinkedList<Integer> list=new LinkedList<Integer>();
list.add(10); // Auto Boxing
list.add(20); // Auto Boxing
list.add(30); // Auto Boxing
Iterator<Integer> itr=list.iterator();
while(itr.hasNext())
{
int num=itr.next(); // Auto Unboxing
System.out.println(num);
}
}
}
컬렉션 프레임워크에 set이 들어가면 저장 순서가 없고, 중복이 불가능하다.
중복의 기준을, Object의 hashCode()와 equals()메서드를 이용해준다.
HashSet은 해시 테이블을 기반으로 구현되어 있어 빠른 검색, 삽입, 삭제 연산을 제공한다.
예제 1
import java.util.Iterator;
import java.util.HashSet;
class UsefulIterator
{
public static void main(String[] args)
{
HashSet<String> set=new HashSet<String>();
set.add("First");
set.add("Second");
set.add("Third");
set.add("Fourth");
Iterator<String> itr=set.iterator();
System.out.println("반복자를 이용한 1차 출력과 \"Third\" 삭제");
while(itr.hasNext())
{
String curStr=itr.next();
System.out.println(curStr);
if(curStr.compareTo("Third")==0)
itr.remove();
}
System.out.println("\n\"Third\" 삭제 후 반복자를 이용한 2차 출력 ");
itr=set.iterator();
while(itr.hasNext())
System.out.println(itr.next());
}
}
예제 2
import java.util.Iterator;
import java.util.HashSet;
class SetInterfaceFeature
{
public static void main(String[] args)
{
HashSet<String> hSet=new HashSet<String>();
hSet.add("First");
hSet.add("Second");
hSet.add("Third");
hSet.add("First");
System.out.println("저장된 데이터 수: "+hSet.size());
Iterator<String> itr=hSet.iterator();
while(itr.hasNext())
System.out.println(itr.next());
}
}
예제 3
import java.util.Iterator;
import java.util.HashSet;
class SimpleNumber
{
int num;
public SimpleNumber(int n)
{
num=n;
}
public String toString()
{
return String.valueOf(num);
}
}
class HashSetEqualityOne
{
public static void main(String[] args)
{
HashSet<SimpleNumber> hSet=new HashSet<SimpleNumber>();
hSet.add(new SimpleNumber(10));
hSet.add(new SimpleNumber(20));
hSet.add(new SimpleNumber(20));
System.out.println("저장된 데이터 수: "+hSet.size());//3
Iterator<SimpleNumber> itr=hSet.iterator();
while(itr.hasNext())
System.out.println(itr.next());
}
}
예제 4
Hashset에서 중복이 있는지 없는지 비교하기위해
Object클래스의 hashCode()와 equals()를 오버라이딩 해야한다.
import java.util.Iterator;
import java.util.HashSet;
class SimpleNumber
{
int num;
public SimpleNumber(int n)
{
num=n;
}
public String toString()
{
return String.valueOf(num);
}
public int hashCode()
{
return num%3;
}
public boolean equals(Object obj)
{
SimpleNumber comp=(SimpleNumber)obj;
if(comp.num==num)
return true;
else
return false;
}
}
class HashSetEqualityTwo
{
public static void main(String[] args)
{
HashSet<SimpleNumber> hSet=new HashSet<SimpleNumber>();
hSet.add(new SimpleNumber(10));
hSet.add(new SimpleNumber(20));
hSet.add(new SimpleNumber(20));
System.out.println("저장된 데이터 수: "+hSet.size());
Iterator<SimpleNumber> itr=hSet.iterator();
while(itr.hasNext())
System.out.println(itr.next());
}
}
객체를 생성하기 전에 hashCode()를 먼저 호출해 값의 나머지를 반환하고, equals메서드를 호출해서 해당 나머지들의 값들과 비교해 반환한다.
요소를 정렬된 순서로 저장하고, 중복을 허용하지 않는다. >> 추가해도 내부적으로 정렬되어 저장된다.
예제 1
import java.util.Iterator;
import java.util.TreeSet;
class SortTreeSet
{
public static void main(String[] args)
{
TreeSet<Integer> sTree=new TreeSet<Integer>();
sTree.add(1);
sTree.add(2);
sTree.add(4);
sTree.add(3);
sTree.add(2);
System.out.println("저장된 데이터 수: "+sTree.size());//4
Iterator<Integer> itr=sTree.iterator();
while(itr.hasNext())
System.out.println(itr.next());
}
}
예제 2
import java.util.Iterator;
import java.util.TreeSet;
class Person implements Comparable<Person>
{
String name;
int age;
public Person(String name, int age)
{
this.name=name;
this.age=age;
}
public void showData()
{
System.out.printf("%s %d \n", name, age);
}
public int compareTo(Person p)//나이를 기준으로 오름차순
{
if(age>p.age)
return 1;
else if(age<p.age)
return -1;
else
return 0;
}//return age - p.age; 라고 표현하는게 더 좋다(같은 식)
}
class ComparablePerson
{
public static void main(String[] args)
{
TreeSet<Person> sTree=new TreeSet<Person>();
sTree.add(new Person("Lee", 24));
sTree.add(new Person("Hong", 29));
sTree.add(new Person("Choi", 21));
Iterator<Person> itr=sTree.iterator();
//iterator는 컬렉션내의 요소들을 차례로 접근 , 다음요소의 존재여부를 확인하고, 다음요소를 가져온
while(itr.hasNext())
itr.next().showData();
}
}
예제 2-1
문자의 사전적 순이 아닌 문자열 길이로 비교하고싶다면?
import java.util.TreeSet;
import java.util.Iterator;
class MyString implements Comparable<MyString>
{
String str;
public MyString(String str)
{
this.str=str;
}
public int getLength()
{
return str.length();
}
public int compareTo(MyString mStr)
{
if(getLength()>mStr.getLength())
return 1;
else if(getLength()<mStr.getLength())
return -1;
else
return 0;
/*
* return getLength()-mStr.getLength();
*/
}
public String toString()
{
return str;
}
}
class ComparableMyString
{
public static void main(String[] args)
{
/*
TreeSet<String> tSet=new TreeSet<String>();
tSet.add("Orange");
tSet.add("Apple");
tSet.add("Dog");
tSet.add("Individual");
Iterator<String> itr=tSet.iterator();
while(itr.hasNext())
System.out.println(itr.next());
*/
TreeSet<MyString> tSet=new TreeSet<MyString>();
tSet.add(new MyString("Orange"));
tSet.add(new MyString("Apple"));
tSet.add(new MyString("Dog"));
tSet.add(new MyString("Individual"));
Iterator<MyString> itr=tSet.iterator();
while(itr.hasNext())
System.out.println(itr.next());
}
}
예제3
Comparator인터페이스를 구현해 compare()메서드를 이용한다.
두개의 객체를 비교할 때 비교 규칙을 정의해서 비교할 수 있다.
import java.util.TreeSet;
import java.util.Iterator;
import java.util.Comparator;
class StrLenComparator implements Comparator<String>
{
public int compare(String str1, String str2)
{
if(str1.length()> str2.length())
return 1;
else if(str1.length()< str2.length())
return -1;
else
return 0;
/*
* return str1.length()-str2.length();
*/
}
}
class IntroComparator
{
public static void main(String[] args)
{
TreeSet<String> tSet=new TreeSet<String>(new StrLenComparator());//얘를 기준으로 정렬
tSet.add("Orange");
tSet.add("Apple");
tSet.add("Dog");
tSet.add("Individual");
Iterator<String> itr=tSet.iterator();
while(itr.hasNext())
System.out.println(itr.next());
}
}
StrLenComparator()클래스에서 정의한 비교 규칙을 가지고 비교하는 것이다.
"문자열을 사전편찬 순서가 아닌, 길이 순으로 정렬해서 TreeSet에 저장하고 싶다."
이를 위해서 "ComparableMyString.java" 소스에서 MyString이라는 String의 Wrapper 클래스를 정의했는데, TreeSet의 정렬 기준을 변경하기 위해서 MyString이라는 별도의 클래스를 정의한다는 것이, 사실 이치에는 맞지 않는다. 오히려 다음과 같이 요구할 수 있어야 정상 아니겠는가?
"야!TreeSet 인스턴스! 사전편찬 순서 말고, 길이 순으로 문자열을 졍렬해라!"
이러한 유형의 요구를 위해 정의된 것이 Comparator인터페이스이다. 이 인터페이스는 다음과 같이
정의되어 있다.
interface Comparator
{
int compare(T obj1, T obj2);
boolean equals(Object obj);
}
위의 인터페이스 중에서 equals 메소드는 신경 쓰지 않아도 된다. 이 인터페이스를 구현하는 모든 클래스는
Object 클래스를 상속하기 때문에, Object 클래스의 equals 메소드가 위의 equals 메소드를 구현하는 꼴이
되기 때문이다. 따라서 compare 메소드만 신경을 쓰면 된다. compare 메소드의 구현방법은 앞서 소개한
compareTo 메소드의 구현방법과 유사하다. obj1이 크면 양수를, obj2가 크면 음수를, obj1과 obj2가 같으면
0을 반환하면 된다. 물론 크고 작음에 대한 기준은 여러분이 결정할 몫이다.
package com.test.memo;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
class MenuChoiceException extends Exception {
private int choice;
MenuChoiceException(int choice) {
super("유효하지 않은 메뉴 값입니다.");
this.choice = choice;
}
public void showWrongMenu() {
System.out.println(choice + "에 해당하는 선택은 존재하지 않습니다.");
System.out.println("메뉴 선택을 처음부터 다시 진행합니다.");
}
}
class PhoneInfo {
private String name;
private String phone;
PhoneInfo(String name, String phone) {
this.name = name;
this.phone = phone;
}
public String getName() {
return name;
}
public void showPhoneInfo() {
System.out.println("이름 : " + name);
System.out.println("전화번호 : " + phone);
}
}
class PhoneUnivInfo extends PhoneInfo {
private String major;
private int year;
PhoneUnivInfo(String name, String phone, String major, int year) {
super(name, phone);
this.major = major;
this.year = year;
}
public void showPhoneInfo() {
super.showPhoneInfo();
System.out.println("전공 : " + major);
System.out.println("학년 : " + year);
}
}
class PhoneCompanyInfo extends PhoneInfo {
private String company;
PhoneCompanyInfo(String name, String phone, String company) {
super(name, phone);
this.company = company;
}
public void showPhoneInfo() {
super.showPhoneInfo();
System.out.println("회사 : " + company);
}
}
class PhoneBook {
private static PhoneBook pb;
private List<PhoneInfo> pInfo;
// private int cntOfPhone;
// private int sizePhoneInfo;
private PhoneBook(int sizePhoneInfo) {
pInfo = new ArrayList<>(sizePhoneInfo);// 배열로 이루어진 List
// cntOfPhone = 0;
// this.sizePhoneInfo = sizePhoneInfo;
}
public static PhoneBook getPhoneBookInst(int sizePhoneInfo) {
if (pb == null)
pb = new PhoneBook(sizePhoneInfo);
return pb;
}
public void inputPhoneInfo(PhoneInfo pInfo) {
int i = 0, j = 0;
// if (cntOfPhone >= sizePhoneInfo) {
// System.out.println("더 이상 저장할 수 없습니다.");
// return;
// }
int cnt = this.pInfo.size();
for (i = 0; i < cnt; i++) {
if (this.pInfo.get(i).getName().compareTo(pInfo.getName()) > 0) {
// for (j = cntOfPhone - 1; j >= i; j--) {
// pInfo.add(j + 1) = pInfo.get(j);
// }
break;
}
}
// this.pInfo[i] = pInfo;
// cntOfPhone++;
this.pInfo.add(i, pInfo);
}
public void searchPhoneInfo(String name) {
int result = search(name);
if (result != -1)
pInfo.get(result).showPhoneInfo();
else
System.out.println("찾으시는 데이터가 없습니다.");
}
public void deletePhoneInfo(int idx) {
// int i = 0;
// for (i = idx; i < cntOfPhone - 1; i++)
// pInfo[i] = pInfo[i + 1];
// pInfo[i] = null;
// cntOfPhone--;
pInfo.remove(idx);
System.out.println("삭제가 완료되었습니다.");
}
public int search(String name) {
for (int i = 0; i < pInfo.size(); i++) {
if (pInfo.get(i).getName().compareTo(name) == 0)
return i;
}
return -1;
}
public void showAllPhoneInfo() {
for (int i = 0; i < pInfo.size(); i++)
pInfo.get(i).showPhoneInfo();
}
}
interface PhoneMenuString {
int INPUT_PHONEINFO = 1;
int SEARCH_PHONEINFO = 2;
int DELETE_PHONEINFO = 3;
int SHOW_ALL_PHONEINFO = 4;
int PROGRAM_QUIT = 5;
int GENERAL = 1;
int UNIVERCITY = 2;
int COMPANY = 3;
int YES = 1;
int NO = 2;
}
class PhoneUI {
private static final int MAX_CNT = 100;
public static Scanner sc = new Scanner(System.in);
private static PhoneBook pb = PhoneBook.getPhoneBookInst(MAX_CNT);
private PhoneUI() {
}
public static void mainMenu() {
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.print("선택 : ");
}
public static void inputMenu() {
System.out.println("1. 일반, 2. 대학, 3. 회사");
}
public static void inputMenuChoice() throws MenuChoiceException {
int choice = 0;
choice = sc.nextInt();
sc.nextLine();
if (choice < PhoneMenuString.GENERAL || choice > PhoneMenuString.COMPANY)
throw new MenuChoiceException(choice);
switch (choice) {
case PhoneMenuString.GENERAL:
inputGeneralPhoneInfo();
break;
case PhoneMenuString.UNIVERCITY:
inputUniversityPhoneInfo();
break;
case PhoneMenuString.COMPANY:
inputCompanyPhoneInfo();
break;
}
}
public static void inputGeneralPhoneInfo() {
String name;
String phone;
System.out.println("데이터 입력을 시작합니다.");
System.out.print("이름 : ");
name = sc.nextLine();
System.out.print("전화번호 : ");
phone = sc.nextLine();
System.out.println("데이터 입력이 완료되었습니다.");
pb.inputPhoneInfo(new PhoneInfo(name, phone));
}
public static void inputUniversityPhoneInfo() {
String name;
String phone;
String major;
int year;
System.out.println("데이터 입력을 시작합니다.");
System.out.print("이름 : ");
name = sc.nextLine();
System.out.print("전화번호 : ");
phone = sc.nextLine();
System.out.print("전공 : ");
major = sc.nextLine();
System.out.print("학년 : ");
year = sc.nextInt();
sc.nextLine();
System.out.println("데이터 입력이 완료되었습니다.");
pb.inputPhoneInfo(new PhoneUnivInfo(name, phone, major, year));
}
public static void inputCompanyPhoneInfo() {
String name;
String phone;
String company;
System.out.println("데이터 입력을 시작합니다.");
System.out.print("이름 : ");
name = sc.nextLine();
System.out.print("전화번호 : ");
phone = sc.nextLine();
System.out.print("회사 : ");
company = sc.nextLine();
System.out.println("데이터 입력이 완료되었습니다.");
pb.inputPhoneInfo(new PhoneCompanyInfo(name, phone, company));
}
public static void searchPhoneInfo() {
String name;
System.out.println("데이터 검색을 시작합니다.");
System.out.println("검색하시고자 하는 이름을 입력하세요.");
name = sc.nextLine();
pb.searchPhoneInfo(name);
}
public static void deletePhoneInfo() {
String name;
int result = 0;
int answer = 0;
System.out.println("검색하시고자 하는 이름을 입력하세요.");
name = sc.nextLine();
result = pb.search(name);
if (result != -1) {
System.out.println("정말 삭제하시겠습니까? 1. Yes 2. No");
answer = sc.nextInt();
sc.nextLine();
switch (answer) {
case PhoneMenuString.YES:
pb.deletePhoneInfo(result);
break;
case PhoneMenuString.NO:
break;
default:
System.out.println("잘못 누르셨습니다.");
}
} else
System.out.println("삭제하시려는 데이터가 없습니다.");
}
public static void showAllPhoneInfo() {
pb.showAllPhoneInfo();
}
}
class Practice {
public static void main(String[] args) {
int choice = 0;
while (true) {
try {
PhoneUI.mainMenu();
choice = PhoneUI.sc.nextInt();
PhoneUI.sc.nextLine();
if (choice < PhoneMenuString.INPUT_PHONEINFO || choice > PhoneMenuString.PROGRAM_QUIT)
throw new MenuChoiceException(choice);
switch (choice) {
case PhoneMenuString.INPUT_PHONEINFO:
PhoneUI.inputMenu();
PhoneUI.inputMenuChoice();
break;
case PhoneMenuString.SEARCH_PHONEINFO:
PhoneUI.searchPhoneInfo();
break;
case PhoneMenuString.DELETE_PHONEINFO:
PhoneUI.deletePhoneInfo();
break;
case PhoneMenuString.SHOW_ALL_PHONEINFO:
PhoneUI.showAllPhoneInfo();
break;
case PhoneMenuString.PROGRAM_QUIT:
return;
}
} catch (MenuChoiceException e) {
System.out.println(e.getMessage());
e.showWrongMenu();
}
}
}
}
ArrayList 클래스는 저장되는 데이터의 수가 증가함에 따라서, 용량(데이터 저장 가능한 용량)이 자동으로 증가하는 클래스이다. >> 용량을 늘리기위해 기존 배열보다 큰 배열을 만들어 복사하는 원리기 때문에 용량이 자동으로 증가하는 것이다.
ArrayList의 메소드 중에는 저장용량의 설정을 위한 메소드가 존재한다. 따라서 여러분은 API 문서를 참조하여 이 메소드를 찾기 바란다. 그리고 다음 조건을 만족시키는 코드를 각각 구성해 보기 바란다.
Integer 인스턴스를 저장할 수 있는 ArrayList를 생성하고 저장용량을 500으로 늘린다.
ArrayList에 저장되어 있는 인스턴스 수의 두 배로 저장용량을 늘린다.
ArrayList<Integer> list = new ArrayList<Integer>();
list.ensureCapacity(500);
list.ensureCapacity(list.size()*2);
중복을 비교하기 위해서는 Object클래스의 hashCode()와 equals메서드를 오버라이딩 해야한다.
내가 푼 풀이
package com.test.memo;
import java.util.HashSet;
import java.util.Iterator;
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return name + "(" + age + "세)";
}
@Override
public int hashCode() {
return age % 3;
}
@Override
public boolean equals(Object obj) {
Person per = (Person) obj;
//if (per.age == age && per.name.equals(name)) { 이렇게 하는게 더 좋당..
if (per.age == age && per.name == name) {
return true;
}
return false;
}
}
public class Practice1 {
public static void main(String[] args) {
HashSet<Person> hSet = new HashSet<Person>();
hSet.add(new Person("이진호", 10));
hSet.add(new Person("이진호", 20));
hSet.add(new Person("김명호", 20));
hSet.add(new Person("김명호", 15));
hSet.add(new Person("이진호", 20));
hSet.add(new Person("김명호", 20));
System.out.println("저장된 데이터 수 : " + hSet.size());
Iterator<Person> itr = hSet.iterator();
while (itr.hasNext())
System.out.println(itr.next());
}
}
.equals()로 비교하는게 좋다.선생님 풀이
import java.util.HashSet;
import java.util.Iterator;
import java.util.Objects;
class Person
{
String name;
int age;
public Person(String name, int age)
{
this.name = name;
this.age = age;
}
public String toString()
{
return name + "(" + age + "세)";
}
// @Override
// public int hashCode()
// {
// return name.hashCode() + age%7;
// }
@Override
public int hashCode()
{
return Objects.hash(name, age); // 전달 인자 name, age 기반 해쉬 값 반환
}
@Override
public boolean equals(Object obj)
{
Person cmp = (Person)obj;
if(cmp.name.equals(name) && cmp.age == age)
return true;
else
return false;
}
}
class PersonMain
{
public static void main(String[] args)
{
HashSet<Person> hSet = new HashSet<Person>();
hSet.add(new Person("이진호", 10));
hSet.add(new Person("이진호", 20));
hSet.add(new Person("김명호", 20));
hSet.add(new Person("김명호", 15));
hSet.add(new Person("이진호", 20));
hSet.add(new Person("김명호", 20));
System.out.println("저장된 데이터 수 : " + hSet.size());
Iterator<Person> itr = hSet.iterator();
while(itr.hasNex
클래스를 정의할 때마다 이렇듯 hashCode 메소드를 정의하는 것은 번거로운 일이므로 아래와같이 입력하는게 좋다.
public static int hash(Object...values) >> java.util.Objects에 정의된 메소드, 전달된 인자 기반의 해쉬 값 반환package com.test.memo;
import java.util.Set;
import java.util.TreeSet;
public class Practice1 {
public static void main(String[] args) {
// TreeSet<Integer> set = new TreeSet<Integer>();
Set<Integer> set = new TreeSet<Integer>();
while (set.size() < 6) {
int num = (int) (Math.random() * 45) + 1;
set.add(num);
}
System.out.println(set);
}
}
TreeSet은 중복되지않고 정렬도 해주니까 편리하게 로또 프로그램을 구현할 수 있는것이다.
package com.test.memo;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
class Person implements Comparable<Person> {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void showData() {
System.out.printf("%s %d \n", name, age);
}
public int compareTo(Person p) {
if (age > p.age)
return 1;
else if (age < p.age)
return -1;
else
return 0;
}
public String getName() {
return name;
}
}
class Compare implements Comparator<Person> {
@Override
public int compare(Person o1, Person o2) {
return o1.getName().compareTo(o2.getName());//앞에가 크면 양수, 뒤에가 크면 음수
}
}
public class Practice1 {
public static void main(String[] args) {
TreeSet<Person> sTree = new TreeSet<Person>();
sTree.add(new Person("Lee", 24));
sTree.add(new Person("Hong", 29));
sTree.add(new Person("Choi", 21));
Iterator<Person> itr = sTree.iterator();
while (itr.hasNext())
itr.next().showData();
}
}
이를 익명 클래스
package com.test.memo;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
class Person implements Comparable<Person> {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void showData() {
System.out.printf("%s %d \n", name, age);
}
public int compareTo(Person p) {
if (age > p.age)
return 1;
else if (age < p.age)
return -1;
else
return 0;
}
public String getName() {
return name;
}
}
class Compare implements Comparator<Person> {
@Override
public int compare(Person o1, Person o2) {
return o1.getName().compareTo(o2.getName());
}
}
public class Practice1 {
public static void main(String[] args) {
TreeSet<Person> sTree = new TreeSet<Person>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getName().compareTo(o2.getName());
}
});
sTree.add(new Person("Lee", 24));
sTree.add(new Person("Hong", 29));
sTree.add(new Person("Choi", 21));
Iterator<Person> itr = sTree.iterator();
while (itr.hasNext())
itr.next().showData();
// Comparator com = new Comparator<Person>() {
//
// @Override
// public int compare(Person o1, Person o2) {
// return o1.getName().compareTo(o2.getName());
// }
//
// };
}
}
삭제가 제대로 안됌
검색이 제대로 안됌
내가 푼 풀이
public boolean searchPhoneInfoByName(String name)
{
PhoneInfo pInfo = null;
Iterator<PhoneInfo> itr = set.iterator();
boolean result = false;
while(itr.hasNext())
{
pInfo = itr.next();
if(pInfo.getName().equals(name))
{
pInfo.printCurrentState();
result = true;
}
}
return result;
}package com.test.memo;
import java.util.HashSet;
import java.util.Scanner;
class PhoneInfo {
private String name;
private String phone;
PhoneInfo(String name, String phone) {
this.name = name;
this.phone = phone;
}
String getName() {
return name;
}
void showPhoneInfo() {
System.out.println("이름: " + name);
System.out.println("번호: " + phone);
}
@Override
public int hashCode() {
return phone.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == this) //현재 객체와 매개변수로 전달된 객체가 동일한 객체인지 확인
return true;
if (!(obj instanceof PhoneInfo))
return false; // PhoneInfo클래스나, 그 하위 클래스의 인스턴스인지 확인
PhoneInfo other = (PhoneInfo) obj;
return phone.equals(other.phone);
}
}
class PhoneUnivInfo extends PhoneInfo {
private String major;
private int year;
PhoneUnivInfo(String name, String phone, String major, int year) {
super(name, phone);
this.major = major;
this.year = year;
}
void showPhoneInfo() {
super.showPhoneInfo();
System.out.println("전공: " + major);
System.out.println("학년: " + year);
}
}
class PhoneCompanyInfo extends PhoneInfo {
private String company;
PhoneCompanyInfo(String name, String phone, String company) {
super(name, phone);
this.company = company;
}
void showPhoneInfo() {
super.showPhoneInfo();
System.out.println("회사: " + company);
}
}
class PhoneBook {
private static PhoneBook pb;
private HashSet<PhoneInfo> pInfo;
//private Set<PhoneInfo>pInfo; 이 형식으로 해야 호환성이 좋다.
PhoneBook(int size) {
pInfo = new HashSet<>(size);
}
static PhoneBook getPhoneBookInst(int sizePhoneInfo) {//이 부분은 없어도 되는 부분인
if (pb == null) {
pb = new PhoneBook(sizePhoneInfo);
}
return pb;
}
void inputPhoneInfo(PhoneInfo pInfo) {
this.pInfo.add(pInfo);
}
PhoneInfo search(String name) {
for (PhoneInfo ph : pInfo) {
if (ph.getName().equals(name)) {
return ph;
}
}
return null;
}
void searchPhoneInfo(String name) {
PhoneInfo result = search(name);
if (result != null) {
result.showPhoneInfo();
}else {
System.out.println("찾으시는 데이터가 없습니다.");
}
}
void deletePhoneInfo(String name) {
PhoneInfo result = search(name);
if (result != null) {
pInfo.remove(result); // pInfo객체에서 result값을 삭제
System.out.println("삭제가 완료되었습니다.");
}
System.out.println("삭제하시려는 데이터가 없습니다.");
// Iterator<PhoneInfo> iter = pInfo.iterator();
// while (iter.hasNext()) {
// PhoneInfo ph = iter.next();
// if (ph.getName().equals(name)) {
// iter.remove();
// System.out.println("삭제가 완료되었습니다.");
// }
// }
}
void showAllPhoneInfo() {
for (PhoneInfo pp : pInfo) {
pp.showPhoneInfo();
}
}
}
interface PhoneMenuString {
int INPUT_PHONEINFO = 1;
int SEARCH_PHONEINFO = 2;
int DELETE_PHONEINFO = 3;
int SHOW_ALL_PHONEINFO = 4;
int PROGRAM_QUIT = 5;
int GENERAL = 1;
int UNIVERCITY = 2;
int COMPANY = 3;
int YES = 1;
int NO = 2;
}
class MenuChoiceException extends Exception {
private int choice;
MenuChoiceException(int choice) {
super("유효하지 않은 메뉴 값입니다.");
this.choice = choice;
}
public void showWrongMenu() {
System.out.println(choice + "에 해당하는 선택은 존재하지 않습니다.");
System.out.println("메뉴 선택을 처음부터 다시 진행합니다.");
}
}
class PhoneUI {
private static final int MAX_CNT = 100;
public static Scanner sc = new Scanner(System.in);
private static PhoneBook pb = PhoneBook.getPhoneBookInst(MAX_CNT);
private PhoneUI() {
}
public static void mainMenu() {
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.print("선택 : ");
}
public static void inputMenu() {
System.out.println("1. 일반, 2. 대학, 3. 회사");
}
public static void inputMenuChoice() throws MenuChoiceException {
int choice = 0;
choice = sc.nextInt();
sc.nextLine();
if (choice < PhoneMenuString.GENERAL || choice > PhoneMenuString.COMPANY)
throw new MenuChoiceException(choice);
switch (choice) {
case PhoneMenuString.GENERAL:
inputGeneralPhoneInfo();
break;
case PhoneMenuString.UNIVERCITY:
inputUniversityPhoneInfo();
break;
case PhoneMenuString.COMPANY:
inputCompanyPhoneInfo();
break;
}
}
public static void inputGeneralPhoneInfo() {
String name;
String phone;
System.out.println("데이터 입력을 시작합니다.");
System.out.print("이름 : ");
name = sc.nextLine();
System.out.print("전화번호 : ");
phone = sc.nextLine();
System.out.println("데이터 입력이 완료되었습니다.");
pb.inputPhoneInfo(new PhoneInfo(name, phone));
}
public static void inputUniversityPhoneInfo() {
String name;
String phone;
String major;
int year;
System.out.println("데이터 입력을 시작합니다.");
System.out.print("이름 : ");
name = sc.nextLine();
System.out.print("전화번호 : ");
phone = sc.nextLine();
System.out.print("전공 : ");
major = sc.nextLine();
System.out.print("학년 : ");
year = sc.nextInt();
sc.nextLine();
System.out.println("데이터 입력이 완료되었습니다.");
pb.inputPhoneInfo(new PhoneUnivInfo(name, phone, major, year));
}
public static void inputCompanyPhoneInfo() {
String name;
String phone;
String company;
System.out.println("데이터 입력을 시작합니다.");
System.out.print("이름 : ");
name = sc.nextLine();
System.out.print("전화번호 : ");
phone = sc.nextLine();
System.out.print("회사 : ");
company = sc.nextLine();
System.out.println("데이터 입력이 완료되었습니다.");
pb.inputPhoneInfo(new PhoneCompanyInfo(name, phone, company));
}
public static void searchPhoneInfo() {
String name;
System.out.println("데이터 검색을 시작합니다.");
System.out.println("검색하시고자 하는 이름을 입력하세요.");
name = sc.nextLine();
pb.searchPhoneInfo(name);
}
public static void deletePhoneInfo() {
String name;
int answer = 0;
System.out.println("검색하시고자 하는 이름을 입력하세요.");
name = sc.nextLine();
if (pb.search(name) != null) {
System.out.println("정말 삭제하시겠습니까? 1. Yes 2. No");
answer = sc.nextInt();
sc.nextLine();
switch (answer) {
case PhoneMenuString.YES:
pb.deletePhoneInfo(name);
break;
case PhoneMenuString.NO:
break;
default:
System.out.println("잘못 누르셨습니다.");
}
} else
System.out.println("삭제하시려는 데이터가 없습니다.");
}
public static void showAllPhoneInfo() {
pb.showAllPhoneInfo();
}
}
public class Practice1 {
public static void main(String[] args) {
int choice = 0;
while (true) {
try {
PhoneUI.mainMenu();
choice = PhoneUI.sc.nextInt();
PhoneUI.sc.nextLine();
if (choice < PhoneMenuString.INPUT_PHONEINFO || choice > PhoneMenuString.PROGRAM_QUIT)
throw new MenuChoiceException(choice);
switch (choice) {
case PhoneMenuString.INPUT_PHONEINFO:
PhoneUI.inputMenu();
PhoneUI.inputMenuChoice();
break;
case PhoneMenuString.SEARCH_PHONEINFO:
PhoneUI.searchPhoneInfo();
break;
case PhoneMenuString.DELETE_PHONEINFO:
PhoneUI.deletePhoneInfo();
break;
case PhoneMenuString.SHOW_ALL_PHONEINFO:
PhoneUI.showAllPhoneInfo();
break;
case PhoneMenuString.PROGRAM_QUIT:
return;
}
} catch (MenuChoiceException e) {
System.out.println(e.getMessage());
e.showWrongMenu();
}
}
}
}
선생님 - 아예 다른 형태
package com.test.memo;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
class MenuChoiceException extends Exception {
public MenuChoiceException(int menu) {
super(menu + "에 해당하는 선택은 존재하지 않습니다.\n" + "메뉴 선택을 처음부터 다시 진행합니다.");
}
}
class PhoneUnivInfo extends PhoneInfo {
private String major;
private int year;
public PhoneUnivInfo(String name, String phoneNumber, String major, int year) {
super(name, phoneNumber);
this.major = major;
this.year = year;
}
@Override
public void printCurrentState() {
super.printCurrentState();
System.out.println("전공 : " + major);
System.out.println("학년 : " + year);
}
}
class PhoneCompanyInfo extends PhoneInfo {
private String company;
public PhoneCompanyInfo(String name, String phoneNumber, String company) {
super(name, phoneNumber);
this.company = company;
}
@Override
public void printCurrentState() {
super.printCurrentState();
System.out.println("회사 : " + company);
}
}
class PhoneInfo {
private String name;
private String phoneNumber;
public PhoneInfo(String name, String phoneNumber) {
this.name = name;
this.phoneNumber = phoneNumber;
}
public String getName() {
return name;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void printCurrentState() {
System.out.println("이름 : " + name);
System.out.println("전화번호 : " + phoneNumber);
}
@Override
public boolean equals(Object obj) {
return phoneNumber.equals(((PhoneInfo) obj).phoneNumber);
}
@Override
public int hashCode() {
return phoneNumber.hashCode();
}
}
class PhoneBook {
private static PhoneBook pb;
private Set<PhoneInfo> set;
private PhoneBook() {
set = new HashSet<PhoneInfo>();
}
public static PhoneBook getPhoneBook() {
if (pb == null)
pb = new PhoneBook();
return pb;
}
public boolean insertPhoneInfo(PhoneInfo phoneInfo) {
return set.add(phoneInfo);
}
public boolean searchPhoneInfoByName(String name) {
PhoneInfo pInfo = null;
Iterator<PhoneInfo> itr = set.iterator();
boolean result = false;
while (itr.hasNext()) {
pInfo = itr.next();
if (pInfo.getName().equals(name)) {
pInfo.printCurrentState();
result = true;
}
}
return result;
}
public boolean deletePhoneInfoByPhoneNumber(String phoneNumber) {
PhoneInfo pInfo = null;
Iterator<PhoneInfo> itr = set.iterator();
while (itr.hasNext()) {
pInfo = itr.next();
if (pInfo.getPhoneNumber().equals(phoneNumber)) {
itr.remove();
return true;
}
}
return false;
}
public void printAllPhoneInfo() {
Iterator<PhoneInfo> itr = set.iterator();
while (itr.hasNext()) {
itr.next().printCurrentState();
}
// for(PhoneInfo info: set)
// info.printCurrentState();
}
}
class PhoneBookUI {
private PhoneBook pb;
public static Scanner sc = new Scanner(System.in);
public PhoneBookUI() {
this.pb = PhoneBook.getPhoneBook();
}
public void printMenu() {
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("선택 : ");
}
public void inputMenu() {
System.out.println("데이터 입력을 시작합니다.");
System.out.println("1. 일반, 2. 대학, 3. 회사");
System.out.print("선택 >>");
}
public void inputPhoneInfo(int menu) {
String name, phoneNumber, major, company;
int year = 0;
boolean result;
PhoneInfo phoneInfo = null;
System.out.println("데이터 입력을 시작합니다.");
System.out.println("이름 : ");
name = sc.nextLine();
System.out.println("전화번호 : ");
phoneNumber = sc.nextLine();
if (menu == 1) // 추가
phoneInfo = new PhoneInfo(name, phoneNumber);
else if (menu == 2) {
System.out.println("전공 : ");
major = sc.nextLine();
System.out.println("학년 : ");
year = sc.nextInt();
phoneInfo = new PhoneUnivInfo(name, phoneNumber, major, year);
} else if (menu == 3) {
System.out.println("회사 : ");
company = sc.nextLine();
phoneInfo = new PhoneCompanyInfo(name, phoneNumber, company);
}
result = pb.insertPhoneInfo(phoneInfo);
if (result == false)
System.out.println("이미 등록된 데이터 입니다.");
else
System.out.println("데이터 입력이 완료되었습니다.");
}
public void searchPhoneInfoByName() {
String name;
System.out.println("검색하시고자 하는 이름을 입력해 주세요.");
name = sc.nextLine();
System.out.println("사용자 검색을 시작합니다.");
if (!pb.searchPhoneInfoByName(name))
System.out.println("찾으시는 사용자가 없습니다.");
}
public void deletePhoneInfoByPhoneNumber() {
String phoneNumber;
System.out.println("삭제하시고자 하는 전화번호를 입력해 주세요.");
phoneNumber = sc.nextLine();
boolean result = pb.deletePhoneInfoByPhoneNumber(phoneNumber);
if (result)
System.out.println("삭제가 완료되었습니다.");
else
System.out.println("삭제하시고자 하는 전화번호 정보가 없습니다.");
}
public void printAllPhoneInfo() {
System.out.println("모든 사용자 정보를 출력합니다.");
pb.printAllPhoneInfo();
}
public void quitProgram() {
System.out.println("프로그램을 종료합니다.");
sc.close();
}
}
public interface Menu {
int INSERT_PHONE_INFO = 1; // 상수로 준 이유 : 가독성을 높이기 위해서
int SEARCH_PHONE_INFO = 2;
int DELETE_PHONE_INFO = 3;
int SHOW_ALL_PHONE_INFO = 4;
int QUIT_PHONE_INFO = 5;
}
class Practice {
public static void main(String[] args) {
int menu = 0;
PhoneBookUI pbUI = new PhoneBookUI();
Scanner sc = PhoneBookUI.sc;
while (true) {
pbUI.printMenu();
try {
menu = sc.nextInt();
sc.nextLine();
if (menu < Menu.INSERT_PHONE_INFO || menu > Menu.QUIT_PHONE_INFO) {
throw new MenuChoiceException(menu);
}
switch (menu) {
case Menu.INSERT_PHONE_INFO:
pbUI.inputMenu();
menu = sc.nextInt();
sc.nextLine();
if (menu < 1 || menu > 3) {
throw new MenuChoiceException(menu);
}
pbUI.inputPhoneInfo(menu);
break;
case Menu.SEARCH_PHONE_INFO:
pbUI.searchPhoneInfoByName();
break;
case Menu.DELETE_PHONE_INFO:
pbUI.deletePhoneInfoByPhoneNumber();
break;
case Menu.SHOW_ALL_PHONE_INFO:
pbUI.printAllPhoneInfo();
break;
case Menu.QUIT_PHONE_INFO:
pbUI.quitProgram();
return;
}
} catch (MenuChoiceException e) {
System.out.println(e.getMessage());
}
}
}
}
Comparable사용
선생님
package com.test.memo;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;
class MenuChoiceException extends Exception {
public MenuChoiceException(int menu) {
super(menu + "에 해당하는 선택은 존재하지 않습니다.\n" + "메뉴 선택을 처음부터 다시 진행합니다.");
}
}
class PhoneUnivInfo extends PhoneInfo {
private String major;
private int year;
public PhoneUnivInfo(String name, String phoneNumber, String major, int year) {
super(name, phoneNumber);
this.major = major;
this.year = year;
}
@Override
public void printCurrentState() {
super.printCurrentState();
System.out.println("전공 : " + major);
System.out.println("학년 : " + year);
}
}
class PhoneCompanyInfo extends PhoneInfo {
private String company;
public PhoneCompanyInfo(String name, String phoneNumber, String company) {
super(name, phoneNumber);
this.company = company;
}
@Override
public void printCurrentState() {
super.printCurrentState();
System.out.println("회사 : " + company);
}
}
class PhoneInfo implements Comparable<PhoneInfo> {
private String name;
private String phoneNumber;
public PhoneInfo(String name, String phoneNumber) {
this.name = name;
this.phoneNumber = phoneNumber;
}
public String getName() {
return name;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void printCurrentState() {
System.out.println("이름 : " + name);
System.out.println("전화번호 : " + phoneNumber);
}
@Override
public int compareTo(PhoneInfo pInfo) {
return name.compareTo(pInfo.name);
}
}
class PhoneBook {
private static PhoneBook pb;
private Set<PhoneInfo> set;
private PhoneBook() {
set = new TreeSet<PhoneInfo>();
}
public static PhoneBook getPhoneBook() {
if (pb == null)
pb = new PhoneBook();
return pb;
}
public boolean insertPhoneInfo(PhoneInfo phoneInfo) {
return set.add(phoneInfo);
}
public boolean searchPhoneInfoByName(String name) {
PhoneInfo pInfo = null;
Iterator<PhoneInfo> itr = set.iterator();
boolean result = false;
while (itr.hasNext()) {
pInfo = itr.next();
if (pInfo.getName().equals(name)) {
pInfo.printCurrentState();
result = true;
}
}
return result;
}
public boolean deletePhoneInfoByPhoneNumber(String phoneNumber) {
PhoneInfo pInfo = null;
Iterator<PhoneInfo> itr = set.iterator();
while (itr.hasNext()) {
pInfo = itr.next();
if (pInfo.getPhoneNumber().equals(phoneNumber)) {
itr.remove();
return true;
}
}
return false;
}
public void printAllPhoneInfo() {
Iterator<PhoneInfo> itr = set.iterator();
while (itr.hasNext()) {
itr.next().printCurrentState();
}
// for(PhoneInfo info: set)
// info.printCurrentState();
}
}
class PhoneBookUI {
private PhoneBook pb;
public static Scanner sc = new Scanner(System.in);
public PhoneBookUI() {
this.pb = PhoneBook.getPhoneBook();
}
public void printMenu() {
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("선택 : ");
}
public void inputMenu() {
System.out.println("데이터 입력을 시작합니다.");
System.out.println("1. 일반, 2. 대학, 3. 회사");
System.out.print("선택 >>");
}
public void inputPhoneInfo(int menu) {
String name, phoneNumber, major, company;
int year = 0;
boolean result;
PhoneInfo phoneInfo = null;
System.out.println("데이터 입력을 시작합니다.");
System.out.println("이름 : ");
name = sc.nextLine();
System.out.println("전화번호 : ");
phoneNumber = sc.nextLine();
if (menu == 1)
phoneInfo = new PhoneInfo(name, phoneNumber);
else if (menu == 2) {
System.out.println("전공 : ");
major = sc.nextLine();
System.out.println("학년 : ");
year = sc.nextInt();
phoneInfo = new PhoneUnivInfo(name, phoneNumber, major, year);
} else if (menu == 3) {
System.out.println("회사 : ");
company = sc.nextLine();
phoneInfo = new PhoneCompanyInfo(name, phoneNumber, company);
}
result = pb.insertPhoneInfo(phoneInfo);
if (result == false)
System.out.println("이미 등록된 데이터 입니다.");
else
System.out.println("데이터 입력이 완료되었습니다.");
}
public void searchPhoneInfoByName() {
String name;
System.out.println("검색하시고자 하는 이름을 입력해 주세요.");
name = sc.nextLine();
System.out.println("사용자 검색을 시작합니다.");
if (!pb.searchPhoneInfoByName(name))
System.out.println("찾으시는 사용자가 없습니다.");
}
public void deletePhoneInfoByPhoneNumber() {
String phoneNumber;
System.out.println("삭제하시고자 하는 전화번호를 입력해 주세요.");
phoneNumber = sc.nextLine();
boolean result = pb.deletePhoneInfoByPhoneNumber(phoneNumber);
if (result)
System.out.println("삭제가 완료되었습니다.");
else
System.out.println("삭제하시고자 하는 전화번호 정보가 없습니다.");
}
public void printAllPhoneInfo() {
System.out.println("모든 사용자 정보를 출력합니다.");
pb.printAllPhoneInfo();
}
public void quitProgram() {
System.out.println("프로그램을 종료합니다.");
sc.close();
}
}
interface Menu {
int INSERT_PHONE_INFO = 1; // 상수로 준 이유 : 가독성을 높이기 위해서
int SEARCH_PHONE_INFO = 2;
int DELETE_PHONE_INFO = 3;
int SHOW_ALL_PHONE_INFO = 4;
int QUIT_PHONE_INFO = 5;
}
class Practice {
public static void main(String[] args) {
int menu = 0;
PhoneBookUI pbUI = new PhoneBookUI();
Scanner sc = PhoneBookUI.sc;
while (true) {
pbUI.printMenu();
try {
menu = sc.nextInt();
sc.nextLine();
if (menu < Menu.INSERT_PHONE_INFO || menu > Menu.QUIT_PHONE_INFO) {
throw new MenuChoiceException(menu);
}
switch (menu) {
case Menu.INSERT_PHONE_INFO:
pbUI.inputMenu();
menu = sc.nextInt();
sc.nextLine();
if (menu < 1 || menu > 3) {
throw new MenuChoiceException(menu);
}
pbUI.inputPhoneInfo(menu);
break;
case Menu.SEARCH_PHONE_INFO:
pbUI.searchPhoneInfoByName();
break;
case Menu.DELETE_PHONE_INFO:
pbUI.deletePhoneInfoByPhoneNumber();
break;
case Menu.SHOW_ALL_PHONE_INFO:
pbUI.printAllPhoneInfo();
break;
case Menu.QUIT_PHONE_INFO:
pbUI.quitProgram();
return;
}
} catch (MenuChoiceException e) {
System.out.println(e.getMessage());
}
}
}
}