본문 바로가기
Java

자바와 자료구조 / Generic, T extends, 컬렉션 프레임워크

by 리잼 2022. 11. 30.
반응형

6.  제네릭(Generic) 프로그래밍

제네릭 자료형 정의

  • 클래스에서 사용하는 변수의 자료형이 여러개일 수 있고, 그 기능(메서드)은 클래스의 자료형을 특정하지 않고
    추후 해당 클래스를 사용할 때 지정할 수 있도록 선언
  • 실제 사용되는 자료형의 변환은 컴파일러에 의해 검증되므로 안정적인 프로그래밍 방식
  • 컬렉션 프레임워크에서 많이 사용되고 있다.

제네릭 타입을 사용하지 않는 경우의 예

  • 재료가 Powder인 경우
public class ThreeDPrinter1 {

	private Powder material;

	public Powder getMaterial() {
		return material;
	}

	public void setMaterial(Powder material) {
		this.material = material;
	}
	
}
  • 재료가 Plastic인 경우
public class ThreeDPrinter2 {

	private Plastic material;

	public Plastic getMaterial() {
		return material;
	}

	public void setMaterial(Plastic material) {
		this.material = material;
	}

}
  • 여러 타입을 대체하기위해 Object를 사용할 수 있음
public class ThreeDPrinter3 {

	private Object material;

	public Object getMaterial() {
		return material;
	}

	public void setMaterial(Object material) {
		this.material = material;
	}

}
  • Object를 사용하는 경우엔 형 변환을 해주어야 한다.

  • 제네릭 클래스 정의

GenericPrinter.java

public class GenericPrinter<T> {

	private T material; // T 자료형으로 선언한 변수

	public T getMaterial() {
		return material;
	}

	public void setMaterial(T material) { // T 자료형을 반환하는 제네릭 메서드
		this.material = material;
	}
	
	public String toString() {
		return material.toString();
	}
	
}
  • 자료형 매개변수 T ( type parameter ) : 이 클래스를 사용하는 시점에 실제 사용할 자료형을 지정,
    static 변수는 사용할 수 없다
  • GenericPrinter : 제네릭 자료형
  • E : element, K : key, V : value 등 여러 알파벳을 의미에 따라 사용 가능

Powder.java

public class Powder {
	
	public String toString() {
		return "재료는 Powder 입니다";
	}
	
}

Plastic.java

public class Plastic {
	
	public String toString() {
		return "재료는 Plastic 입니다";
	}

}

GenericPrinterTest.java

public class GenericPrinterTest {

	public static void main(String[] args) {

		Powder powder = new Powder();
		GenericPrinter<Powder> powderPrinter = new GenericPrinter<>();
		powderPrinter.setMaterial(powder);
		
		Powder p = powderPrinter.getMaterial();
		System.out.println(p.toString());
		
		Plastic plastic = new Plastic();
		GenericPrinter<Plastic> plasticPrinter = new GenericPrinter<>();
		plasticPrinter.setMaterial(plastic);
		
		Plastic pl = plasticPrinter.getMaterial();
		System.out.println(pl.toString());
		
		System.out.println(powderPrinter.toString());
		System.out.println(plasticPrinter.toString());
		
	}

}

다이아몬드 연산자 <>

  • <> 를 다이아몬드 연산자라고 한다
  • ArrayList list = new ArrayList<>(); // 다이아몬드 연산자 내부에서 자료형은 생략이 가능함
  • 제네릭에서 자료형 추론 ( 자바10 부터 )
    ArrayList list  = new ArrayList()  >> var list = new ArrayList();

7. T extends 클래스

상위 클래스의 필요성

  • T자료형의 범위를 제한할 수 있음
  • 상위 클래스에서 선언하거나 정의하는 메서드를 활용할 수 있음
  • 상속을 받지 않는 경우는 T는 Object클래스가 기본으로 제공하는 메서드만 사용가능

T extends를 사용한 프로그래밍

  • GenericPrinter에 material 변수의 자료형을 상속받아 구현
  • T에 무작위 클래스가 들어갈 수 없게 Material 클래스를 상속받은 클래스로 한정

Material.java

 Powder.java, Plastic.java

GenericPinter.java

GenericPinterTest.java


8. Generic 메서드 활용

제네릭 메서드란?

  • 자료형 매개변수를 메서드의 매개변수나 반환 값으로 가지는 메서드는 자료형 매개변수가 하나 이상인 경우도 있음
  • 제네릭 클래스가 아니더라도 내부에 제네릭 메서드는 구현하여 사용할 수 있음
  • public < 자료형 매개변수 > 반환형 메서드 이름 ( 자료형 매개변수 ) {}

제네릭 메서드의 활용 예

  • 두 점 ( top, bottom )을 기준으로 사각형을 만들 때 사각형의 너비를 구하는 메서드를 만들어 보자
  • 두 점은 정수인 경우도 있고, 실수인 경우도 있으므로 제네릭 타입을 사용하여 구현한다.

Point.java

public class Point<T,V> {

	T x;
	V y;
	
	Point(T x, V y) {
		this.x = x;
		this.y = y;
	}
	
	public T getX() {
		return x;
	}
	
	public V getY() {
		return y;
	}
	
}

GenericMethod.java

public class GenericMethod {

	public static <T, V> double makeRectangle(Point<T, V> p1, Point<T, V> p2 ) {
		
		double left = ((Number)p1.getX()).doubleValue();
		double right = ((Number)p2.getX()).doubleValue();
		double top = ((Number)p1.getY()).doubleValue();
		double bottom = ((Number)p2.getY()).doubleValue();
		
		double width = right - left;
		double height = bottom - top;
	
		return width * height;
		
	}
	
	public static void main(String[] args) {
		
		Point<Integer, Double> p1 = new Point<Integer, Double>(0, 0.0);
		Point<Integer, Double> p2 = new Point<>(10, 10.0);
		
		double size = GenericMethod.<Integer, Double>makeRectangle(p1, p2);
		System.out.println(size);
	}

}


9. 컬렉션 프레임 워크

컬렉션 프레임 워크란 ?

  • 프로그램 구현에 필요한 자료구조를 구현해 놓은 JDK 라이브러리
  • java.util 패키지에 구현돼 있음
  • 개발에 소요되는 시간을 절약하면서 최적화 된 알고리즘을 사용할 수 있음
  • 여러 구현 클래스와 인터페이스의 활용에 대한 이해가 필요함

Collection 인터페이스

  • 하나의 객체를 관리하기위한 메서드가 선언된 인터페이스의 하위에 Set과 List인터페이스가 있다.

List 인터페이스

  • 객체를 순서에 따라 저장하고 관리하는데 필요한 메서드가 선언된 인터페이스
  • 자료구조 리스트 (배열, 연결리스트)의 구현을 위한 인터페이스
  • 중복을 허용함
  • ArrayList, Vector, LinkedList, Stack, Queue등

Set 인터페이스

  • 순서와 관계없이 중복을 허용하지 않고, 유일한 값을 관리하는데 필요한 메서드가 선언됨
  • 아이디, 주민번호, 사번 등을 관리하는데 유용
  • 저장된 순서와 출력되는 순서는 다를 수 있음
  • HashSet, TreeSet 등

Map 인터페이스

  • 쌍( pair )으로 이루어진 객체를 관리하는데 사용하는 메서드들이 선언된 인터페이스
  • 객체는 key-value의 쌍으로 이루어짐
  • key는 중복을 허용하지 않음
  • HashTable, HashMap, Properties, TreeMap등이 Map인터페이스를 구현함

10. List 인터페이스

멤버십 관리하기

  • Member 클래스를 만들고, 아이디와 이름을 멤버 변수로 선언
  • Member 클래스로 생성된 인스턴스들을 관리하는 클래스를 컬렉션 프레임워크 클래스들을 활용하여 구현한다.

ArrayList 활용하기

  • 멤버를 순차적으로 관리

Member.java

public class Member {
	
	private int memberId;
	private String memberName;
	
	public Member(int memberId, String memberName) {
		this.memberId = memberId;
		this.memberName = memberName;
	}

	public int getMemberId() {
		return memberId;
	}

	public void setMemberId(int memberId) {
		this.memberId = memberId;
	}

	public String getMemberName() {
		return memberName;
	}

	public void setMemberName(String memberName) {
		this.memberName = memberName;
	}
	
	@Override
	public String toString() {
		return memberName + " 회원님의 아이디는 " + memberId + " 입니다";
	}
	
}

MemberArrayList.java

import java.util.ArrayList;

public class MemberArrayList {

	private ArrayList<Member> arrayList;
	
	public MemberArrayList() {
		arrayList = new ArrayList<>();
	}	
	
	public MemberArrayList(int size) {
		arrayList = new ArrayList<>(size);
	}
	
	public void addMember(Member member) {
		arrayList.add(member);
	}
	
	public boolean removeMember(int memberId) {
		
		for(int i=0; i<arrayList.size(); i++) {
			Member member = arrayList.get(i);
			
			int tempId = member.getMemberId();
			if(tempId == memberId) {
				arrayList.remove(i);
				return true;
			}
		}
		System.out.println(memberId + "가 존재하지 않습니다.");
		return false;
	}
	
	public void showAllMember() {
		for( Member member : arrayList ) {
			System.out.println(member);
		}
		System.out.println();
	}
}

MemberArrayListTest.java

public class MemberArrayListTest {

	public static void main(String[] args) {

		MemberArrayList memberArrayList = new MemberArrayList();
		
		Member memberLee = new Member(1001, "이순신");
		Member memberKim = new Member(1002, "김유신");
		Member memberKang = new Member(1003, "강감찬");
		Member memberHong = new Member(1004, "홍길동");
		
		memberArrayList.addMember(memberLee);
		memberArrayList.addMember(memberKim);
		memberArrayList.addMember(memberKang);
		memberArrayList.addMember(memberHong);
		
		memberArrayList.showAllMember();
		memberArrayList.removeMember(memberKim.getMemberId());
		memberArrayList.showAllMember();
		
	}

}
public class MemberArrayListTest {

	public static void main(String[] args) {

		MemberArrayList memberArrayList = new MemberArrayList();
		
		Member memberLee = new Member(1001, "이순신");
		Member memberKim = new Member(1002, "김유신");
		Member memberKang = new Member(1003, "강감찬");
		Member memberHong = new Member(1004, "홍길동");
		
		memberArrayList.addMember(memberLee);
		memberArrayList.addMember(memberKim);
		memberArrayList.addMember(memberKang);
		memberArrayList.addMember(memberHong);
		
		memberArrayList.showAllMember();
		memberArrayList.removeMember(memberKim.getMemberId());
		memberArrayList.showAllMember();
		
	}

}


11. Collection 요소를 순회하는 Iterator

요소의 순회란?

  • 컬렉션 프레임워크에 저장된 요소들을 하나씩 차례로 참조하는 것
  • 순서가 있는 List인터페이스의 경우는 Iterator를 사용하지 않고 get(i)메서드를 활용할 수 있음
  • Set 인터페이시의 경우 get(i)메서드가 제공되지 않으므로 Iterator를 활용하여 객체를 순회함

Iterator 사용하기

  • boolean hasNext() : 이후에 요소가 더 있는지를 체크하는 메서드, 요소가 있다면 true를 반환
  • E next() : 다음에 있는 요소를 반환

MemberArrayList.java와 removeMember() 메서드를 Iterator를 활용하여 구현

MemberArrayList.java

for 문을 사용한것과 같은 동작을 한다


12.  중복되지 않게 자료를 관리하는 Set 인터페이스를 구현한 클래스 활용

HashSet 클래스

  • Set 인터페이스를 구현한 클래스와 멤버 중복 여부를 체크하기 위해 인스턴스의 동일성을 확인해야 함
  • 동일성 구현을 위해 필요에 따라 equals()와 hashCode() 메서드를 재정의 함

Member.java

hashCode, equlas 추가

  • 아이디가 동일한 경우 같은 멤버이므로 중복되지 않도록 Member 클래스의 equals()와 hashCode()메서드를 재정의

MemberHashSet.java

import java.util.HashSet;
import java.util.Iterator;

public class MemberHashSet {

	private HashSet<Member> hashSet;
	
	public MemberHashSet() {
		hashSet = new HashSet<>();
	}	
	
	public MemberHashSet(int size) {
		hashSet = new HashSet<>(size);
	}
	
	public void addMember(Member member) {
		hashSet.add(member);
	}
	
	public boolean removeMember(int memberId) {

		Iterator<Member> ir = hashSet.iterator();
		
		while(ir.hasNext()) {
			Member member = ir.next();
			
			int tempId = member.getMemberId();
			if(tempId == memberId) {
				hashSet.remove(member);
				return true;
			}
		}
		System.out.println(memberId + "가 존재하지 않습니다.");
		return false;
	}
	
	public void showAllMember() {
		for( Member member : hashSet ) {
			System.out.println(member);
		}
		System.out.println();
	}
	
	
}

MemberHashSetTest.java

public class MemberHashSetTest {

	public static void main(String[] args) {

		MemberHashSet memberHashSet = new MemberHashSet();
		
		Member memberLee = new Member(1001, "이순신");
		Member memberKim = new Member(1002, "김유신");
		Member memberKang = new Member(1003, "강감찬");
				
		memberHashSet.addMember(memberLee);
		memberHashSet.addMember(memberKim);
		memberHashSet.addMember(memberKang);
		
		memberHashSet.showAllMember();
		
		Member memberHong = new Member(1003, "홍길동"); // Id값이 같은값 추가
		memberHashSet.addMember(memberHong);
		memberHashSet.showAllMember();
		
	}

}

HashSet을 사용하기 전에는 같은 ID 값인 1003 홍길동이 출력되지만 

HashSet추가 후에는 중복값이므로 출력되지 않는다. ( 집합 개념 )


13. 정렬을 위해 Comparable과 Comparator 인터페이스 구현

TreeSet 클래스 활용

  • 객체의 정렬에 사용하는 클래스
  • Set 인터페이스를 구현하여 중복을 허용하지 않고, 오름차순이나 내림차순으로 객체를 정렬할 수 있음
  • 내부적으로 이진검색트리(binary search tree)로 구현됨
  • 이진검색트리에 저장하기 위해 각 객체를 비교해야 함
  • 비교 대상이 되는 객체에 Comparable이나 Comparator 인터페이스를 구현해야 TreeSet에 추가 될 수 있음
  • String, Integer등 JDK의 많은 클래스들이 이미 Comparable을 구현했음

Member.java

public class Member implements Comparable<Member>{

	......

	@Override
	public int compareTo(Member member) {
		
		//return (this.memberId - member.memberId);   //오름차순
		return (this.memberId - member.memberId) *  (-1);   //내림 차순
	}
}

오름차순 내림차순

class MyCompare implements Comparator<String>{

	@Override
	public int compare(String s1, String s2) {
		return (s1.compareTo(s2)) *-1 ;
	}
}

public class ComparatorTest {
	
	public static void main(String[] args) {
		
		Set<String> set = new TreeSet<String>(new MyCompare());
		set.add("aaa");
		set.add("ccc");
		set.add("bbb");
				
		System.out.println(set);
	}
}
  • Comparator의 활용 : 이미 Comparable이 구현된 경우 Comparator로 비교하는 방식을 다시 구현할 수 있음

14. 쌍 ( pair )으로 자료를 관리하는 Map 인터페이스를 구현한 클래스와 활용

HashMap 클래스 활용

  • Map 인터페이스를 구현한 클래스와 가장 많이 사용되는 Map 인터페이스 기반 클래스
  • key - value 를 쌍으로 관리하는 메서드 구현
  • 검색을 위한 자료구조
  • key를 이용하여 값을 지정하고 key를 이용하여 값을 꺼내오는 방식 - hash 알고리즘으로 구현
  • key가 되는 객체는 중복될 수 없고 객체의 유일성 비교를 위한 equals()와 hashCode() 메서드를 구현해야 함

Member.java

public class Member {
	
	private int memberId;
	private String memberName;
	
	public Member(int memberId, String memberName) {
		this.memberId = memberId;
		this.memberName = memberName;
	}

	public int getMemberId() {
		return memberId;
	}

	public void setMemberId(int memberId) {
		this.memberId = memberId;
	}

	public String getMemberName() {
		return memberName;
	}

	public void setMemberName(String memberName) {
		this.memberName = memberName;
	}
	
	@Override
	public int hashCode() {
		return memberId;
	}

	@Override
	public boolean equals(Object obj) {
		if ( obj instanceof Member ) {
			Member member = (Member)obj;
			if( this.memberId == member.memberId ) {
				return true;
			} else return false;
		} return false;
	}

	@Override
	public String toString() {
		return memberName + " 회원님의 아이디는 " + memberId + " 입니다";
	}
	
}

 MemberHashMap.java

import java.util.HashMap;
import java.util.Iterator;

public class MemberHashMap {

	private HashMap<Integer, Member> hashMap;
	
	public MemberHashMap() {
		hashMap = new HashMap<>();
		
	}
	
	public void addMember(Member member) {
		hashMap.put(member.getMemberId(), member);
	}
	
	public boolean removeMember(int memberId) {
		if ( hashMap.containsKey(memberId)) {
			hashMap.remove(memberId);
		} 
		System.out.println("no element");
		return false;
	}

	public void showAllMember() {
		Iterator<Integer> ir = hashMap.keySet().iterator();
		while(ir.hasNext()) {
			int key = ir.next();
			Member member = hashMap.get(key);
			System.out.println(member);
		}
	}
}

MemberHashMapTest.java

import java.util.HashMap;

public class MemberHashMapTest {

	public static void main(String[] args) {

		MemberHashMap memberHashMap= new MemberHashMap();
		
		Member memberHong = new Member(1004, "홍길동");
		Member memberLee = new Member(1001, "이순신");
		Member memberKim = new Member(1002, "김유신");
		Member memberKang = new Member(1003, "강감찬");
				
		memberHashMap.addMember(memberHong);
		memberHashMap.addMember(memberLee);
		memberHashMap.addMember(memberKim);
		memberHashMap.addMember(memberKang);
				
		memberHashMap.showAllMember();
		
		HashMap<Integer, String> hashMap = new HashMap<Integer, String>();
		hashMap.put(1001, "Kim");
		hashMap.put(1002, "Lee");
		hashMap.put(1003, "Park");
		hashMap.put(1004, "Hong");
		
		System.out.println(hashMap.toString());
	}

}

TreeMap 클래스

  • Map 인터페이스를 구현한 클래스이고, key에 대한 정렬을 구현할 수 있음
  • key가 되는 클래스에 Comparable이나 Comparator 인터페이스를 구현함으로써 key-value쌍의 자료를
    key값 기준으로 정렬하여 관리가 가능
반응형