제네릭 프로그래밍
자바 컬렉션 프레임 워크?
자바에서 여러 자료구조, 알고리즘을 구현해둔 라이브러라
| 제네릭 프로그래밍
변수의 선언이나 메서드의 매개변수를 하나의 참조 자료형이 아닌
여러 자료형으로 변환 될 수 있도록 프로그래밍 하는 방식
실제 사용되는 참조 자료형으로의 변환은 컴파일러가
검증하므로 안정적인 프로그래밍 방식
| 자료형 매개 변수 T
여러 참조 자료형으로 대체 될 수 있는 부분을 하나의 문자로 표현
public class GenericPrinter<T> { private T material; public void setMaterial (T material) { this.material = material; } public T getMaterial( ){ return material; } } 밑줄 : 제네릭 클래스 빨간 글자 : type의 약자, 자료형 매개변수 |
type의 의미로 T 사용
| <T extends 클래스>
T 대신에 사용될 자료형을 제한하기 위해 사용
Material | |
↑ | |
↑ | ↑ |
Powder | Plastic |
Material에 정의된 메서드를 공유할 수 있음
| 자료형 매개 변수가 두 개 이상일 때
/// 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 Y gety() { return y; } } 빨간색 : 제너릭 메서드 |
| 제너릭 메서드
메서드의 매개 변수를 자료형 매개 변수로 사용하는 메서드
일반 클래스라도 제너릭 메서드를 쓸 수 있다
이때 T, V는 지역 변수와 똑같다
/// GenericMethod.java /// // x 값은 int, y값은 double // 넓이를 구하는 메서드 public class GenericMethod { public static <T,V> double makeRectangle(Point<T,V> p1, Point<T,V> p2) { // 각 x,y 좌표 값 지정 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 값 return width * height; } } 빨간색 : 제너릭 메서드 ![]() |
| 제너릭 메서드
메서드 내에서의 자료형 매개 변수는 메서드 내에서만 유효함 ( 지역 변수와 같은 개념 )
class Shape<T> { public static <T,V> double makeRectangle (Point<T,V>p1, Point<T,V>p2) { ……, } } |
Shape의 T와 makeRectangle의 T는 전혀 다른 의미
| 제너릭 메서드, 근데 매개 변수를 비워두는
GenericPrinter printer = new GenericPrinter(); // 이 경우에는 IDE에서 <T>를 지정하는게 좋을 것 같다고 경고함 // 안할 경우에는 상위 클래스인 object로 세팅됨 |
—-----------
/// Powder.java ///
package generic;
public class Powder extends Material
{
public String toString()
{
return “재료는 Powder입니다";
}
@Override
public void doPrinting()
{
System.out.println(“Powder로 프린팅합니다”);
}
}
/// Plastic.java ///
package generic;
public class Plastic extends Material
{
public String toString()
{
return “재료는 Plastic입니다";
}
@Override
public void doPrinting()
{
System.out.println(“Plastic로 프린팅합니다”);
}
}
/// Material.java ///
package generic;
public abstract class Material
{
// 기능 1 : 상위 클래스로서의 위치로 타입에 대한 제한하는 기능
// 기능 2 : 추상(abstract)을 추가하면 메서드 추가의 기능
// 왜냐하면 Generic<T>는 상위의 object 클래스의 메서드 밖에 못 쓰기 때문에
// 이것을 통해서 메서드 추가
public abstract void doPrinting();
}
/// GenericPrinter.java ///
package generic;
public class GenericPrinter<T extends material>
{
private T material;
public T getMaterial()
{
return material;
}
public T setMaterial(T material)
{
this.material = material;
}
public String toString()
{
return material.toString();
}
public void printing()
{
material.doPrinting();
}
}
/// GenericPrinterTest.java ///
package generic;
public class GenericPrinterTest
{
public static void main(String[] args)
{
// 순서 :
Generic<T>를 이용해서 생성,
BUT 변수가 생성이 안되었기 때문에 new로 생성하고
setMaterial로 생성된 변수를 세팅하는 것
// 예제 1 : Powder 타입으로 생성하는 것
GenericPrinter<Powder> powderPrinter = new GenericPrinter();
Powder powder = new Powder();
powderPrinter.setMaterial();
System.out.println(powderPrinter);
// 출력 : 재료는 Powder입니다.
// 예제 2 : Plastic 타입으로 생성하는 것
GenericPrinter<Plastic> plasticPrinter = new GenericPrinter();
Plastic plastic = new Plastic();
plasticPrinter.setMaterial();
System.out.println(plasticPrinter);
// 출력 : 재료는 Plastic입니다.
// 예제 3 : 상위 클래스 Material을 통해서 한정을 두는 것
// water 클래스는 Material을 통한 상속을 안했기 때문에
// 오류가 발생한다.
// ( 여기서는 일부러 굳이 클래스 작성하지 않음 )
// 어차피 오류 발생에 대한 예제이기 때문에
GenericPrinter<Water> waterPrinter = new GenericPrinter();
Water water = new Water();
waterPrinter.setMaterial();
System.out.println(waterPrinter);
// 예제 4 : Material 클래스에서 생성한 (추상)printing을
// GenericPrinter에서 구현하고 메서드로 실행시킴
powderPrinting.printing();
plasticPrinting.printing();
}
}
—----------------—----------------—----------------—----------------—----------------—----------------—------
—----------------—----------------—----------------—----------------—----------------—----------------—------
컬렉션 프레임워크란?
| 컬렉션 프레임 워크?
프로그램 구현에 필요한 자료구조와 알고리즘을 구현해 놓은 라이브러리
java.util 패키지에 구현되어 잇음
개발에 소요되는 시간을 절약하고 최적화된 라이브러리를 사용할 수 있음
Collection 인터페이스와 Map 인터페이스로 구성됨
| Collection 인터페이스
하나의 객체의 관리를 위해 선언된 인터페이스로
필요한 기본 메서드가 선언되어 있음
하위에 List, Set 인터페이스가 있음
Collection | |||
⬆ | |||
⬆ | ⬆ | ||
List | Set | ||
⮤ | ArrayList | ⮤ | HashSet |
⮤ | Vector | ⮤ | TreeSet |
⮤ | LinkedList | ⮤ | … |
⮤ | … |
분류 | 설명 |
List 인터페이스 | 순서가 있는 자료관리, 중복 허용이 인터페이스를 구현한 클래스는 ArrayList, Vector, LinkedList, Stack, Queue 등이 있음 |
Set 인터페이스 | 순서가 정해져 있지 않음, 중복을 허용하지 않음 이 인터페이스를 구현한 클래스는 HashSet, TreeSet등이 있음 |
| Map 인터페이스
쌍(Pair)으로 이루어진 객체를 관리하는데 필요한 여러 메서드가 선언되어 있음
Map을 사용하는 객체는 Key-Value 쌍으로 되어 있고 Key는 중복될 수 없음
( Value는 가능 )
Map | ||
⬆ | ||
⮥ | ⬆ | ⮤ |
HashTable | HashMap | TreeMap |
⬆ | ||
Properties |
| 컬렉션 프레임 워크에 앞선 자료구조 간략한 설명
배열 / Array
연속된, 선형의 자료구조
논리적 = 물리적
장점 : I번째 요소를 찾을 때 빠름(산술 연산_링크드 리스트는 하나씩 찾아가야 됌)
단점 : 고정된 길이(Fixed Length)이기 때문에 추가시 불편
0 | 1 | 2 | 3 | 4 | 5 |
링크드 리스트 / Linked List
논리적으로는 선형이지만 물리적으로는 동떨어져있는
장점 : 요소를 추가/삭제시 링크로 연결이 이루어져 있어서 빠름 (Than 배열)
그러면 무엇을 쓸지 어떻게 판단?
- 변동성이 심한 데이터 : 링크드 리스트
- 변동성이 심하지 않은 데이터 : 배열
스택 / Stack
큐 / Queue
해쉬 / Hash
검색을 위한 자료구조
hash function :
hash(key) = index
산술 연산이 되기 때문에 굉장히 빠르다 ( 시간 복잡도 : O(1) )
Key는 중복 X
이진 트리 / Binary Tree & 이진 탐색 트리 / BST (Binary Search Tree) )
부모(Parent)의 하위(Child)에 자식 클래스가 2개보다 작거나 같은
—------—------—------—------—------—------—------—------—------—------—------—------—------—---
—------—------—------—------—------—------—------—------—------—------—------—------—------—---
List 인터페이스
| List 인터페이스
Collection 하위 인터페이스
객체를 순서에 따라 저장하고 관리하는데 필요한 메서드가 선언된 인터페이스
배열의 기능을 구현하기 위한 메서드가 선언됨
ArrayList, Vector, LinkedList
| ArrayList와 Vector
객체 배열 클래스
Vector는 자바 2부터 제공된 클래스
일반적으로 ArrayList를 더 많이 사용
Vector는 멀티 스레드 프로그램에서 동기화를 지원
동기화(Synchronization) : 두개의 스레드가 동시에 하나의 리소스에
접근할 때 순서를 맞추어서 데이터의 오류가 방지하지 않도록 함
capacity와 size는 다른 의미이다
capacity는 배열의 용량 / size는 배열에 요소가 몇개가 들어가 있느냐
| ArrayList와 LinkedList
둘 다 자료의 순차적 구조를 구현한 클래스
ArrayList는 배열을 구현한 클래스로 논리적 순서와 물리적 순서가 동일함
LinkedList는 논리적으로 순차적인 구조지만,
물리적으로는 순차적이지 않을 수 있음
LinkedList 구조
LinkedList에서 자료의 추가와 삭제
추가
삭제
/// Member.java ///
package collection;
public class Member
{
public int memberId;
private String memberName;
public Member(){}
public Member(int memberId, String memberName)
{
this.memberId = memberId;
this.memberName = memberName;
}
public int getMemberId()
{
return memberId;
}
public int setMemberId()
{
this.memberId = memberId;
}
public int getMemberName()
{
return memberName;
}
public int setMemberName()
{
this.memberName = memberName;
}
public String toString()
{
return memberName + “회원님의 아이디는" + memberId + “입니다”;
}
}
/// LinkedList.java ///
package collection;
import java.util.LinkedList;
public class LinkedListTest
{
public static void main(String[] args)
{
LinkedList<String> myList = new LinkedList<String>();
// LinkedList에서 제공하는 기본 메서드 중 하나인 add를 사용
myList.add(“A”);
myList.add(“A”);
myList.add(“C”);
System.out.println(myList);
// 출력 : [ A, B, C ]
// 데이터를 추가할 때 편한 LL
// xxList.add(index, “element”)
myList.add(1,”D”)
// 출력 : [ A, D, B, C ]
// 데이터를 삭제하는 다양한 메서드가 있다
myList.removeLast();
// 출력 : [ A, D, B ]
//
for( int i=0; i<myList.size(); i++;)
{
String s = myList.get(i)
System.out.println(s);
}
// 출력 : A D B
}
}
// 대부분의 Collection은 toString()을 제공한다
—----------------—----------------—----------------—----------------—----------------—----------------—------
—----------------—----------------—----------------—----------------—----------------—----------------—------
Stack과 Queue 구현하기
Stack은 제공되고 있고, Queue는 ArrayList를 이용해서 많이 사용한다
BUT 강의에서는 Stack/Queue를 직접 구현해 볼 것이다.
| Stack 구현
Last In First Out (LIFO) : 맨 마지막에 추가 된 요소가 가장 먼저 꺼내지는 자료구조
이미 구현된 클래스가 제공됨
ArrayList나 LinkedList로 구현 할 수 있음
게임에서 무르기, 최근 자료 가져오기 등에 구현
| Queue 구현하기
First In First Out (FIFO) : 먼저 저장된 자료가 먼저 꺼내지는 자료구조
선착순, 대기열등을 구현할 때 가장 많이 사용되는 자료구조
ArrayList나 LinkedList로 구현 할 수 있음
/// StackTest.java ///
package collection;
import java.util.ArrayList;
class MyStack
{
private ArrayList<String> arrayStack = newArrayList<String>();
public void push(String data)
{
arrayStack.add(data);
}
public void pop()
{
int len = arrayStack.size();
if ( len == 0 )
{
System.out.println(“스택이 비었습니다”);
return;
}
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());
System.out.println(stack.pop());
// 출력 : C B A 스택이 비었습니다
}
}
—------—------—------—------—------—------—------—------—------—------—------—------—------—---
—------—------—------—------—------—------—------—------—------—------—------—------—------—---
Set 인터페이스 1&2
Set 인터페이스 1 >
| Iterator로 순회하기
Collection의 개체를 순회하는 인터페이스
iterator() 메서드 호출
Iterator ir = memberArrayList.iterator(); |
iterator에 선언된 메서드
메서드 | 설명 |
booleanhashNext() | 이후에 요소가 더 있는지를 체크하는 메서드이며, 요소가 있다면 true를 반환합니다. |
E next() | 다음에 있는 요소를 반환합니다 |
| Set 인터페이스
Collection 하위의 인터페이스
중복을 허용하지 않음
List는 순서기반의 인터페이스지만, Set은 순서가 없음
get(i) 메서드가 제공되지 않음 (Iterator로 순회)
저장된 순서와 출력순서는 다를 수 있음
아이디, 주민번호, 사번 등 유일한 값이나 객체를 관리할 때 사용
HashSet, TreeSet 클래스
| HashSet 클래스
Set 인터페이스를 구현한 클래스
중복을 허용하지 않으므로 저장되는 객체의 동일함 여부를 알기 위해
equals()와 hashCode() 메서드를 재정의 해야 함
/// HashSetTest.java ///
package collection.set;
import java.util.HashSet;
public class HashSetTest
{
public static void main(String[] args)
{
set.add(“이순신”);
set.add(“김유신”);
set.add(“강감찬”);
set.add(“이순신”);
System.out.println(set);
// 출력 : [ 김유신, 강감찬, 이순신 ]
// 설명 : 우선 같은 요소는 중복이 안된다.
// 또한 순서대로 출력 X, 내부적으로 Hash // 방식으로 관리된다
// if ArrayList라면?
// 출력 : [ 이순신, 김유신, 강감찬, 이순신 ]
// 설명 : 중복 허용
Iterator<String> ir = set.iterator();
while(ir.hashNext())
{
String str = ir.next();
System.out.println(str);
}
// 출력 : 김유신 강감찬 이순신
}
}
/// MemberHashSet.java ///
package collection.set;
import java.util.Hashset;
import.java.util.Iterator;
public class MemberHashSet
{
private HashSet<Member> hashSet;
public MemberHashSet()
{
hashSet = new HashSet<Member>();
}
public void addNumber(Member member)
{
hashSet.add(member);
}
public boolean removeMember(int memberId)
{
iterator<Member> ir = hashSet.iterator();
while(ir.hashNext())
{
Member member = ir.next();
if( member.getMemberId() = 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 ///
package collection.set;
public class MemberHashSetTest
{
public static void main(String[] args)
{
MemberHashSet manager = new MemberHashSet();
Member memberLee = new Member(100, “Lee”);
Member memberKim = new Member(200, “Kim”);
Member memberPark = new Member(300, “Park”);
manager.addMember(memberLee);
manager.addMember(memberKim);
manager.addMember(memberPark);
manager.showAllMember();
// 출력 :
// Park회원님의 아이디는 300입니다
// Lee회원님의 아이디는 100입니다
// Kim회원님의 아이디는 200입니다
manager.removeMember(100);
manager.showAllMember();
// 출력 :
// Park회원님의 아이디는 300입니다
// Kim회원님의 아이디는 200입니다
// +예제
Member memberPark2 = new Member(300, “Park2”)
// 원래 hashSet에서는 중복이 안되지만 그것은 String을 사용했을 때
// JDK에서는 이미 무엇이 중복인지에 대한 구현이 되어있기 때문이다.
// 고로 Member도 무엇이 중복인지에 대한 구현을 해야 됌
// Member 클래스에서 equals(), hashCode() 재정의(Override)
// 물론 기존에 있는 클래스는 괜찮다 ( int, string등 )
}
}
/// Member.java ///
// equals(), hashCode()에 대한 재정의만 여기서 함
@override
public int hashCode()
{
return memberId;
}
@override
public boolean equals(Object obj)
{
if (obj instanceof Member)
{
Member member = (Member)obj;
return(this.memberId==member.memberId);
}
return false;
}
Set 인터페이스 2 >
| TreeSet 클래스
객체의 정렬에 사용되는 클래스
중복을 허용하지 않으면서 오름차순이나 내림차순으로 객체를 정렬함
내부적으로 이진 검색 트리(binary search tree)로 구현되어 있음
이진 검색 트리에 자료가 저장될 때 비교하여 저장될 위치를 정함
객체 비교를 위해 Comparable이나 Comparator 인터페이스를 구현해야 함
| Comparable 인터페이스와 Comparator 인터페이스
정렬 대상이 되는 클래스가 구현해야 하는 인터페이스
Comparable은 compareTo() 메서드를 구현
매개 변수와 객체 자신(this)를 비교
Comparator는 compare() 메서드를 구현
두 개의 매개 변수를 비교
TreeSet 생성자에 Comparator가 구현된 객체를 매개변수로 전달
TreeSet<Member> treeSet = new TreeSet<Member>(new Member()); |
일반적으로 Comparable을 더 많이 사용
이미 Comparable이 구현된 경우 Comparator를 이용하여
다른 정렬 방식을 정의할 수 있음
/// TreeSetTest.java ///
package collection.treeset;
import java.util.TreeSet;
public class TreeSetTest
{
public static void(String[] args)
{
TreeSet<String> treeSet = new TreeSet<String>();
treeSet.add(“홍길동”);
treeSet.add(“강감찬”);
treeSet.add(“이순신”);
for ( String str : treeSet )
{
System.out.println(str);
// 출력 : 강감찬 이순신 홍길동
}
}
}
/// Member.java ///
/// MemberTreeSet.java ///
/// MemberTreeSetTest.java ///
이 3가지 java 파일 ( 기존 HashSet )들의 HashTest를 TreeSet으로만
클래스명, 객체명을 바꿔준다.
왜냐하면 거의 같은 구조를 쓰기 때문이다.
/// MemberTreeSetTest.java ///
~~
public static void main(String[] args)
{
MemberTreeSet manager = new MemberTreeSet();
Member memberPark = new Member(300, “Lee”);
Member memberLee = new Member(100, “Kim”);
Member memberKim = new Member(200, “Park”);
manager.addMember(memberLee);
manager.addMember(memberKim);
manager.addMember(memberPark);
manager.showAllMember();
// 출력 : error > Comparable이 구현이 안되었다.
// int, string 객체 같은 기본 객체랑 다르게
// member 객체는 Comparable에 대한 구현 요망
~~
// Member.java ///
~~
public class Member implements Comparable
{
~~
~~
~~
// implements Comparable일 때
@override
public int compareTo(Member member)
{
// 정렬 기준 : Number
return (this.memberId - member.memberId);
// 출력 : 오름차순으로 정렬이 된다 ( this 값 - 매개 변수 값 = 양수일 때 )
return (this.memberId - member.memberId) * (-1);
// 출력 : 내림차순으로 정렬이 된다 ( this 값 - 매개 변수 값 = 음수일 때 )
// 정렬 기준 : String
return this.memberName.compareTo(member.getMemberName());
}
// implements Comparator<Member>일 때
@override
public int compare(Member member1, Member member 2)
{
// 정렬 기준 : Number
return (memberId.member1 - memberId.member2);
// 출력 : 오름차순으로 정렬이 된다 ( this 값 - 매개 변수 값 = 양수일 때 )
return (memberId.member1 - memberId.member2) * (-1);
// 출력 : 내림차순으로 정렬이 된다 ( this 값 - 매개 변수 값 = 음수일 때 )
}
// member1이 this, member2가 받는 매개변수
// public MemberTreeSet(){}에 new Member()를 써야함
~~
}
// CompareTest.java ///
package collection.treeset;
import java.util.Comparator;
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)
{
TreeSet<String> treeSet = new TreeSet<String>(new MyCompare);
treeSet.add(“홍길동”);
treeSet.add(“강감찬”);
treeSet.add(“이순신”);
for ( String str : treeSet )
{
System.out.println(str);
// 출력 : 강감찬 이순신 홍길동
// new MyCompare로 바꾼 후에는 반대로(내림차순) 출력됌
}
}
}
—------—------—------—------—------—------—------—------—------—------—------—------—------—---
—------—------—------—------—------—------—------—------—------—------—------—------—------—---
Map 인터페이스
| Map 인터페이스
Key-value pair의 객체를 관리하는데 필요한 메서드가 정의됨
Key는 중복될 수 없음
검색을 위한 자료 구조
Key를 이용하여 값을 저장하거나 검색, 삭제할 때 사용하면 편리함
내부적으로 hash 방식으로 구현됨
index = hash(key) // index는 저장 위치 |
key가 되는 객체는 객체의 유일성함의 여부를 알기 위해
equals()와 hashCode() 메서드를 재정의 함
| HashMap 클래스
Map 인터페이스를 구현한 클래스 중 가장 일반적으로 사용하는 클래스
HashTable 클래스는 자바2부터 제공된 클래스로 Vector처럼 동기화를 제공함
pair 자료를 쉽고 빠르게 관리할 수 있음
| TreeMap 클래스
Key 객체를 정렬하여 Key-value를 pair로 관리하는 클래스
Key에 사용되는 클래스에 Comparable, Comparator 인터페이스를 구현
java에 많은 클래스들은 이미 Comparable이 구현되어 있음
구현된 클래스를 Key로 사용하는 경우는 구현할 필요 없음
public final class Integer extends Number implements Comparable<integer> { … public int compareTo(Integer anotherInteger) { return compare(this.value, anotherInteger.value); } } |
이 정리 내용은 패스트 캠퍼스, K-Digital Credit Java 강의를 참고했습니다