제네릭
- 어떤 값이 하나의 참조 자료형이 아닌 여러 참조 자료형을 사용할 수 있도록 프로그래밍 하는 것을
제네릭 프로그래밍 이라고 한다. - 제네릭 프로그램은 참조 자료형이 변환될 때 이에 대한 검증을 컴파일러가 하므로 안정적이다.
제네릭의 필요성
- 3D 프린터를 예시로 들면 입체 모형을 만드는데 필요한 재료가 한 가지가 아니기 때문에 재료마다 클래스를 만드는 것은 비효율적이게 된다. 이런 경우에 재료 변수의 자료형을 Object로 사용이 가능하다.
public class ThreeDPrinter {
private Object material; //필요한 재료를 Object로 사용
public void setMaterial(Object material) {
this.material = material;
}
public Object getMaterial() {
return material;
}
}
ThreeDPrinter printer = new ThreeDPrinter();
Powder p1 = new Powder();
printer.setMaterial(p1); //자동 형 변환됨
Powder p2 = (Powder)printer.getMaterial(); //직접 형 변환을 해야 함
위처럼 Object 클래스를 사용하면 매번 형 변환을 해야 하는 번거로움이 있다. 때문에 사용하는 것이 제네릭이다.
제네릭 클래스 정의하기
- 제네릭에서는 자료형을 사용하는 부분에 Object가 아닌 하나의 문자료 표현한다.
public class GenericPrinter<T> { //제너릭 클래스 T는 type의 약자로 자료형 매개변수라 부른다.
//나중에 클래스를 사용할 때 T 위치에 실제 사용할 자료형을 지정한다.
private T material;
public void setMaterial(T material) {
this.material = material;
}
public T getMaterial() {
return material;
}
}
제네릭 클래스 사용하기
Powder 클래스 정의
package generics;
public class Powder{
public void doPrinting() {
System.out.println("Powder 재료로 출력합니다");
}
public String toString() {
return "재료는 Powder 입니다";
}
}
Plastic 클래스 정의
package generics;
public class Plastic{
public void doPrinting() {
System.out.println("Plastic 재료로 출력합니다");
}
public String toString() {
return "재료는 Plastic 입니다";
}
}
GenericPrinter<T> 클래스 정의
package generics;
public class GenericPrinter<T> {
private T material; //T 자료형으로 선언한 변수
public void setMaterial(T material) {
this.material = material;
}
public T getMaterial() { //T 자료형 변수 material을 반환하는 제네릭 메서드
return material;
}
}
GenericPrinter 클래스 사용
package generics;
public class GenericPrinterTest {
public static void main(String[] args) {
GenericPrinter<Powder> powderPrinter = new GenericPrinter<Powder>();
//Powder 형으로 GenericPrinter 클래스 생성
powderPrinter.setMaterial(new Powder());
Powder powder = powderPrinter.getMaterial(); // 형변환 하지 않음
System.out.println(powderPrinter);
GenericPrinter<Plastic> plasticPrinter = new GenericPrinter<Plastic>();
//Plastic 형으로 GenericPrinter 클래스 생성
plasticPrinter.setMaterial(new Plastic());
Plastic plastic = plasticPrinter.getMaterial(); // 형변환 하지 않음
System.out.println(plasticPrinter);
}
}
T 자료형에 사용할 자료형을 제한하는 <T extends 클래스>
- 제네릭 클래스에서 T 자료형에 사용할 자료형에 제한을 둘 수 있다.
- extends 예약어를 사용하여 제할을 둘 수 있다.
package generics;
public abstract class Material {
public abstract void doPrinting();
}
위와 같이 Material 클래스를 추상 클래스로 정의하여 재료와 GenericPrinter<T> 클래스에서 extends로 상속 받는다.
public class Plastic extends Material
public class Powder extends Material
public class GenericPrinter<T extends Material>
제네릭 메서드 활용하기
package generics;
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;
}
}
package generics;
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 rect = GenericMethod.<Integer, Double>makeRectangle(p1, p2); //<Integer, Double>는 생략 가능
System.out.println("두 점으로 만들어진 사각형의 넓이는 " + rect + "입니다.");
}
}
컬레션 프레임워크
- 프로그램을 개발할 때 사용하는 자료를 어떤 구조로 관리할 것인지가 중요하다. 이 때 사용하는 것이 자료구조이며
자바에서는 필요한 자료 구조를 미리 구현하여 패키지에서 제공하고 있는데 이름 컬레션 프레임워크라고 한다.
- 위 그림을 보면 컬렉션 프레임워크의 전체 구조는 Collection 인터페이스와 Map 인터페이스 기반으로 이루어져 있다.
- Collection 인터페이스는 하나의 자료를 모아서 관리하는데 필요한 기능을 제공하고
Map 인터페이스는 쌍으로 된 자료들을 관리하는데 유용한 기능을 제공한다.
Collection 인터페이스
- Collection 인터페이스 하위에 List 인터페이스와 Set 인터페이스가 있다.
- List를 구현한 클래스는 순차적인 자료를 관리하는데 사용하는 클래스이다.
- Set 인터페이스는 수학 시간에 배운 집합을 생각하면 되며, 집합은 순서와 상관 없이 중복을 허락하지 않기 때문에
Set 계열의 클래스는 아이디처럼 중복되지 않는 객체를 다루는데 사용한다.
Map 인터페이스
- Map 인터페이스는 하나가 아닌 쌍으로 되어있는 자료를 관리하는 메서드들이 선언되어 있다.
- key-value 쌍이라고 표현하는데 키 값은 중복될 수 없다.
- 왼쪽은 Map 인터페이스를 구현한 클래스 중 대표 클래스이고 오른쪽은 key-value의 예시이다.
- Map은 기본적으로 검색용 자료구조이다 -> 어떤 key 값을 알고 잇을 때 value를 찾기 위한 자료구조
- Map 인터페이스에 선언된 메서드 중 주요 메서드는 아래와 같다.
List 인터페이스
- List 인터페이스에는 객체를 순서에 따라 저장하고 유지하는데 필요한 메서드가 선언되어 있다.
- 대표적인 예로는 배열이 있으면 자바에서 배열을 구현한 대표 클래스로는 ArrayList, Vector가 있다.
배열과 구현 방식은 다르지만 순차 자료 구조를 구현한 LinkedList도 있다.
ArrayList 클래스
package collection.arraylist;
import java.util.ArrayList; // 사용할 클래스 import
import collection.Member;
public class MemberArrayList {
private ArrayList<Member> arrayList; // ArrayList 선언
public MemberArrayList(){
arrayList = new ArrayList<Member>(); //멤버로 선언한 ArrayList 생성
}
public void addMember(Member member){ //ArrayList에 회원을 추가하는 메서드
arrayList.add(member);
}
public boolean removeMember(int memberId){ // 멤버 아이디를 매개변수로 해당 아이디를 가진 회원을
//ArrayList에서 찾아서 제거
for(int i =0; i<arrayList.size(); i++){ // 해당 아이디를 가진 멤버를 ArrayList에서 찾음
Member member = arrayList.get(i); //get() 메서드로 회원을 순차적으로 가져옴
int tempId = member.getMemberId();
if(tempId == memberId){ // 멤버아이디가 매개변수와 일치하면
arrayList.remove(i); // 해당 멤버를 삭제
return true;
}
}
System.out.println(memberId + "가 존재하지 않습니다"); //for 가 끝날때 까지 return 이 안된경우
return false;
}
public void showAllMember(){ //전체 회원을 출력하는 메서드
for(Member member : arrayList){
System.out.println(member);
}
System.out.println();
}
}
package collection.arraylist;
import collection.Member;
public class MemberArrayListTest {
public static void main(String[] args) {
MemberArrayList memberArrayList = new MemberArrayList();
Member memberLee = new Member(1001, "이지원"); //새로운 회원 인스턴스 생성
Member memberSon = new Member(1002, "손민국");
Member memberPark = new Member(1003, "박서훤");
Member memberHong = new Member(1004, "홍길동");
memberArrayList.addMember(memberLee); //ArrayList에 회원 추가
memberArrayList.addMember(memberSon);
memberArrayList.addMember(memberPark);
memberArrayList.addMember(memberHong);
memberArrayList.showAllMember(); //전체 회원 출력
memberArrayList.removeMember(memberHong.getMemberId()); //memberHong 회원 삭제
memberArrayList.showAllMember(); //회원 삭제 후 다시 전체 회원 출력
}
}
ArrayList와 Vector 클래스
- Vector 클래스도 ArrayList와 마찬가지로 배열을 구현한 클래스이다.
- ArrayList와 Vector 클래스의 차이는 동기화 지원 여부이다.
- 동기화는 두개 이상의 스레드가 동시에 Vector를 사용할 때 오류가 나지 않도록 실행 순설르 보장하는 것이다.
- 스레드는 작업의 단위를 말하며, 하나의 스레드만 수행되면 단일 스레드, 두 개 이상의 스레드가 동시에 실행되는 경우를 멀티 스레드 라고 한다.
- 두 작업이 동시에 실행되는 멀티 스레드 환경이 아닌 경우 ArrayList를 그러한 환경이면 Vector를 사용한다.
LinkedList 클래스
- 배열은 생성한 때 정적인 크기로 선언하고, 자료를 중간에 삽입하거나 삭제할 때 나머지 자료를 이동시키지 않고 연속된 자료 구조를 구현하며, 크기 이상으로 요소가 추가되면 크기에 맞게 배열을 새로 생성하여 복사해야 하는 번거로움이 있다. 이러한 단점을 개선한 자료 구조가 링크드 리스트이다.
- 링크드 리스트의 각 요소는 다음 요소를 가르키는 주소 값을 가지기 때문에 요소를 추가하거나 삭제하는 경우
나머지 요소를 옮기고 요소가 가리키는 주소 값만 변경해 주면 된다. - 사용하는 자료의 변동이 많은 경우에는 링크드 리스트를, 거의 없는 경우에는 배열을 사용하는 것이 효율적이다.
package collection.linkedlist;
import java.util.LinkedList;
public class LinkedListTest {
public static void main(String[] args) {
LinkedList<String> myList = new LinkedList<String>();
myList.add("A"); //링크드 리스트에 요소 추가
myList.add("B");
myList.add("C");
System.out.println(myList); //리스트 전체 출력
myList.add(1, "D"); //링크드 리스트의 첫 번째 위치에 D 추가
System.out.println(myList);
myList.addFirst("O"); //링크드 리스트의 맨 앞에 0 추가
System.out.println(myList);
System.out.println(myList.removeLast()); //링크드 리스트 맨 뒤 요소 삭제후 해당 요소 출력
System.out.println(myList);
}
}
ArrayList로 스택과 큐 구현하기
- 스택은 상자를 쌓듯이 자료를 관리하는 방식으로 맨 나중에 추가된 데이터를 먼저 꺼내는(Last In First Out:LIFO) 방식
- 스택에 자료를 추가하는 것을 push(), 자료를 꺼내는 것을 pop() 이라고 한다.
- 큐는 대기열처럼 먼저 추가된 데이터부터 꺼내서 사용하는(First In First Out:FIFO) 방식
스택 구현하기
package collection.arraylist;
import java.util.ArrayList;
class MyStack{
private ArrayList<String> arrayStack = new ArrayList<String>();
public void push(String data) { //스택의 맨 뒤에 요소를 추가
arrayStack.add(data);
}
public String pop() { //스택의 맨 뒤에서 요소를 꺼냄
int len = arrayStack.size(); //ArrayList에 저장된 유효한 자료의 개수
if(len == 0 ) {
System.out.println("스택이 비었습니다.");
return null;
}
return(arrayStack.remove(len-1)); //맨 뒤에 있는 자료를 반환하고 배열에서 제거
}
}
public class StackTest {
public static void main(String[] args) {
MyStack stack = new MyStack();
stack.push("A");
stack.push("B");
stack.push("C");
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.pop());
}
}
큐 구현하기
package collection.arraylist;
import java.util.ArrayList;
class MyQueue{
private ArrayList<String> arrayQueue = new ArrayList<String>();
public void enQueue(String data) { //큐의 맨 뒤에 추가
arrayQueue.add(data);
}
public String deQueue() { //큐의 맨 앞에서 꺼냄
int len = arrayQueue.size();
if(len == 0 ) {
System.out.println("큐가 비었습니다.");
return null;
}
return(arrayQueue.remove(0)); //맨 앞의 자료 반환하고 배열에서 제거
}
}
public class QueueTest {
public static void main(String[] args) {
MyQueue queue = new MyQueue();
queue.enQueue("A");
queue.enQueue("B");
queue.enQueue("C");
System.out.println(queue.deQueue());
System.out.println(queue.deQueue());
System.out.println(queue.deQueue());
}
}
Collection 요소를 순회하는 Iterator
- 순서가 없는 인터페이스를 구현한 경우에 Iterator를 사용하여 요소를 순환시킨다.
public boolean removeMember(int memberId){ // 멤버 아이디를 매개변수로 해당 아이디를 가진 회원을
//ArrayList에서 찾아서 제거
Iterator<Member> ir = arrayList.iterator(); //Iterator 반환
while(ir.hasNext()) { //요소가 있는 동안
Member member = ir.next(); //다음 회원을 반환받음
int tempId = member.getMemberId();
if(tempId == memberId){ // 멤버아이디가 매개변수와 일치하면
arrayList.remove(member); // 해당 멤버를 삭제
return true; // true 반환
}
}
System.out.println(memberId + "가 존재하지 않습니다"); //for 가 끝날때 까지 return 이 안된경우
return false;
}
Set 인터페이스
- 순서와 상관없이 중복을 허용하지 않는 경우에는 Set 인터페이스를 구현한 클래스를 사용한다.
- Set 인터페이스룰 구현한 대표 클래스에는 HashSet 과 TreeSet이 있다.
HashSet 클래스
- HashSet 클래스는 집합 자료 구조를 구현하며 중복을 허용하지 않는다.
package collection.hashset;
import java.util.HashSet;
public class HashSetTest {
public static void main(String[] args) {
HashSet<String> hashSet = new HashSet<String>();
hashSet.add(new String("임정순"));
hashSet.add(new String("박현정"));
hashSet.add(new String("홍연의"));
hashSet.add(new String("강감찬"));
hashSet.add(new String("강감찬"));
System.out.println(hashSet);
}
}
중복을 허용하지 않음
HashSet 활용
package collection.hashset;
import java.util.HashSet;
import java.util.Iterator;
import collection.Member;
public class MemberHashSet {
private HashSet<Member> hashSet; //HashSet 선언
public MemberHashSet(){
hashSet = new HashSet<Member>(); //HashSet 생성
}
public void addMember(Member member){ //HashSet에 회원 추가
hashSet.add(member);
}
public boolean removeMember(int memberId){ //매개변수로 받은 회원 아이디에 해당하는 회원 삭제
Iterator<Member> ir = hashSet.iterator(); //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();
}
}
package collection.hashset;
import collection.Member;
public class MemberHashSetTest {
public static void main(String[] args) {
MemberHashSet memberHashSet = new MemberHashSet();
Member memberLee = new Member(1001, "이지원");
Member memberSon = new Member(1002, "손민국");
Member memberPark = new Member(1003, "박서훤");
memberHashSet.addMember(memberLee);
memberHashSet.addMember(memberSon);
memberHashSet.addMember(memberPark);
memberHashSet.showAllMember();
Member memberHong = new Member(1003, "홍길동"); //1003 아이디 중복
memberHashSet.addMember(memberHong);
memberHashSet.showAllMember();
}
}
TreeSet 클래스
- Tree로 시작하는 클래스는 데이터를 추가한 후 결과를 출력하면 결과 값이 정렬된다.
- TreeSet은 자료의 중복을 허용하지 않을면서 출력 결과 값을 정렬하는 클래스이다.
package collection.treeset;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
TreeSet<String> treeSet = new TreeSet<String>();
treeSet.add("홍길동");
treeSet.add("강감찬");
treeSet.add("이순신");
for(String str : treeSet) {
System.out.println(str);
}
}
}
TreeSet 활용
package collection.treeset;
import java.util.Iterator;
import java.util.TreeSet;
import collection.Member;
public class MemberTreeSet {
private TreeSet<Member> treeSet;
public MemberTreeSet(){
treeSet = new TreeSet<Member>();
}
public void addMember(Member member){ //TreeSet에 회원을 추가하는 메서드
treeSet.add(member);
}
public boolean removeMember(int memberId){ //TreeSet에서 회원을 삭제하는 메서드
Iterator<Member> ir = treeSet.iterator();
while( ir.hasNext()){
Member member = ir.next();
int tempId = member.getMemberId();
if( tempId == memberId){
treeSet.remove(member);
return true;
}
}
System.out.println(memberId + "가 존재하지 않습니다.");
return false;
}
public void showAllMember(){ //전체 회원을 출력하는 메서드
for(Member member : treeSet){
System.out.println(member);
}
System.out.println();
}
}
package collection.treeset;
import collection.Member;
public class MemberTreeSetTest {
public static void main(String[] args) {
MemberTreeSet memberTreeSet = new MemberTreeSet();
Member memberPark = new Member(1003, "박서훤");
Member memberLee = new Member(1001, "이지원");
Member memberSon = new Member(1002, "손민국");
memberTreeSet.addMember(memberLee);
memberTreeSet.addMember(memberSon);
memberTreeSet.addMember(memberPark);
memberTreeSet.showAllMember();
Member memberHong = new Member(1003, "홍길동"); //1003 아이디 중복
memberTreeSet.addMember(memberHong);
memberTreeSet.showAllMember();
}
}
위 코드를 실행해 보면 오류가 발생하게 되는데 그 이유는 TreeSet의 요소로 Member 클래스를 추가할 때 어떤 기준으로
노드를 비교하여 트리를 형성해야 하는지 구현하지 않았기 때문이다.
Comparable 인터페이스와 Comparator 인터페이스
- Comparable과 Comparator는 정렬을 구현할 수 있게 해주는 인터페이스이다.
- 정렬 방식은 정렬 기준 값이 있는 클래스에 구현하면 된다.
Comparable 인터페이스 구현
Member.java에 정렬 기준 구현
@Override
public int compareTo(Member member) { //compareTo 메서드 재정의
return (this.memberId - member.memberId); //오름차순
}
Comparator 인터페이스 구현
@Override
public int compare(Member2 mem1, Member2 mem2) { //전달받은 두 매개변수를 비교함
return mem1.getMemberId() - mem2.getMemberId();
}
package collection.treeset;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
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>(); //TreeSet 생성자의 매개변수로 정렬 방식을 지정
set.add("aaa");
set.add("ccc");
set.add("bbb");
System.out.println(set);
}
}
Map 인터페이스
- Map 인터페이스는 자료를 쌍으로 관리하는데 필요한 메서드가 정의되어 있다.
- Map 인터페이스를 구현한 클래스는 내부적으로 해시 알고리즘에 의해 구현되어 있다.
- 가장 많이 사용하는 클래스는 HashMap 클래스이다.
HashMap 클래스
package map.hashmap;
import java.util.HashMap;
import java.util.Iterator;
import collection.Member;
public class MemberHashMap {
private HashMap<Integer, Member> hashMap;
public MemberHashMap()
{
hashMap = new HashMap<Integer, Member>();
}
public void addMember(Member member){ //HashMap에 회원을 추가하는 메서드
hashMap.put(member.getMemberId(), member); //key-value 쌍으로 추가
}
public boolean removeMember(int memberId){ //HashMap에서 회원을 삭제하는 메서드
if(hashMap.containsKey(memberId)){ //HashMap에 회원을 추가하는 메서드
hashMap.remove(memberId); //해당 회원 삭제
return true;
}
System.out.println(memberId + "가 존재하지 않습니다.");
return false;
}
public void showAllMember(){ //Iterator를 사용해 전체 회원을 출력하는 메서드
Iterator<Integer> ir = hashMap.keySet().iterator();
while (ir.hasNext()){ //다음 key가 있으면
int key = ir.next(); //key 값을 가져와서
Member member = hashMap.get(key); //key로부터 value 가져오기
System.out.println(member);
}
System.out.println();
}
}
TreeMap 클래스
- Map 인터페이스를 구현한 클래스 중 key 값으로 자료를 정렬하려면 TreeMap을 사용할 수 있다.
- key 값으로 정렬하므로 key 값에 해당하는 클래스에 Comparable이나 Comparator 인터페이스를 구현해야 한다.
package map.treemap;
import java.util.Iterator;
import java.util.TreeMap;
import collection.Member;
public class MemberTreeMap {
private TreeMap<Integer, Member> treeMap;
public MemberTreeMap()
{
treeMap = new TreeMap<Integer, Member>();
}
public void addMember(Member member){
treeMap.put(member.getMemberId(), member); //key-value 쌍으로 추가
}
public boolean removeMember(int memberId){
if(treeMap.containsKey(memberId)){
treeMap.remove(memberId); //key 값에 맞는 자료 삭제
return true;
}
System.out.println(memberId + "가 존재하지 않습니다.");
return false;
}
public void showAllMember(){
Iterator<Integer> ir = treeMap.keySet().iterator();
while (ir.hasNext()){
int key = ir.next();
Member member = treeMap.get(key);
System.out.println(member);
}
System.out.println();
}
}
package map.treemap;
import collection.Member;
public class MemberTreeMapTest {
public static void main(String[] args) {
MemberTreeMap memberHashMap = new MemberTreeMap();
Member memberPark = new Member(1003, "박서훤"); //회원 아이디 순서와 상관없이 회원 추가
Member memberLee = new Member(1001, "이지원");
Member memberHong = new Member(1004, "홍길동");
Member memberSon = new Member(1002, "손민국");
memberHashMap.addMember(memberPark);
memberHashMap.addMember(memberLee);
memberHashMap.addMember(memberHong);
memberHashMap.addMember(memberSon);
memberHashMap.showAllMember();
memberHashMap.removeMember(1004);
memberHashMap.showAllMember();
}
}
'Java-Study' 카테고리의 다른 글
Java Study : 9주차 정리 - 내부 클래스, 람다식, 스트림 (0) | 2023.01.08 |
---|---|
Java Study : 8주차 - 백준 문제풀이 (0) | 2022.12.23 |
Java Study : 7주차 - 백준 문제풀이 (0) | 2022.12.23 |
Java Stduy : 7주차 정리 - 기본 클래스(Object, String, Wrapper, Class 클래스) (0) | 2022.12.23 |
Java Study : 6주차 - 백준 문제풀이 (1) | 2022.12.09 |