본문 바로가기
Backend

06월 10일 월 | OOP 16 - JAVA Collection Framework과 Generic

by 구라미 2019. 6. 10.

이번주의 진도

Java Collection Framework

Thread

IO 입출력

---------------------

Network

JDBC

---------------------

자체교재 - chapter 20~22

교학사 - 9장, 12장

---------------------

UI 화면 구현 클래스

awt

 


11. Java Collection Framework

Java Collection Framework

컬렉션은 객체데이터의 자료구조를 구현하기 위한 표준 라이브러리이다.

이 라이브러리는 인터페이스 형식으로 제공된다.

자료를 모아서 저장할 수 있는 클래스들.

컬렉션이란 데이터의 집합, 그룹을 의미한다. 

JCF는 이러한 데이터, 자료구조인 컬렉션과 이를 구현하는 클래스를 정의하는 인터페이스를 제공한다.

배열도 이것의 일종이다.

어떤 식으로 자료를 모으냐에 따라서 달라진다.

 

외부에서 제공하는 라이브러리

대부분 확장명이 (.jar)의 형식을 갖고 있다.

내가 만들어서 배포할 수도 있음.

패키지 -> 마우스 우클릭 -> Export -> Java -> jar file.

 

 

 

Collection

List

-구현클래스: 

AbstractList, AbstractSequentialList, ArrayList, AttributeList, CopyOnWriteArrayList, LinkedList, RoleList, 

RoleUnresolvedList, Stack, Vector

-특징: 순서(index)가 있다. 이 순서는 0부터 시작한다. 순차적으로 정보가 들어간다. 

페이지간 자료 공유할 때 불러와서 전달해줄 때 데이터를 다 축적해서 한번에 전달.

자료 여러개를 한꺼번에 넘길 때 사용.

 

//List 계열
List list = new Vector(); //다형성
List item = new ArrayList(); //다형성
Vector vec = new Vector();

vec.add(new Object()); //add ()안 매개변수 object
vec.add(new Integer(3));
vec.add(new Double(2.1));
vec.add(new String("라면"));
vec.add(5); //autoboxing되서 추가가능.
vec.add(6.7); //autoboxing되서 추가가능.
vec.add("짜장면"); //autoboxing되서 추가가능.
System.out.println(vec);
System.out.println(vec.size());

//결과값
//[java.lang.Object@15db9742, 3, 2.1, 라면, 5, 6.7, 짜장면]
//7 <-배열의 요소갯수

이렇게 vec안에 요소 데이터가 차곡차곡 쌓인다.

 

배열의 요소가져오려면

//요소가져오기
Object three = vec.get(3);		
System.out.println(three);
String str = (String) three; //다형성

//결과값
//라면

이런 식으로 오버라이드 할 수 있음.

 

//전체요소 출력하기
for(int i=0; i<vec.size(); i++){
	System.out.println(vec.get(i));
}
		
//0번째 요소 제거
vec.remove(0); //0번째 요소를 삭제함.
for(int i=0; i<vec.size(); i++){
	System.out.println(vec.get(i));
}
		
//모든 요소 삭제하기.
vec.removeAllElements(); //모든 요소삭제
System.out.println(vec.size());
		
if(vec.isEmpty()){
	System.out.println("요소가 없습니다.");
}else {
	System.out.println("요소가 있습니다.");
}

 

 

연습문제 1) remove()를 이용해서 요소 전부 삭제하기

*함정이 있었다. 

//연습문제 1)
//remove()를 이용해서 요소 전부 삭제하기.
for(int i=0; i<vec.size(); i++){
	vec.remove(i);
	i--;
}
System.out.println(vec.size());


//선생님 답안
for(int i=vec.size()-1; i>=0; i--){
	vec.remove(i);
}
System.out.println(vec.size());

프로그래밍을 하다가 remove()를 이용해 요소를 삭제할 경우, 그 요소가 없어진 자리에 뒤에 있던 자료가 다시 오기 때문에 그 위치는 삭제가 안됨. 전체가 인덱스가 틀어지기 때문에 뒤에서부터 지워줘야한다.

 

다형성

//다형성
List lst = new ArrayList();
lst.add(3);
lst.add(1.2);
lst.add("seoul");
lst.add(new Integer(5));
		
for(int i = 0; i<lst.size(); i++){
	System.out.println(lst.get(i));
}

//결과값
//3
//1.2
//seoul
//5

 

 

Set

-구현클래스: 

AbstractSet, ConcurrentHashMap.KeySetView, ConcurrentSkipListSet, 

CopyOnWriteArraySet, EnumSetHashSet, JobStateReasons, LinkedHashSet, TreeSet

-특징: 순서를 유지하지 않는 데이터의 집합, 데이터 중복을 허용하지 않는다.

 

Interface Iterator

인터페이스 중에 iterator라는 것이 있는데 이것은 set이랑 짝이다.

요소가 있긴 한데 인덱스가 살아있는 것은 순차적으로 접근하면된다.

그러나 set은 순서가 없기 때문에 위치를 가리켜주는게 필요.

현재 한 요소를 가리키는 것을 커서라고 한다. iterator는 그 가리키는 역할을 한다.

커서가 가리키는 요소를 가져와라 -> iterator와 set 사용.

 

*StringTokenizer 개념 참고.

 

Set과 Iterator

//Set 계열
Set set = new HashSet();
set.add(1);
set.add(1.2);
set.add("HAPPY");
set.add(new Integer(5));
System.out.println(set.size());
		
//Interface Iterator
//->cursor를 이용해서 요소를 접근하는 경우 
Iterator iter = set.iterator();
while(iter.hasNext()){//cursor가 존재하는지 판단
	//cursor가 가리키는 요소 가져오기
	Object obj = iter.next();
	System.out.println(obj);
}//while end


//결과값
//1.2
//1
//HAPPY
//5

set은 순서가 없다. 이 일관성 없는 문제를 해결할 때 제네릭 사용.

 

 

Map

-구현클래스:

AbstractMap, Attributes, AuthProvider, ConcurrentHashMap, ConcurrentSkipListMap, 

EnumMap, HashMapHashtable, IdentityHashMap, LinkedHashMap, PrinterStateReasons, 

Properties, Provider, RenderingHints, SimpleBindings, TabularDataSupport, TreeMap, UIDefaults, WeakHashMap

-특징: 순서가 없다. 그러나 Key(이름)와 Value(값)라는 것이 존재한다. Key는 중복불허, Value는 중복허용.

Iterator란?

Iterator 메소드는 자바의 컬렉션 프레임워크에서 컬렉션에 저장되어 있는 요소들을 읽어오는 방법을 표준화하였는데

그 중의 하나이다. Iterator를 사용하여 List, Map 등 자료구조안의 요소들을 읽어올 수 있다.

 

Map map = new HashMap();
map.put("one", "손흥민");
map.put("two", 3);
map.put("three", new Double(5.6));
		
//key는 중복선언 불가.
//one이 겹쳤기 때문에 나중에 선언된 것이 그 전 값 덮어버림
map.put("one", "박지성"); 
		
//value는 중복이 가능
map.put("four", "손흥민");
		
System.out.println(map.size());
System.out.println(map.get("one"));
System.out.println(map.get("three"));

//결과값
//4
//박지성
//5.6

 

 

연습문제 2)
= 문자 기준으로 문자열 앞은 key, 뒤의 문자열은 value로 분리해서 map에 저장하고
key 값으로 "read.do"를 호출하면 
value값으로 "net.bbs.Read" 출력하시오.

 

내 풀이

HashSet command = new HashSet();

command.add("list.do=net.bbs.List");
command.add("read.do=net.bbs.Read");
command.add("write.do=net.bbs.Write");
		
Map map = new HashMap();
		
Iterator iter = command.iterator();
while(iter.hasNext()){//cursor가 존재하는지 판단
	//cursor가 가리키는 요소 가져오기
	String str = (String) iter.next();
	StringTokenizer st = new StringTokenizer(str,"=");
	while(st.hasMoreTokens()){ //토큰기호가 있는지 판별
		String key = st.nextToken();
		String value = st.nextToken();
		map.put(key,value);
	}
			
}//while end		
		
System.out.println(map.get("read.do"));

▶ 풀이

1. HashSet클래스 command를 만들고 객체를 생성하였다.

2. command 객체 안에 차례대로 문자열을 넣어준다.

3. HashMap클래스 객체를 생성하였다.

4. Interator iter에 Iterator메소드로 command를 읽어온 것을 담는 객체를 만든다.

5. while반복문안에 조건을 iter를 hasNext()메소드로 차례대로 읽어옴.

6. 문자열 str 변수 안에 iter안의 요소를 형변환해서 담는다?

7. StringTonkenizer st로 새로운 생성자 인자를 str을 "="기준으로 토큰을 짜른다.

8. while 반복문으로 토큰기호가 있는 지 판별하고

9. 잘라낸 순서대로 String key, String value에 담는다.

10. map안에 key와 valuer값을 넣는다.

11. map에서 get메소드로 "read.do"를 출력하면 이 key값에 대응하는 value net.bbs.Read 가 나온다. 

 

 

hasNext()의 의미는 Iterator가 보통 순방향으로 이동하는데, Iterator가 가리키는 데이터저장소의 현재 위치에서 이동할 항목이 있는지 체크합니다. 즉, Iterator는 내부적으로 데이터저장소의 자신이 가리키고 있는 지점을 알고 있습니다.  결국, 이동할 항목이 있다면 true을 리턴하고 그렇지 않으면 false을 리턴합니다.

next()메소드는 실제로 Iterator가 자신이 가리키는 데이터저장소에서 현재위치를 순차적으로 하나 증가해서 이동하는 것을 말합니다.

 

선생님 풀이

//선생님 풀이
		
HashSet command = new HashSet();

command.add("list.do=net.bbs.List");
command.add("read.do=net.bbs.Read");
command.add("write.do=net.bbs.Write");
		
Map map = new HashMap();
	
Iterator iter = command.iterator();
while(iter.hasNext()){
	String str = (String) iter.next(); //command에 추가된 문자열을 iter에 담음.
	int pos = str.indexOf("=");
	String key = str.substring(0,pos);
	String value = str.substring(pos+1);
	map.put(key,value);
}//while end						
		
System.out.println(map.get("read.do"));

▶ 풀이

나는 StringTokenizer를 이용해 해결한 것을 선생님은 substring을 이용해 해결하였다.

 

12. Generic

Generic 제네릭이란?

데이터를 수집하는 경우 자료형을 제한할 수 있다.

제네릭은 다양한 타입의 객체들을 다루는 메소드나 컬렉션 클래스에 컴파일 시의 타입 체크를 해주는 기능이다.

즉, 클래스 내부에서 사용할 데이터 타입을 나중에 인스턴스를 생성할 때 확정하는 것을 제네릭이라고 한다.

한꺼번에 여러 자료형이 들어오면 관리하기 어렵다. 제네릭으로 원하는 자료형만 제한을 둘 수 있다.

 

제네릭의 여러가지 형태

<E> Element :  <참조자료형> 을 넣어주면 됨. 클래스만 올 수 있음.

<T> 사용자 정의형으로도 만들 수 있음.

<?>

 

제네릭 예시 1)

//Vector<int> vec: 에러 기본형이 아니라 참조형이어야함.
//Element는 참조형(클래스)만 가능하다.

Vector<String> vec = new Vector<String>();
vec.add("안녕");
//vec.add(3); //에러
vec.add(new String("손흥민"));
//vec.add(new Integer(5)); //에러
vec.add("김연아");
vec.add("심석희");
		
for(int i=0; i<vec.size(); i++){
	String str = vec.get(i);
	System.out.println(str);
}
//결과값
//안녕
//손흥민
//김연아
//심석희

 

제네릭 예시2)

ArrayList<Integer> arr = new ArrayList<Integer>();
arr.add(3);
arr.add(new Integer(5));
//arr.add("안녕"); //String형이라 에러
//arr.add('r'); //Character형이라 에러
//arr.add(2.5); //Double형이라 에러

 

제네릭 예시3)

HashSet<String> set = new HashSet<String>();
set.add("심석희");
set.add("최민정");
set.add("김아랑");
		
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("Twice", 9); //한글은 Key값으로 웬만하면 쓰지 말아야 한다.
map.put("RedVelvet", new Integer(5));
map.put("BTS", 2);
map.put("BlackPink", 3);

 

 

제네릭 예시4)

하나의 꾸러미형태로 만들어서, 값을 모아 다른페이지에 전달할 수 있다.

제네릭은 사용자가 만든 자료형을 넣을 수 있다.

package oop0610;

import java.util.*;


class Mountain {
	String name; //산이름
	int height; //산높이
	public Mountain(){}
	public Mountain(String name, int height){
		this.name = name;
		this.height = height;
	}
	
	
}//class end




public class Test03_Generic {

	public static void main(String[] args) {
		
		Mountain one = new Mountain("Everest",8848);
		Mountain two = new Mountain("Rocky",4800);
		Mountain three = new Mountain("Annapurna",8091);
		
		ArrayList <Mountain> list = new ArrayList<Mountain>(); //사용자 정의 자료형도 제네릭에 올 수 있다.
		list.add(one);
		list.add(two);
		list.add(three);
		//list.add("솔데스트"); //에러
		
		for(int i =0; i<list.size(); i++){
			Mountain dto = list.get(i);
			System.out.println(dto.name+"는 "+"해발 "+dto.height+"m입니다.");			
		}
		
		
	}//main end
}//class end

 

 

ㄴㅇㄹㄴㅇㄹㄴㅇㄹㄴㅇㄹ

 

 

 


예제 1) 상품구매 프로그램 만들기

지금까지 배웠던 기능을 써본 예제이다.

자유자재로 쓸 수 있을 때까지 반복해서 보기

 

예제1) 상품구매 프로그램

package oop0610;
import java.util.*;

class Product { //인터페이스로 만드는거도 괜찮다.
	public int price; //상품가격
	public int bonusPoint; //마일리지
	public Product(){} //default Constructor
	public Product(int price){
		this.price = price;
		this.bonusPoint=(int)(price*0.1); //마일리지는 상품가격의 10%
	} //메소드 오버로딩
	
}//class end

class SmartTV extends Product{
	 //상품가격 100
	 //부모에게 상속받은 price에 상품가격 넣어주기
	 //super 명령어 이용하기.
	public SmartTV(){ //'나'의 생성자를 통해서 상속.
		super(100); //상품가 100, 보너스포인트 10
	}
	
	@Override
	public String toString() {
		return "스마트TV"; //상품명
	}
	
	
}

class Notebook extends Product{
	//상품가격 200
	public Notebook(){
		super(200); //상품가 100, 보너스포인트 10
	}
	
	@Override
	public String toString() {
		return "노트북";
	}
}

class SmartPhone extends Product{
	 //상품가격 300
	public SmartPhone(){
		super(150);
	}
	
	@Override
	public String toString() {
		return "스마트폰";
	}
}//class end


class Buyer{
	private int money = 1000; //총 재산
	private int mileage = 0;  //내 마일리지
	private int myProduct = 0;
	
	//구매한 상품을 저장
	private Product[] item = new Product[10]; //배열은 자료형이 같은 것만 받아온다. 그래서 부모인 Product 자료형을 받음.
	private int i=0;
	
	
	public Buyer(){}
	
	//이 셋은 부모가 product란 공통점이 있다.
/*	public void buy(SmartTV a){} 
	public void buy(Notebook a){}
	public void buy(SmartPhone a){}*/
	
	public void buy(Product a){ //몇 개를 샀는지 모아놓을 필요가 있다. 컬렉션프레임워크나 객체배열에 저장한다.
		if(this.money<a.price){
			System.out.println("잔액이 부족합니다.");
			return;
		}//if end
		
		//구매한 상품을 저장
		item[i++]=a;

		money = this.money - a.price;
		mileage = this.mileage + a.bonusPoint;
		
		
		
	}
	
	public void disp(){
		//내가 구매한 상품명
		String goods ="";
		
		//내가 사용한 총 누적 합계
		int hap =0;
		int items = 0;
		for(int n=0; n<item.length; n++){
			if(item[n]==null) break;
			goods = goods+item[n]+" ";			
			hap = hap+item[n].price;
			items = n+1;
		}//for end
		
		System.out.println("구매한 상품: "+goods);
		System.out.println("구매한 제품갯수: "+items+"개 입니다.");
		System.out.println("구매한 금액: "+hap+"원 입니다.");
		System.out.println("잔여금액: "+this.money+"원이 남았습니다.");
		System.out.println("마일리지: "+this.mileage+"점이 적립되었습니다.");
	}
	
}//class end



public class Test04_BuyerTest {

	public static void main(String[] args) {
		//상품구매 관련 프로그램.
		//지금까지 배운 것을 적용한 예제.
		
		
		//상품 진열하기.
		SmartTV tv = new SmartTV();
		Notebook note = new Notebook();
		SmartPhone phone = new SmartPhone();
		
		Buyer kim = new Buyer();
		kim.buy(tv);
		kim.buy(tv);
		kim.buy(note);
		kim.buy(note);
		kim.buy(phone);
		kim.disp();
		
		
		

	}//main end
}//class end

▶ 풀이

1. java.util.*;을 import한다.

2. class Product를 선언한다.

   1) 멤버변수로 public int 상품가격, 마일리지 선언한다.

   2) class Product의 생성자 메소드로 초기화 한다.

   3) class Product (메소드오버로드한 것이다.) 에 int price를 매개변수로 받는 메소드를 선언하고

      this.price로 자기 자신 참조 this.bonusPoint로는 price*0.1라고 연산하는 기능을 넣는다.

3. Product를 상속받는 SmartTV, Notebook, SmartPhone 클래스를 선언한다.

4. 내부에 super로 부모의 price를 상속받는 생성자 메소드와 상품명을 리턴하는 toString메소드를 만든다.

5. class Buyer를 선언한다.

6. 그 안에 private 제한자로 int money, int mileage, int myProduct 변수를 선언한다.

7. 구매한 상품을 저장할, Product[] 배열객체를 10개공간이 들어갈 만큼 생성한다.

8. public Buyer(){}로 초기화하는 생성자 메소드를 선언한다.

9. Product a를 매개변수로 받는 buy메소드를 선언하고 안에 물건의 가격보다 돈이 부족할 시 "잔액이 부족합니다"를

   리턴하는 조건문을 만든다. 

10. item[i++]하여 Product[] 배열객체에 구매한 상품을 담는다.

11. 현재 돈은 돈-구매한 상품가격 이다.

12. 마일리지는 현재 마일리지 + 구매한 상품가의 10%이다.

13. 출력문을 담을 메소드 disp를 선언한다.

14. 그 안에 구매할 상품명이 들어갈 String 변수 goods를 만들고 ""라는 값을 넣는다. //약간 초기값느낌

15.  int hap = 0, int items = 0을 선언하여 내가 사용한 총 금액과 제품갯수를 담을 변수를 선언한다.

16. if문으로 (vec이 비었을 때) "상품구매내역이 존재하지 않습니다." 출력 조건문을 만든다.

17. 반복문으로 vec의 요소들을 하나씩 순회하고 vec의 요소가 없을 때 반복문을 멈추고,

     그게 아니라면 goods 변수 안에 구매한 상품들을 불러와 넣는다.

     hap 변수 안에 구매한 요소들의 가격을 불러와 넣는다.

     items는 카운트한다.

18. 구매한 상품, 제품갯수, 금액, 잔여금액, 마일리지 등을 출력

19. main 메소드 안에 SmartTV클래스의 SmartTv객체, Notebook클래스의 Notebook객체, Smartphone클래스의 Smartphone객체를 선언한다.

20. Buyer 클래스의 kim이라는 new연산자로 Buyer객체를 선언하고

21. 이 kim이 buy메소드 인자로 tv, tv, note, note, phone을 넣어주고

22. kim이 disp메소드로 출력하면 연산된 결과들이 출력된다.

 

 

예제2) 상품구매 후 반품 프로그램

package oop0609;
import java.util.*;

class Product { //인터페이스로 만드는거도 괜찮다.
	public int price; //상품가격
	public int bonusPoint; //마일리지
	public Product(){} //default Constructor
	public Product(int price){
		this.price = price;
		this.bonusPoint=(int)(price*0.1); //마일리지는 상품가격의 10%
	} //메소드 오버로딩
	
}//class end

class SmartTV extends Product{
	 //상품가격 100
	 //부모에게 상속받은 price에 상품가격 넣어주기
	 //super 명령어 이용하기.
	public SmartTV(){ //'나'의 생성자를 통해서 상속.
		super(100); //상품가 100, 보너스포인트 10
	}
	
	@Override
	public String toString() {
		return "스마트TV"; //상품명
	}
	
	
}

class Notebook extends Product{
	//상품가격 200
	public Notebook(){
		super(200); //상품가 100, 보너스포인트 10
	}
	
	@Override
	public String toString() {
		return "노트북";
	}
}

class SmartPhone extends Product{
	 //상품가격 300
	public SmartPhone(){
		super(150);
	}
	
	@Override
	public String toString() {
		return "스마트폰";
	}
}//class end


class Buyer{
	private int money = 1000; //총 재산
	private int mileage = 0;  //내 마일리지
	private int myProduct = 0;
	
	//구매한 상품을 저장
	Vector<Product> vec = new Vector<Product>();
	
	
	public Buyer(){}
	
	//이 셋은 부모가 product란 공통점이 있다.
/*	public void buy(SmartTV a){} 
	public void buy(Notebook a){}
	public void buy(SmartPhone a){}*/
	
	public void buy(Product a){ //몇 개를 샀는지 모아놓을 필요가 있다. 컬렉션프레임워크나 객체배열에 저장한다.
		if(this.money<a.price){
			System.out.println("잔액이 부족합니다.");
			return;
		}//if end
		
		//구매한 상품을 저장
		vec.add(a);

		money = this.money - a.price;
		mileage = this.mileage + a.bonusPoint;
		
	}
	
	
	
	
	public void disp(){
		//내가 구매한 상품명
		String goods ="";
		
		//내가 사용한 총 누적 합계
		int hap =0;
		int items = 0;
		
		if(vec.isEmpty()){
			System.out.println("상품구매내역이 존재하지 않습니다.");
			return;
		}
		
		for(int n=0; n<vec.size(); n++){
			if(vec.elementAt(n)==null) break;
			goods = goods+vec.elementAt(n)+" ";			
			hap = hap+vec.elementAt(n).price;
			items = n+1;
		}//for end
		
		System.out.println("구매한 상품: "+goods);
		System.out.println("구매한 제품갯수: "+items+"개 입니다.");
		System.out.println("구매한 금액: "+hap+"원 입니다.");
		System.out.println("잔여금액: "+this.money+"원이 남았습니다.");
		System.out.println("마일리지: "+this.mileage+"점이 적립되었습니다.");
	}
	
	public void refund(Product a){
		if(vec.remove(a)){
			System.out.println(a.toString()+"(반품)");
			this.money=this.money+a.price;
			this.mileage=this.mileage-a.bonusPoint;
		}else {
			System.out.println(a.toString()+"상품이 없습니다.");
		}
	}
	
	
	
}//class end



public class Test05_BuyerTest {

	public static void main(String[] args) {
		//상품구매 관련 프로그램.
		//지금까지 배운 것을 적용한 예제.
		
		
		//상품 진열하기.
		SmartTV tv = new SmartTV();
		Notebook note = new Notebook();
		SmartPhone phone = new SmartPhone();
		
		Buyer kim = new Buyer();
		kim.buy(tv);
		kim.buy(note);
		kim.buy(phone);
		kim.disp();
		
		//상품 반품하기.	
		System.out.println("----------------반품----------------");
		kim.refund(phone);
		kim.refund(note);
		
		//구매내역 출력하기.
		kim.disp();
		
		
		

	}//main end
}//class end

▶ 풀이

위 프로그램에 몇가지 추가 변경사항이 있는 코드이다.

구매할 상품을 저장할 자료구조가 달라졌다. 이 이전은 Product[] 배열이었는데 이 코드에서는

제네릭으로 사용자가 정의한 <Product> 자료형만 받는 Vector 객체 vec을 생성하였다.

그리고 vec.add(a) Vector객체에 구매한 상품을 담았다.

 

그리고 환불기능이 추가되었다.

1. Buyer 클래스안에 Product a를 매개변수로 받는 refund 메소드를 선언한다.

2. if 조건문으로 (만약 vec안에 있던 a를 지운다면) a 상품명과 + 반품 출력

   돈은 환불되었으니 환불한 Product a 가격만큼 추가가 되고

   마일리지는 a의 10%인 마일리지 가격만큼 마이너스가 된다.

   위 조건과 맞지 않는다면 else로 상품이 없습니다. 출력한다.

 

 

 

 

 

 

 

 

 

댓글