[JAVA.13] 제네릭(generic)과 컬렉션 프레임워크(Collection)⚡

Kama_Code·2023년 7월 23일
1

JAVA

목록 보기
18/20
post-thumbnail
post-custom-banner

자바(JAVA) 후반부의 첫 파트이다.
제네릭.. 컬렉션 프레임워크.. 이름이 굉장히 거창하고 멋있게 느껴진다.
이런 것들이 탄생한 이유는 다 개발자들이 편하라고 나온 방식이니
이것저것 많다고 불평말고 반드시 꼭 학습하여 넘어가자!

<Step.1> 제네릭(generic)

  • 제네릭(generic)이란?
    ㄴ데이터의 타입(data type)을 일반화한다(generalize)는 것을 의미한다
    ㄴ클래스나 메소드에서 사용할 내부 데이터 타입을 컴파일 시에 미리 지정하는 방법이다.

※ 제네릭이 없다면 겪는 문제점

  • 이름만 다르고 구조는 같은 코드일시 코드가 중복된다는 단점
    그렇게 코드가 복잡해지면 개발시 어려움을 헤깔리는 불편함을 겪게 된다.

  • OrangeBox 클래스는 Orange 객체만 저장할 수 있다.
  • 단순하기에 자료형에 안전한 구조를 가지고 있다.
  • 단, 다른 객체를 저장하기 위해서는 클래스를 추가해야 하는 번거로움 발생.
class OrangeBox {
	Orange item; // 오렌지 아이템만 들어갈 수 있음
	public void store(Orange item) {
		this.item = item;
	}
	public Orange pullOut() {
		return item;
	}
}

  • FruitBox는 모든 객체를 저장할 수 있는 클래스이다.
  • 구현의 편의성이 보장된다.
  • 단, 자료형에는 안전하지 못하다는 단점이 있다.
class FruitBox{
	Object item;
	public void store(Object item) {
		this.item = item;
	}
	public Object pullOut() {
		return item;
	}
}

▣ 제네릭 클래스의 설계 및 인스턴스 생성

1. 타입 매개변수 (Type Parameter): Camp<T>에서 T를 말한다.
2. 타입 인자 (Type Argument): Camp<Npc>에서 Npc를 말한다.
3. 매개변수화 타입 (Parameterized Type): Camp<Npc>를 말한다.  

▣ 다중 매개변수 기반 제네릭 클래스의 정의

▣ 타입 변수의 제한
제네릭은 'T'와 같은 타입 변수(type variable)를 사용하여 타입을 제한합니다.
이때 extends 상속 키워드를 사용하면 타입 변수에 특정 타입만을
사용하도록 제한할 수 있다

class AnimalList<T extends LandAnimal> { ... }

타입 변수에 제한을 걸어 놓으면 클래스 내부에서 사용된 모든 타입 변수에 제한이 걸린다.

이때 인터페이스를 구현할 경우에도 implements 키워드가 아닌 extends 키워드를
사용해야만 한다.


  • 클래스와 인터페이스를 동시에 상속받고 구현해야 한다면 엠퍼센트(&) 기호를 사용한다.
class AnimalList<T extends LandAnimal & WarmBlood> { ... }

<Step.2> 제네릭 메소드(generic method)

  • 제네릭 메소드란?
    ㄴ메소드의 선언부에 타입 변수를 사용한 메소드를 의미한다.
    ㄴ이때 타입 변수의 선언은 메소드 선언부에서 반환 타입 바로 앞에 위치한다.

클래스 전부가 아닌 메소드 하나에 대해 제네릭으로 정의
제네릭 메서드의 T는 메서드 호출 시점에 결정한다.

public static <T> void sort( ... ) { ... }

<Step.3> 와일드카드

와일드카드(wild card)란 이름에 제한을 두지 않음을 표현하는 데 사용되는 기호를 의미
제네릭에서는 물음표(?) 기호를 사용하여 이러한 와일드카드를 사용할 수 있다.

<?>           // 타입 변수에 모든 타입을 사용할 수 있음.

<? extends T> // T 타입과 T 타입을 상속받는 자손 클래스 타입만을 사용할 수 있음.

<? super T>   // T 타입과 T 타입이 상속받은 조상 클래스 타입만을 사용할 수 있음.

<Step.4> 컬렉션 프레임워크

  • 다수의 객체 저장과 참조를 위해 정의된 클래스들의 집합
  • 별도의 구현과 이해없이 자료구조와 알고리즘을 적용할 수 있다.
    즉, 데이터를 저장하는 자료 구조와 데이터를 처리하는 알고리즘을 구조화하여
    클래스로 구현해 놓은 것이다.

컬렉션 프레임워크는 자바의 인터페이스(interface)를 사용하여 구현된다.


  • 배열
    배열은 크기가 고정되어 있어 데이터를 추가하거나 삭제할 수 없다.
    ㄴ리스트와 반대이다.

  • 리스트
    떨어진 곳에 존재하는 데이터를 화살표로 묶어서 관리하는 자료구조
    데이터가 추가되거나 삭제될 때 연결하는 정보만 바꾸면 쉽게 추가, 삭제가 된다.
    ㄴ배열과 반대이다.

  • 스택
    입력(Push) 꺼낸다(Pop) -> 마지막에 입력된 것이 먼저 출력된다.


  • 먼저 들어간 것을 먼저 출력한다 -> 슈퍼마켓 줄을 선 사람들을 생각하면 된다.


  • ㄴ 덱(Deque)은 어떤 쪽으로 입력하고 어떤 쪽으로 출력하느냐에 따라
    ㄴ 스택(Stack)으로 사용할 수도 있고 큐(queue)로 사용할 수 있다
    ㄴ 한쪽으로만 입력 가능하도록 설정한 것을 스크롤(scroll)
    ㄴ 한쪽으로만 출력 가능하도록 설정한 덱은 셀프(shelf)
public static void main(String[] args)
	{
		// 둘 다 사용 가능
		Deque<String> deq = new ArrayDeque<>();
//		Deque<String> deq = new LinkedList<>();
		
		// 앞으로 넣고 : 
		deq.offerFirst("A");
		deq.offerFirst("B");
		deq.offerFirst("C");
		
		// 리스트 확인
		for(String s : deq)
			System.out.print(s.toString() + '\t');
		System.out.println();
		
		// 앞으로 꺼내기
		System.out.println(deq.pollFirst());
		System.out.println(deq.pollFirst());
		System.out.println(deq.pollFirst());
		
		System.out.println("---------------------"); // 이것이 스택
		
		// 뒤로 넣고
		deq.offerLast("A");
		deq.offerLast("B");
		deq.offerLast("C");
		
		// 리스트 확인
		for(String s : deq)
			System.out.print(s.toString() + '\t');
		System.out.println(); // A먼저 넣고 B C 순서대로 넣음
		
		// 뒤에서 꺼내기
		System.out.println(deq.pollLast());
		System.out.println(deq.pollLast());
		System.out.println(deq.pollLast());
		
		System.out.println("---------------------"); // 이것이 스택
		
		// 뒤로 넣고
		deq.offerLast("A");
		deq.offerLast("B");
		deq.offerLast("C");
		
		// 리스트 확인
		for(String s : deq)
			System.out.println(s.toString() + '\t');
		System.out.println();
		
		// 앞으로 꺼내기
		System.out.println(deq.pollFirst());
		System.out.println(deq.pollFirst());
		System.out.println(deq.pollFirst());
		
		System.out.println("---------------------"); // 이것이 큐
	}
}

  • 트리

▣ 컬렉션의 인터페이스 구조

▣ Arrays.AsList()

  • 일반적인 배열을 List컬렉션으로 변환해준다.
  • 컬렉션의 모든 메소드를 사용할 수 있어 편리하다
  • 단, 값만 참조할 수 있다.
  • 변경을 하고 싶다면 객체를 복사후 진행한다.
▣ List<E> 인터페이스: 순서가 있는 데이터의 집합(중복 허용)
배열과 같은 특성을 기반으로 객체를 저장
중복저장이 허용되며 저장 순서가 유지된다.
  + ArrayList<E> : 배열 기반 자료구조, 배열을 이용하여 인스턴스 저장  
  + LinkedList<E> : 리스트기반 자료구조, 리스트를 구성하여 인스턴스 저장
  
1. ArrayList<E> : 추가 및 수정시 새로 배열을 만들고 전체를 옮겨야 한다.
- 데이터 중복 저장을 허용한다
- 데이터의 저장 순서를 보장한다
- 데이터 접근시 get() 혹은 iterator()를 이용한다
- Array라는 이름처럼 '배열'의 특성을 가지고 있어서 인덱스를 통한 저장 및 접근이 가능
- 탐색 또는 정렬을 자주 사용하는 경우에 많이 사용
- 추가가 가능하나 시간이 오래걸린다. 이사하여 처음부터 다 쓰여지기 때문
public static void main(String[] args)
{
	ArrayList<Integer> list=new ArrayList<Integer>();
	list.add(new Integer(11));
	list.add(new Integer(22));
	list.add(new Integer(33));

	for(int i=0; i<list.size(); i++)
		System.out.println(list.get(i));
	list.remove(0);	
}

2. LinkedList<E> : 객체가 추가될 때 사이에 끼워넣기만 하면 된다.
- 데이터의 중복 저장을 허용한다
- 데이터의 저장 순서를 보장한다
- 데이터 접근시 get() 혹은 iterator()를 이용한다
- linked라는 연결기반 자료 구조, 앞의 자료 구조에서 본 리스트를 구성하여 객체를 저장
- arraylist()보다 데이터 추가 및 삭제가 용이하다
public static void main(String[] args)
{
	ArrayList list = new ArrayList();
	list.add("소녀시대");
	list.add("빅뱅");

	Iterator itr = list.iterator();
	while(itr.hasNext()) {
		System.out.print(itr.next() +" ");
	}
}
▣ List<E> 관련 메소드
▣ 저장된 인스턴스의 순차적 접근 방법 1 : 향상된 for 문 ▣ 저장된 인스턴스의 순차적 접근 방법 2 : Iterator(반복자)를 통한 순차접근
  • 이터레이터, 반복자라고 한다.
    컬렉션에 저장된 인스턴스를 순차적으로 접근할 수 있다.
    컬렉션의 종류에 상관없이 동일한 형태의 데이터 참조방식을 유지할 수 있다.
    컬렉션을 변경하더라도 별도의 참조방식을 확인할 필요가 없다.
public static void main(String[] args)
{
	ArrayList list = new ArrayList();
	list.add("소녀시대");
	list.add("빅뱅");

	Iterator itr = list.iterator();
	while(itr.hasNext()) {
		System.out.print(itr.next() +" ");
	}
}
▣ 배열 기반 리스트를 연결 기반 리스트로 바꾸기

▣ 기본 자료형 데이터의 저장과 참조
ㄴ오토 박싱과 오토 언박싱 덕분에 컬렉션 인스턴스에 기본 자료형의 값도 저장 가능하다.

▣ Set<E> 인터페이스: 순서가 없는 데이터의 집합(중복 불가)
집합과 같은 특성을 기반으로 객체를 저장한다
중복저장이 허용되지 않으며, 저장 순서가 유지되지 않는다.
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());
}
1. HashSet<E> : Set계열의 컬렉션 
- 분류를 하면서 속도가 빨라진다.
- Set 계열의 인터페이스를 구현한 컬렉션
- 객체가 순서없이 저장된다
- 객체의 중복저장을 허용하지 않는다.
- List가 배열의 특성을 가진다면, Set은 집합의 특성을 가진다

▣ 특성을 이용해서 중복제거 하는 방법
1. 크기의 변경이 안되는 배열(Arrays.asList)
2. set 특성을 이용한 HashSet으로 바꿔준다
( 중복을 쓸 수 없는 특성으로 중복 자동 삭제 )
3. 다시 list로 변환해준다.
4. 중복이 제거되어 있다.

2. TreeSet<T> : 트리라는 자료구조를 기반으로 한 Set계열의 컬렉션
- 가장 많이 쓰인다.
- Set의 기본적인 특성은 동일하다.
- 중복은 허용되지 않는다
- 단 객체가 정렬되어 저장된다. 정렬의 기준은 개발자가 직접 정의
▣ Comparator<T> 인터페이스 기반으로 TreeSet<E>의 정렬 기준 제시하기
public interface Comparator<T>int compare(T o1, T o2) 의 구현을 통해 정렬 기준을 결정할 수 있다.
# x1이 x2보다 크면 양의 정수
# x1이 x2보다 작으면 음의 정수
# x1이 x2와 일치하면 0CompareTo() 메소드를 오버라이딩 하여 정렬의 기준을 결정한다. 
+ 인자로 전달된 obj가 작다면 양의정수반환
+ 인자로 전달된 obj가 크다면 음의정수반환
+ 인자로 전달된 obj가 같다면 0반환
@Override
public int compareTo(MyString paramStr) {	
	if(getLength() > paramStr.getLength())
		return 1;
	else if(getLength() < paramStr.getLength())
		return -1;
	else
		return 0;
}

▣ Map<K,V> 계열의 컬렉션: 키와 값 한쌍으로 이루어진 데이터의 집합

  • Map<K,V> 인터페이스를 구현하는 컬렉션은 key-value 방식으로
    데이터를 저장한다.
  • key는 value를 찾기 위한 식별자를 의미한다.
  • value는 실제 저장될 데이터를 의미한다.
  • 입력된 순서를 보장하지 않는다.

★★★ 객체의 key 값은 유일하며 value 값은 중복될 수 있다.

public static void main(String[] args)
{
	HashMap<Integer, String> hMap
		=new HashMap<Integer, String>();
	hMap.put(new Integer(3), "나삼번");
	System.out.println("6학년 3반 3번 학생: "+hMap.get(3));
}
  • HashMap 클래스 : 내부적으로 해쉬 알고리즘에 의해 구현되어 있다.

  • TreeMap 클래스 : TreeSet 과 마찬가지로 이진 탐색 트리로 구현되어 있다.
    key 값으로 정렬하므로 key 값에 해당하는 클래스에 Comparable 이나 Comparator
    인터페이스가 구현되어 있어야 한다.

  • HashMap<K, V> 클래스는 Iterable 인터페이스를 구현하지 않으니
    for-each문을 통해서, 혹은 ‘반복자’를 얻어서 순차적 접근을 진행할 수 없다.

  • TreeMap<K, V> 클래스는 Iterable 인터페이스를 구현하지 않았지만, Tree 자료구조의 특성상 반복자가 정렬된 순서대로 key들에 접근을 하고 있다.


  • 정렬
클래스 생성시 정렬의 기준을 만들어주기 위해서 Comparable<T> 인터페이스를 구현해야 한다
//정렬 순서 정하기
class Student2 implements Comparable<Student2>
{
	private String name;
	private int age;
	
	// 소스를 통해 만듬
	public Student2(String name, int age)
	{
		this.name = name;
		this.age = age;
	}
	
	@Override // 생략 Object 클래스에서 제공하는 메소드를 오버라이딩
	public String toString()
	{
		return name + ":" + age;
	}
	/*
	TreeSet<T>객체를 저장할 때 정렬을 위해 compareTo()메소드를 오버라이딩한다
	정렬의 기준은 단순하게 가나다와 같은 순서가 될 수도 있다. 문자열의 길이에 따른 될 수도 있다
	 */
	
	@Override // 생략
	public int compareTo(Student2 p) // 이것을 통해 원하는 정렬을 해줄 수 있다.
	{
//		return this.age - p.age; //나이 오름차순 정렬
//		return p.age - this.age; //나이 내림차순 정렬
		return this.name.compareTo(p.name); //이름 가나다 순 정렬
//		return p.name.compareTo(this.name); //이름 역순으로 정렬
	}
}

public class Ex09_Comparable
{
	public static void main(String[] args)
	{
		Set<Student2> tree = new TreeSet<>();
		tree.add(new Student2("홍길동", 30));
		tree.add(new Student2("전우치", 40));
		tree.add(new Student2("손오공", 20));
		
		//Set 컬렉션이므로 중복저장은 허용되지 않는다.
		tree.add(new Student2("손오공", 20)); // 안나옴
		
		for(Student2 s : tree)
			System.out.println(s);
	}
}
Comparator<T>:
  기본 정렬 조건이 있다고 하더라도 새로운 정렬 조건을 주고 싶을 때 사용
class MyStringComparator implements Comparator<String>
{
	@Override
	public int compare(String s1, String s2)
	{
		//길이가 동일한 데이터는 추가되지 않는다. <= 이 조건을 적용
		//길이가 같으면 0으로 출력되어 빠진다
		return s1.length() - s2.length();
	}
}
public class Ex10_Comparator
{

	public static void main(String[] args)
	{
//		Set<String> tree = new TreeSet<>();
		Set<String> tree = new TreeSet<>(new MyStringComparator());
		tree.add("홍길동");
		tree.add("전우치");
		tree.add("전우치");
		tree.add("멀린");
		tree.add("해리포터");
		
		for(String s : tree)
			System.out.println(s.toString() + '\t');
		
		System.out.println();
	}

}

List를 구현한 컬렉션 클래스들은 저장된 인스턴스를 정렬된 상태로 유지하지 않는다.
대신에 정렬을 해야 한다면 다음 메소드를 사용할 수 있다

public static <T extends Comparable<T>> void sort(List<T> list)
→ Collections 클래스에 정의되어 있는 제네릭 메소드
→ 인자로 List<T>의 인스턴스는 모두 전달 가능
→ 단, T는 Comparable<T> 인터페이스를 구현한 상태이어야 한다.

<Step.5> 발전을 위한 문제풀이 (kama_code 출제)

Q. 컬렉션을 이용한 로또번호 생성기를 구현하시오.
1. 배열을 이용한 버전과 동일한 조건으로 제작하되 Set 컬렉션을 이용한다.
2. Set 계열의 컬렉션은 중복이 자동으로 제거된다.
3. 하지만 순서를 보장하지 않으므로 TreeSet을 사용하면 된다.

Q. 사용자로부터 이름을 입력받아 그 이름으로 검색해서 인덱스 위치(indexOf사용)를 알아내서
해당 인덱스로 그 객체를 삭제하고 삭제된 객체의 정보(이름,나이,학번)를 출력하여라.
1.검색할 이름을 입력받음
2.확장for문으로 컬렉션 전체를 접근
3.검색결과 유/무에 따라
ㄴ검색결과 있을때…검색된 데이터 삭제
ㄴ검색결과 없을때...검색결과가 없다고 출력
4.전체정보 출력

Q, 컬렉션 set에 저장된 객체를 이름으로 검색하자.
[필수 추가]
HashSet set = new HashSet();
QuHashSet hero1 = new QuHashSet("토니스타크", "아이언맨", "Mark-48 수트");
QuHashSet hero2 = new QuHashSet("스티브로져스", "캡틴아메리카", "비브라늄 방패");
QuHashSet hero3 = new QuHashSet("브루스배너", "헐크", "강한피부&점프");
QuHashSet hero4 = new QuHashSet("토니스타크", "아이언맨", "Mark-48 수트");
set.add(hero1);
set.add(hero2);
set.add(hero3);
set.add(hero4);
해당 정보가 있으면 해당 어벤져스의 정보를 출력하고 없으면 "해당 영웅은 없어요ㅜㅜ" 라는 메시지 출력해야 한다.
검색 부분은 Iterator를 통해 구현하도록 한다.
set계열의 컬렉션은 기본적으로 중복저장을 허용하지 않는다.
그러나 아래와 같이 새롭게 정의한 클래스에 대해서는 hashCode() 메소드를 적
절히 오버라이딩 처리해야 중복을 제거할 수 있다.
메소드 오버라이딩을 통해 hero4 는 입력되지 않도록 Avengers 클래스를 업데이트 하시오.
▷ 실행결과:
[최초 전체 정보출력]
Avengers [본명=스티브로져스, 닉네임=캡틴아메리카, 능력=비브라늄 방패]
Avengers [본명=브루스배너, 닉네임=헐크, 능력=강한피부&점프]
Avengers [본명=토니스타크, 닉네임=아이언맨, 능력=Mark-48 수트]
검색할 이름을 입력하세욤:브루스배너
Avengers [본명=브루스배너, 닉네임=헐크, 능력=강한피부&점프]
요청하신 정보를 찾았습니다

Q. "그만"이 입력될 때까지 입력키, 아이디, 비밀번호 정보를 가진 ID 클래스를 작성하고
입력키를 '키'로 하는 HashMap<String, ID> 컬렉션을 만들어서 사용자로부터 입력받아
입력키: 네이버, 다음, 쿠팡, 티몬, 애플로 계정을 저장하시오.
그리고 입력키로 아이디, 비밀번호를 출력하는 프로그램을 만드시오

Q. 게임설명 : 가위바위보 게임을 메소드를 통해 구현한다.
1.난수생성기를 이용하여 1, 2, 3중 하나의 숫자를 생성한다.
2.사용자가 가위(1),바위(2),보(3) 중 하나를 낸다.
3.승부를 판단하여 출력한다. 4.1,2,3 이외의 숫자를 입력하면
잘못된 입력을 알려주고 재입력을 요구한다.
5.숫자입력을 잘못한 경우는 게임횟수에 포함하지 않는다.
6.게임은 몇번 진행할 것인지 게임시작하기 전 코인을 입력받고(최대 5번)
재시작(1), 종료(0)
7.0, 1 이외의 숫자를 입력하면 재입력을 요구해야 한다.

★ 정답 및 해설 ☆

  1. 분명히 이전의 로또 생성기보다 코드가 짧아졌다.
    기존에는 번거로운 과정들이 많았으나 컬렉션을 이용해서 핵심 기능만 가지고
    사용하니 짧은 코드로 구현이 가능해진다.
public class Coll01
{
	public static void main(String[] args)
	{
		/*
		Q. 컬렉션을 이용한 로또번호 생성기를 구현하시오. 
		1. 배열을 이용한 버전과 동일한 조건으로 제작하되 Set 컬렉션을 이용한다. 
		2. Set 계열의 컬렉션은 중복이 자동으로 제거된다. 
		3. 하지만 순서를 보장하지 않으므로 TreeSet을 사용하면 된다.
		*/
		// HashSet으로 구현하기!
		
		// Set 계열의 HashSet lotto 변수 생성
		Set lotto = new HashSet();
		for (int i=0; lotto.size()<6; i++) // 6개 출력하기(0번째~5번째)
		{
			Random lottor = new Random(); // 난수 객체 생성
			int bunho = lottor.nextInt(45)+1; // 1~45까지의 난수 생성하여 int형 bunho에 담기
			lotto.add(new Integer(bunho)); // lotto HashSet에 난수 번호를 집어넣기
			
		}
		// list 계열의 ArrayList로 HashSet lotto 변수를 가진 객체 생성
		List list = new ArrayList(lotto);
		Collections.sort(list); // 오름차순으로 정렬
		
		System.out.println("오늘의 로또번호: " + list);		
	}

}
  1. Linkedlist를 통해 삭제 기능을 구현한 문제이다.
    핵심은 흐름을 잘 따라가야 한다. 1번부터 차근차근 하나 하나 기능을 만드는 것이 중요하다
    동시에 자바(java)의 문법까지 지켜야 해서 쉬운 일은 아니다.
    Coll02 클래스로부터 생성할 데이터가 3개씩이나 있어서(이름, 나이, 학번)
    이것을 나누어서 생성자를 통해 재정의하였다.
    그리고 이름만 반환하는 메소드를 만들어서 입력값과 비교하여
    일치하면 문제대로 흘러가는 프로그램이다.
import java.util.LinkedList;
import java.util.Scanner;

public class Coll02
{
	private String name;
	private int age;
	private String stu;
	// 생성자
	public Coll02(String name, int age, String stu)
	{
		this.name = name;
		this.stu = stu;
		this.age = age;
	}
	//객체에 저장된 문자열을 저장하여 재정의
	@Override
	public String toString()
	{
		return "이름: "+ name + " 나이: " + age +" 학번:"+ stu;
	}	
	public String getName()
	{
		return name;
	}
	/*
	 * Q. 사용자로부터 이름을 입력받아 그 이름으로 검색해서 인덱스 위치(indexOf사용)를 알아내서 
	 * 해당 인덱스로 그 객체를 삭제하고 삭제된 객체의 정보(이름,나이,학번)를 출력하여라. 
	 * 1.검색할 이름을 입력받음 
	 * 2.확장for문으로 컬렉션 전체를 접근 
	 * 3.검색결과 유/무에 따라 검색결과 있을때 검색된 데이터 삭제되고
	 * 검색결과 없을때...검색결과가 없다고 출력 
	 * 4.전체정보 출력
	*/

	public static void main(String[] args)
	{

//		ArrayList<Coll02> list = new ArrayList<Coll02>();
		LinkedList<Coll02> list = new LinkedList<Coll02>();

		// 저장할 객체 생성
		Coll02 st1 = new Coll02("가길동", 10, "2018");
		Coll02 st2 = new Coll02("나길동", 20, "2017");
		Coll02 st3 = new Coll02("다길동", 30, "2016");
		Coll02 st4 = new Coll02("마길동", 40, "2015");
		// 객체 추가(컬렉션에 저장)
		list.add(st1);
		list.add(st2);
		list.add(st3);
		list.add(st4);
		
		// 1.검색할 이름을 입력받음
		System.out.println("삭제할 이름을 입력해주세요: ");
		Scanner scan = new Scanner(System.in);
		String name = scan.nextLine();
		// 2.확장for문으로 컬렉션 전체를 접근
		for(Coll02 e : list)
		{
			// 3. 반복문을 돌다가 이름이 일치한 부분을 만나면?
			if(name.equals(e.getName()))
			{
				System.out.println("[검색되었습니다]");
				// 정보가 일치하는 객체를 찾았다면 참조값을 통해 삭제한다
				list.remove(e);
				// 객체를 삭제하면 즉시 반복문 완전 탈출 
				System.out.println("현재 객체수:" + list.size());
				//4.전체정보 출력
				for(Coll02 e1 : list)
				{
					System.out.println(e1);
				}
				return;
			}
		}
		// 없을 경우: 말하기
		System.err.println("그런 이름은 없습니다.");
	}		
}
  1. 어려우면서도 중요한 문제이다.
    프로젝트 내에 존재하는 모든 클래스의 조상이 되는 클래스가 있다.
    바로 Object 클래스다. Object 클래스는
    따로 만들지 않아도 JDK에 기본적으로 존재한다.
    즉 class A {}는 class A extends Object {} 와 같다는 것

★ 모든 클래스는 Object를 상속받는다

그러기에 Object는 생략 가능한 것이다.

하지만 우리는 중복을 제거하기 위해 숨어있는 Object 클래스를 불러낼 것이다.

equals(Object obj)

  • a와 b 두 변수에는 결론적으로 같은 값이 들어 있다.
    사람이 보기엔 딱 봐도 둘은 같다. 그러나 컴퓨터는 가리키는 주소 값이
    다르면 둘을 다르다고 결론짓는다.
    난감한 상황이지만 equals(Object obj)를 사용하면 문제를 해결할 수 있다.

추가로 Hash Code란, 객체를 식별할 수 있는 유니크한 값을 의미한다.

class QuHashSet
{
	// 이름, 닉네임, 무기로 멤버 변수를 나눠준다.
	String name;
	String heroName;
	String weapon;

	// 생성자(매개변수)
	public QuHashSet(String name, String heroName, String weapon)
	{
		this.name = name;
		this.heroName = heroName;
		this.weapon = weapon;
	}

	@Override // (이름, 닉네임 이런 식으로 구분하기 위해 오버라이딩이 필요하다)
	public String toString()
	{
		return "Avengers [본명=" + name + ", 닉네임=" + heroName + ", " + "능력=" + weapon + "]";
	}
	
	@Override
	public int hashCode() 
	{
		int num = java.util.Objects.hash(name); 
		//Object 클래스에 상속된 name을 nume에 넣고 해쉬코드를 반환한다.
		// here1 : -566707385, here2 : 431106087, here3 : -853886273, here4 :-566707385
		return num;
	}
	
	// ★★ 데이터 타입이 Object인 obj 값을 받는 equals 메서드
	// 중복을 제거해주는 중요한 역할
	// 비교할 값이 있고 obj를 QuHashSet 타입으로 형변환할 수 있다면
	// return name == ((QuHashSet)obj).name;  변수에 저장된 값과 참조변수를 통해 접근한 
	// 주소 값을 같게 한다는 의미다.
	
	public boolean equals(Object obj)
	{
		//Object 타입의 obj를 QuHashSet 타입으로 형변환할 수 있는지 여부(값이 같다면 형변환이 가능할 것)
		//그렇지 않을 경우엔 return false;를 반환하여 출력되지 않는다.
		if (obj instanceof QuHashSet)
		{
//			QuHashSet tmp = (QuHashSet) obj;
//			return name.equals(tmp.name);
			return name == ((QuHashSet)obj).name; 		
		}
		return false;
	}
	
	
	// 마지막으로 출력할 양식을 만들어준다.
	public void showAllData()
	{
		System.out.println("Avengers [본명=" + name + ", 닉네임=" + heroName + ", 능력=" + weapon + "]");
	}

	public static void main(String[] args)
	{

		HashSet<QuHashSet> set = new HashSet<QuHashSet>();

		QuHashSet hero1 = new QuHashSet("토니스타크", "아이언맨", "Mark-48 수트");
		QuHashSet hero2 = new QuHashSet("스티브로져스", "캡틴아메리카", "비브라늄 방패");
		QuHashSet hero3 = new QuHashSet("브루스배너", "헐크", "강한피부&점프");
		QuHashSet hero4 = new QuHashSet("토니스타크", "아이언맨", "Mark-48 수트");
		
		set.add(hero1);
		set.add(hero2);
		set.add(hero3);
		set.add(hero4);
		System.out.println("[최초 전체 정보출력]");
		// 향상된 for문 HashSet set 변수를 av 변수를 통해 차례대로 값을 가져온다
		for (QuHashSet av : set)
		{
			System.out.println(av.toString());
		}
		//검색한 정보가 존재하는지 확인하기 위한 변수
		boolean isFind = false;
		System.out.println("검색할 이름을 입력하세요: ");
		Scanner scan = new Scanner(System.in);
		String heroc = scan.nextLine();
		
		// 문제의 조건에 따라 Iterator로 전체 조회를 한다.
		Iterator<QuHashSet> itr = set.iterator();
		while (itr.hasNext())
		{
			QuHashSet fr = itr.next();
			/*
			 * 배열의 각 인덱스에 저장된 객체의 참조값을 통해 멤버변수에 접근한다. 
			 * 검색을 위해 입력한 이름과 비교해서 일치하는 경우에만 정보를
			 * 출력한다. 이때 compareTo() 대신 equals() 를 사용해도 된다.
			 */

			// compareTo(fr.name)==0 또는 equals(fr.name) 둘중 하나 아무거나 가능
			// ★★★ 매우 중요, Iterator에서 compareTo을 통해 값을 비교할 수 있다
			// compareTo -> 값이 같다면 0 , 다르다면 음수
			if (heroc.compareTo(fr.name) == 0) // 사용자가 입력한 이름과 HashSet 안의 값, 이름과 같다면?
			{
				fr.showAllData();
				System.out.println("**귀하가 요청하는 정보를 찾았습니다.**");
				// 그리고 변수를 true로 변경한다.
				isFind = true;
			}
		}
		// 만약 검색된 정보가 없다면 아래와같이 출력한다.
		if (isFind == false)
			System.out.println("해당 영웅은 없어요ㅜㅜ");
	}
};
  1. 계정을 담는 프로그램이다.
    중요한 핵심은 private로 선언했기에 간접적으로만 데이터를 얻어올 수 있어서
    getter 메소드를 사용했다는 점
    입력을 받고 , 쉼표를 통해 값을 쪼개어 저장한 후
    입력 실수를 방지하기 위해 공백제거를 넣어준 점
    그리고 키 - 값 하나가 아닌 전체 리스트를 출력하기 위해
    set 컬렉션으로 변환해준 이후 get()을 통해 검색을 하여 조회를 한 점이 핵심이다.

    흐름을 놓치지 않고 따라가면서
    하나하나 기능을 차근차근 만들어가면서
    자바(java)의 문법을 잘 지켜야 하는 점이 핵심이다.

class ID
{
	// private 개인정보 은닉화
	private String key; 
	private String ida, pass; // 아이디, 비번

	//생성자(매개변수)
	public ID(String key, String ida, String pass)
	{
		this.key = key;
		this.ida = ida;
		this.pass = pass;
	}
	// private을 간접적으로 얻어오기 위한 getter
	public String getkey()
	{
		return key;
	}
	// private을 간접적으로 얻어오기 위한 getter
	public String getida()
	{
		return ida;
	}
	// private을 간접적으로 얻어오기 위한 getter
	public String getpass()
	{
		return pass;
	}
}

public class Coll04
{
	public static void main(String[] args)
	{
		// 입력값을 받고 HashMap 컬렉션 생성
		Scanner sc = new Scanner(System.in);
		HashMap<String, ID> info = new HashMap<String, ID>();

		System.out.println("▶ 등록할 키값, 아이디, 비밀번호 입력: ");

		//값을 저장하기 위한 반복문 생성
		for (int i = 0; i < 5; i++)
		{
			System.out.print(">> ");
			String text = sc.nextLine();
			// ,(쉼표를 기준으로) 문자열을 쪼갬
			StringTokenizer st = new StringTokenizer(text, ","); 
			// 앞뒤 공백을 제거(trim), 입력한 값을 변수에 저장한다.
			String key = st.nextToken().trim(); 
			String ida = st.nextToken().trim();
			String pass = st.nextToken().trim();

			// 생성자 id로 매개변수를 호출
			ID ID = new ID(key, ida, pass);
			// key와 ID는 HASHMAP 컬렉션에 넣는다.
			info.put(key, ID);
		}
		
		Set<String> key01 = info.keySet(); // 해시맵 info에 있는 모든 키 Set 컬렉션으로 변환
		Iterator<String> it = key01.iterator(); // Set을 순차검색하는 이터레이터 생성
		System.out.println("---------------------------");
		while (it.hasNext())
		{
			String key1 = it.next();
			ID ID = info.get(key1); // ID를 키로 하여 객체를 얻는다.
			System.out.println("정상적으로 저장했습니다.");
			System.out.print(ID.getkey() + " ");
			System.out.print(ID.getida() + " ");
			System.out.println(ID.getpass() + " ");
		}
		System.out.println("---------------------------");

		while (true)
		{
			System.out.print("★ 조회할 키값 입력 >> ");
			String key1 = sc.nextLine();
			if (key1.equals("그만"))
				break;

			ID ID = info.get(key1); // 해시맵에서 이름을 키로 검색
			if (ID == null)
			{ // 이름이 해시맵에 없다면
				System.out.println(key1 + "라는 데이터는 존재하지 않습니다.");
			} else
			{ // 해시맵에서 검색된 목록 출력
				System.out.println("====================");
				System.out.println("키값(key): "+ID.getkey());
				System.out.println("아이디(id): "+ID.getida());
				System.out.println("비밀번호(pw): "+ID.getpass());
				System.out.println("====================");
			}
		}
		sc.close();
	}
}
  1. 이 프로그램은 매우 디테일하게 구현된 가위바위보이다.
    중요한 점은 오류가 날만한 경우의 수를 생각하고 그에 따라 기능과 흐름을
    놓치지 말고 따라가며 하나하나 구현하는 것이다.
    게임중일때도 게임의 틀에 벗어나는 입력시에는
    카운팅되지 않아 원인을 친절하게 안내하고 재입력을 요구하여 게임을 끝까지
    진행시키는 것이 목적이다.
    주석에 코드 설명이 나와있으니 꼭 참고하자!
public class Coll05
{
	public static void main(String[] args)
	{
		// 가위바위보게임만들기
		Scanner input = new Scanner(System.in);

		// 게임결과 값을 표현한 변수
		int win = 0, lose = 0, draw = 0, fail = 0, count=0; 
		// 게임횟수 변수
		String coin;

		System.out.print("동전을 넣으세요(최대 5): ");

		// 값이 숫자가 아닐 경우 무한반복
		while (!input.hasNextInt())
		{
			input.next();// 값이 숫자가 아니면 다음으로 넘어간다.
			System.out.println("============================================");
			System.out.println();
			System.out.println("※ 동전을 잘못 넣으셨습니다. 재실행해주세요!");
			System.out.println();
			System.out.println("================GAME END====================");
		}

		coin = input.next();// 정상 입력된 숫자라면, 게임횟수 값 저장
		int coinInt = Integer.valueOf(coin);// 정수형 값(int)로 형변환

		// 예외인 경우 게임실행 x
		if (coinInt > 5)
		{
			System.out.println("============================================");
			System.out.println();
			System.out.println("※ 최대 5판까지만 게임이 가능합니다.");
			System.out.println();
			System.out.println("================GAME END====================");
			return;
		} 
		// 코인을 넣지 않았을 경우
		else if (coinInt == 0)
		{
			System.out.println("============================================");
			System.out.println();
			System.out.println("※ 동전을 넣어주셔야 게임 실행이 가능합니다.");
			System.out.println();
			System.out.println("================GAME END====================");
			return;
		} 
		// 음수값일 경우
		else if (coinInt < 0)
		{
			System.out.println("============================================");
			System.out.println();
			System.out.println("※ 동전이 들어가지 않았습니다. 재실행해주세요.");
			System.out.println();
			System.out.println("================GAME END====================");
			return;
		}
		// 이상없으면 게임 정상적으로 실행

		// GAME START
		System.out.println("===============GAME START================");
		System.out.println();
		System.out.println("▶ 무엇을 내시겠습니까? 가위[1] 바위[2] 보[3]");

		// 코인에 입력한 횟수만큼 반복하는 게임창
		for (int i = 0; i < coinInt; i++)
		{
			String npc = "", user = ""; // 컴퓨터와 사용자의 가위,바위,보 선택 변수
			int computer = (int) Math.ceil(Math.random() * 3);// 난수 생성(랜덤)

			while (!input.hasNextInt())
			{ // 값이 숫자인지 판별,입력값이 숫자가 아니라면 while문 실행
				input.next();// 값이 숫자가 아니면 버린다.
				System.out.println("=========================================");
				System.out.println("▶ 잘못 입력하셨습니다.");
				System.out.println("▷ 가위[1] 바위[2] 보[3] 재입력: ");
				System.out.println("=========================================");
			}
			
			String userInput = input.next();// 사용자의 입력값
			int userInputInt = Integer.parseInt(userInput);
			// 유저의 가위바위보
			switch (userInputInt)
			{
			case 1:
				user = "가위";
				count++;
				break;
			case 2:
				user = "바위";
				count++;
				break;
			case 3:
				user = "보";
				count++;
				break;
			default:
				user = "손가락 오류";
			}
			// 컴퓨터의 가위바위보
			switch (computer)
			{
			case 1:
				npc = "가위";
				break;
			case 2:
				npc = "바위";
				break;
			case 3:
				npc = "보";
				break;
			default:
				npc = "시스템 오류";
			}
			System.out.println("===============["+ coin + "판/"+ count +"판中]===============");
			System.out.println("[유저] " + user + " VS " + npc + " [컴퓨터]");

			// 가위바위보 승패 설정
			if (userInputInt >= 1 && userInputInt <= 3)
			{// 유저의 가위바위보 입력값이 1~3일 때만 정상 진행
				// 가위=1,바위=2,보=3
				if ((computer == 1 && userInputInt == 2) || (computer == 2 && userInputInt == 3)
						|| (computer == 3 && userInputInt == 1))
				{// 유저가 이긴경우
					win++;
					System.out.println("[승리]");
				} 
				else if (computer == userInputInt)
				{// 무승부인경우
					draw++;
					System.out.println("[무승부]");
				} 
				else
				{
					lose++;
					System.out.println("[패배]");
				}
			} 
			else
			{// 오류상황
				fail++;
				coinInt++; //게임횟수에 포함하지 않는 재입력 요구
				System.out.println("가위[1] 바위[2] 보[3] 재입력: ");
			}
			System.out.println("=========================================");
		}
		float wins = ((float) win / (float) (coinInt)) * 100; // 승률변수 =(이긴횟수/총 경기)x100

		System.out.println("[결과] 총 게임수 : " + coin);
		System.out.println("[전적] win:" + win + "번, lose:" + lose + "번, draw:" + draw + "번, 무효:" + fail + "번");
		System.out.printf("[승률] %.2f%% \n", wins);
		System.out.println("================GAME END=================");
		input.close();
	}
}
  • 이렇게 자바(JAVA) 공부의 후반부 시작을 알리는
    제네릭과 컬렉션 프레임워크가 끝이 났다.
    이는 자바(JAVA)가 정한 규칙이며 형식 및 틀이기에 암기할 것도 많고
    아이디어도 중요할 뿐아니라 논리적으로 생각할 것도 많은 어려운 파트이다.
    한번보고 모두 익힐 수 없는 것이 당연하기에 지속적으로 여러번
    많이 복습이 필요한 파트이다!
profile
[Java SQL HTML CSS JS Studying] 발전을 꿈꾸며 이상을 실현합니다
post-custom-banner

0개의 댓글