자바(java)_제너릭, 컬렉션프레임워크(Collection Framework), ArrayList, HashMap
▶제너릭이란?
1
2
3
4
5
6
7
8
|
Class person<T>{
public T info;
}
public class GenericDemo{
public static void main(String[] args){
person<String> p1 = new person <String>();
person<StringBuilder> p2 = new person<StringBuilder>();
|
cs |
위의 예시를 따라 설명을 해주자면
이 T라는 문자가 있는 자리는 info라는 필드의 데이터타입이다.
제너릭은 Class person뒤의 마치 매게변수처럼 놓여있는 <> 에서부터 시작이 된다.
제너릭은 person이라는 class가 실제로 실행이 될 때 실행되는데 main 메서드에서 person<>안에 String이라는 값을 넣어줌으로서 위의 Class person<T>는 Class person<Stirng>이 되고 이에, T는 String이 되어 info 역시 자연스럽게 String타입으로 변한다.
그렇게 p1도 인스턴스의 데이터타입 String으로 변하게되고 p1과 info의 데이터타입이 동일해진다.
p2도 살펴보면 StringBuilder라는 데이터타입을 넣어두었는데 이 역시 p1과 동일한 방법으로 데이터가 옮겨지고 p2와 info는 StringBuilder라는 같은 데이터타입을 공유하게된다.
즉, 제너릭이란 클래스 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법을 의미한다.
▶컬렉션 프레임워크(Collexction Framwork)
자바 Util 패키지에는 자료를 다룰 수 있는 자료구조 클래스가 다수 존재한다.
자료구조는 말 그대로 자료를 저장할 수 있는 구조를 말하는데 이런 저장에 대한 효율적인 방법을 제공하는 기능을 컬렉션 프레임 워크라고한다. 다른말로는 컨테이너로 즉, 값을 담는 그릇이라는 의미이다.
그런데 그 값의 성격에 따라서 컨테이너의 성격이 달라진다.
컨테이너의 구조를 먼저 알아보자.
Collection과 Map이라고 하는 상위 카테고리밑에 여러 하위카테고리들이 위치하고있다.
Set으로 먼저 살펴보자면 Set은 중복 저장을 하지 않는 하나의 기능의 성격이다.
즉, Set은 이 하위 카테고리들의 공통 성격이라고 볼 수 있으며 HashSet, LinkedHashSet, TreeSet은 Set이라는 공통된 성격을 공유하는 비슷한 기능이라는 것이다.
각각의 카테고리에따라서 데이터를 저장하고 가져오는 방식이 모두 다르기에 우리는 이 전체적인 그림을 참고하여 업무에 따라 적당한 컬렉션을 사용해야한다.
만약 관리해야하는 데이터에 무한한 값을 담을 수 있는 배열이 필요하다면 List 카테고리에 있는 기능들을 사용하고 중복저장이 되지 않는 기능이 필요하다면 Set 카테고리안의 기능들을 사용할 수 있다.
여기서 가장 많이 사용되는 것은 Map의 HashMap과 Collection의 ArrayList이다.
다른것들은 거의 사용하지 않기에 이 두가지를 중점으로 살펴보자.
▶ArrayList 클래스
배열은 저장공간을 개발자가 "2"로 지정을 해주면 2를 초과해선 데이터를 입력을 할 순 없다. 그래서 개발자가 배열의 저장 한계점을 알지 못하면 계속된 자바 예외처리 오류가 발생되고 데이터의 양이 많을 경우 이를 찾아내기 어려워 몹시 번거롭다고 할 수 있다.
ArrayList는 이 단점을 보완하는 컬렉션 프레임워크라고 하는 기능 중 하나로 데이터를 무제한으로 보관할 수 있다.
특징은 3가지이다.
1. ArrayList에 데이터가 추가됨과 동시에 자동&순차적으로 배열과 같은 인덱스 번호를 부여받는다.
2. 배열과 쓰임이 비슷하지만 생김새가 다르고
3. 자바 Util arraylist를 반드시 import 시켜주어야한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public static void main(String[]args){
//Array
String[] arrayObj = new String[2]; arrayObj[0] = "one";
arrayObj[1] = "two";
//arrayObj[2] = "tree" // 오류발생
//ArrayList ArrayList al = new ArrayList();
al.add("one");
a1.add("two");
al.add("tree");
for(int i = 0; i<al.size(); i++){
System.out.println(al.get(i));
|
cs |
위의 예시처럼 ArrayList는 값을 추가할 때 add라는 메서드를 사용한다.
또한 값을 가져올 때도 get을 사용하여 저장한 값을 가져올 수 있다.
그런데 코드를 조금 바꾸어
1
2
3
4
5
6
7
8
9
10
|
ArrayList al = new ArrayList();
al.add("one");
a1.add("two");
al.add("tree");
for(int i = 0; i<al.size(); i++){
String value = al.get(i);
System.out.println(value);
|
cs |
get(i)의 값을 String타입의 value에 저장하고 그 value를 출력시키면 오류가 발생한다.
이유가 뭘까
우리가 ArrayList에 추가한 값은 "one"과 "two"라는 String타입이다. 그럼 가지고 오는 타입 역시 당연하게 String타입일 것이다.
그러나 우리는 ArrayList 메소드는 어떠한 데이터 타입도 출력할 수 있는 메서드임을 알아야한다.
그 말은 즉, add라고 하는 메서드의 인자값이 object라는 것이다.
objcet는 데이터타입의 조상으로서 "one"이라고 하는 String타입의 데이터가 ArrayList에 저장되기 위해선 String타입으로 그대로 저장되는 것이 아닌 object 타입으로 저장된다.
그래서 String타입인 Value라는 값에 objcet 타입을 담지 못해 에러가 나는 것이다.
이런 에러를 방지하기 위해선 get의 리턴값에 String으로 형변환을 시켜주어야한다.
즉, arrylist를 사용을 할 땐 그 값을 원래의 값으로 형변환을 해주어야하는 것을 알고있어야한다는 것이다.
그래서 String Value = (String)al.get(i) 로 설정을 해주면 정상적으로 값이 Value에 담기게된다.
그러나 이 방법은 옛날 방법이고 타입의 안정성이 떨어지기에 컬랙션 프레임 워크가 제너릭이라는 문법적 수단을 선택하였다.
List<저장할 데이터의 클래스 이름> list = new ArrayList<저장할 데이터의 클래스 이름>();
ex) ArrayList<String> al = new ArrayList<String>();
이는 객체를 생성할 때 처음부터 al을통해 추가되는 값이 String이라는 것을 제너릭을 통해 지정을 해 주는 것이고 그럼 형변환을 강제로 할 필요가 없다는 것을 의미한다.
이것이 가장 기본적인 ArrayList의 형식이며 이렇게 사용함을 암기한다.
▶ArrayList 사용방법
ArrayList는 객체만 사용 가능하기떄문에 자료형에는 클래스 타입만 가능하다.
기본 데이터형을 사용하고자 하는 경우에는 해당 데이터의 WrapperClass를 사용해야한다.
데이터를 추가하는 방법이다.
따로 배열 저장공간을 지정해주지 않아도 자동으로 총 9개의 저장공간이 생성이 될 것이다.
size()를 통해 데이터 크기를 확인하였고 get()을 통해 값을 꺼내오고 remove를 통해 값을 삭제하였다.
그런데 5번째 요소의 50이란 값을 삭제를 하고 데이터크기가 9에서 하나가 준 8이되는 것 까지 확인을 한 다음 다시 5번째 요소의 값에 데이터를 넣으니 5번째 요소의 값이 60이 나왔다.
이유는 5번째값 즉, 4번방을 삭제를 했을 때 뒤의 5번방(60), 6번방(70), 7번방(80)의 데이터들이 한칸씩 앞으로 와서 중간에 빈 공간이 없도록 채워준 것이다.
그래서 5번째 자리에 데이터를 추가하고싶다면
이렇게 add를 사용하여 방 번호까지 넣어서 데이터를 대입해주어야한다.
그리고 출력을 하면 123이란 값이 4번쨰방에 저장이 된 것을 알 수있다.
이렇게 clear를 통해 전체삭제도 가능하다.
Array 리스트는 Interger로 표현을 했다.
이는 객체를 넣을 수 있다는 의미고 Class로 Array 리스트를 만들 수 있음을 의미한다.
▶ArrayList를 활용한 자바빈즈
people이라고 하는 사람을 관리를 하고 싶어 people 클래스를 하나 만들어 주었다.
[people클래스]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
package collection;
// 주소록 데이터를 표현하기 위한 java Beans
public class People {
private String name;
private String phoneNo;
// 생성자
public People(String name, String phoneNo) {
super();
this.name = name;
this.phoneNo = phoneNo;
}
// getter, setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhoneNo() {
return phoneNo;
}
public void setPhoneNo(String phoneNo) {
this.phoneNo = phoneNo;
}
// toSting()
@Override
public String toString() {
return "People [name=" + name + ", phoneNo=" + phoneNo + "]";
}
|
cs |
[Main클래스]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
package collection;
import java.util.ArrayList;
import java.util.List;
public class Main03 {
public static void main(String[] args) {
// People class로 ArrayList 생성
/*
* List를 사용하는 가장 일반적인 방법은,
* 사용자 정의 클래스에 대한 객체를 포함하도록
* 지정하는 것이다.
*/
List<People> plist = new ArrayList<People>();
// 10명의 데이터를 임의로 추가
// plist.add( new People("회원1", "010-1234-5670") );
// plist.add( new People("회원2", "010-1234-5671") );
// plist.add( new People("회원3", "010-1234-5672") );
// plist.add( new People("회원4", "010-1234-5673") );
// plist.add( new People("회원5", "010-1234-5674") );
// plist.add( new People("회원6", "010-1234-5675") );
// plist.add( new People("회원7", "010-1234-5676") );
// plist.add( new People("회원8", "010-1234-5677") );
// plist.add( new People("회원9", "010-1234-5678") );
// plist.add( new People("회원10", "010-1234-5679") );
for( int i = 0; i<10; i++ ) {
People p = new People("회원" + i, "010-1234-567" + i);
plist.add(p);
// plist.add( new People("회원" + i, "010-1234-567" + i) );
}
// 출력하기
for( int i = 0; i<plist.size(); i++ ) {
// List에 저장된 데이터를 하나 꺼내면,
// People 클래스 형의 객체다
People item = plist.get(i);
System.out.println( plist.get(i) );
System.out.println( item.toString() );
System.out.println("----------------");
}
}
}
|
cs |
people이라는 클래스를 자바빈즈와 ArrayList를 사용하여 main에서 손쉽게 관리하기 위한 코드이다.
우린 회원들의 이름과 나이를 효율적으로 관리하고 정리하기위해 나이와 휴대폰 번호를 people이라는 공통클래스에 정리하였고 main에서 객체를 생성하여 간단하게 값을 추가할 수 있다.
하지만 값을 많이 추가해야한다면? 이는 더이상 간단한일이 아닐 것이다.
위와같은 예제를 보면 회원수가 10명일경우에 우린 객체를 List plist = new List();로 생성하여
이렇게 값을 하나씩 모두 넣어줘야한다.
고작 10명인데도 비효율적으로 보이는데 만약 100명, 1000명이라면? 이걸 모두 입력하는 것은 불필요한 일이다.
객체를 사용하면서 반복된 값을 넣어줄 경우에 이를 좀 더 효율적으로 하기 위해 ArrayList와 반복문을 사용한다.
위와같은 for문을 사용하기 위해 ArrayList를 사용하기 위한 첫단계 List<People> plist = new ArrayList<people>(); 을 작성하고 i를 이용한 배열을 사용하여 반복문을 돌려준다.
ArrayList 객체생성만 제대로 해주면 반복문은 기존 반복문의 형태와 동일하다.
반복문을 사용하여 값을 꺼내는 것도 가능하다.
출력을 하면 값이 이렇게 나오는데 첫번째가 plist.get(i), 2번째가 toString()으로 값을 뽑아낸것이다. 두 방법 모두 값을 출력하는 메서드로 원하는 메서드를 사용하면 되나 toString은 개발자가 출력 형식을 개별설정할 수 있는 장점이 있다.
▶HashMap 클래스
List는 하나의 배열을 의미하지만 HashMap은 두개의 저장 공간(Key,Value)에 데이터가 저장이 되는 것을 뜻한다.
Key라는 저장공간에는 "one"이라는 String으로 값을 주고 value에는 1이란 int형 타입으로 저장이 되게한다.
그리고 출력을 할 때 "one"을 입력하면 1이 출력이 되고 "two"를 입력하면 2가 출력이 되는 것이 Map이다.
이는 HashMap 클래스는 Map 인터페이스를 상속받는 '자료보관소' 이기 때문에, 암묵적 객체 형변환의 규칙에 따라 Map 형태로 선언하고 HashMap 형태로 할당한다.
배열은 원치않지만 index라고 하는 방의 넘버링이 정해졌다면 이 배열은 각각의 방의 이름을 내가 직접 정해주는 것이라고 생각하면된다.
단, 여기서 주의할 점은 역시나 중복이 불가능하다는 것인데 특이한 것은 Key는 중복이 되지 않지만 Value는 중복이 가능하다는 것이다.
만약 여기에 Key에 "one" Value에 200을 입력한다면?
기존의 Key는 중복이 불가능하기에 기존의 one은 사라지고 새 one으로 교체되어 one,200이란 값이 one,1의 값을 삭제시키고 존재하게된다.
HashMap <String, 저장할 데이터의 클래스 이름> data = new HashMap<String, 저장할 데이터의 클래스 이름>();
HashMap의 기본 형식이다.
new HashMap<String, 저장할 데이터의 클래스 이름> 을 보면 2개의 데이터타입을 저장하는 공간이 있는 것을 확인할 수 있다.
첫번째는 key 두번쨰는 Value값을 저장하는 것으로 각각 데이터타입을 다르게 지정한다.
▶HashMap 클래스 사용방법
HashMap 사용방법이다.
HashMap과 Map 둘 다 import를 꼭 해주어야하 실행이 가능하며,
값의 이름과 값의 종류를 설정해준다.
데이터를 추가하는 방법이다.
수학이라는 값이 2번 저장되어있는 것이 확인되는데 HashMap의 Key 부분은 중복을 허락하지 않는다. 그래서 같은 수학이란 이름으로 값이 등록이 되어 자바는 중복된 값이라 인식하고 기존에 저장된 데이터를 덮어 씌워서 수학이라는 데이터에는 최종적으로 100점이 저장이된다.
또 중복저장이 안된다는 점을 응용하여 만약 이 학생이 영어를 시험친 줄 알았는데 시험을 치지않았을 경우엔 어떻게 값을 넣을까?
이렇게 null을 사용하여 표기해준다.
객체를 사용하는 것이기에 null이 사용 가능한 부분이다.
그래서 영어 역시 null이라는 값으로 덮어씌워진다.
hm.size를 통해 hm배열의 저장 공간을 확인할 수 있는데 지금 값은 5번 넣었지만 수학과 영어 중복으로 체크가 되어있기에 최종적으론 3개만 저장됨을 확인할 수 있다.
국어와 수학 영어라는 key 를 통해 Value값을 가져왔다.
Key는 Value를 가져오기 위한 하나의 열쇠인것이다.
또한 hm.get을 통해 데이터를 꺼낼 수 있는데 확인해보면 마지막으로 저장한 값들이 배열에 저장된 되었고 HashMap은 중복을 허락하지않는 다는 것을 한번 더 확인할 수 다.
▶HashMap 클래스 반복문
HashMap의 반복문을 사용하는 방법은 2가지이다.
첫번째는 ForEach를 사용한 반복문이다.
1
2
3
4
5
6
7
8
9
10
11
12
|
public static void main(String[] args){
HashMap<String, Integer> a = new HashMap<String, Integer>();
a.put("one",1);
a.put("two",2);
iteratorUsingForEach(a);
static void interatorUsingForEach(HashMap map){
set<Map.Entry<String,Integer>> entries = map.entrySet();
for(Map.Entry<String,Integer> entry: entries){
System.out.println(entry.getKey() + " : " + entry.getValue());
|
cs |
HashMap 에 데이터가 2개가 아닌 수많은 데이터가 들어가있다고 가정을 하고 반복문을 만들어보았다.
반복문은 우선 iteratorUsingForEach(a)를 통해 먼저 시작을 하게되는데 우선 여기에 a라는 값을 저장을 한 후 이와 동일한 이름의 메서드를 만들어준다.
그리고 (HashMap map)의 map안으로 a라는 값이 들어오게된다.
그리고 바로 밑 코드를 보면 map.entrySet()이라는 메서드가 보이는데 map을 통해 a가 entrySet() 이란 메서드로 값이 이동되게된다.
Set은 우리가 알고있는 값을 가지고 오는 그 Set으로 같은 행 맨 앞에 있는 Set으로 a의 값을 넘기게되고 Set은 그 값을 다시 : 뒤의 entries로 넘기고 entries는 entry로 값을 넘긴다.
최종적으로 a의 값은 entry가 가지게되고 이를 getKey()와 getValue()를 통해 뽑아낸다.
getKey()와 getValue()의 값은 Map.Entry<String,Integer>를 통해 a에 있는 데이터 값 중 동일한 데이터값을 뽑아낼 수 있는 것이다.
엄청 복잡하게 데이터가 이동된다.
두번째 방법은 Iterator를 이용하는 것이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public static void main(String[] args){
Hash<String, Integer> a = new HashMap<String, Integer>();
a.put("one",1);
a.put("two",2);
iteratorUsingIterator(a);
static void iteratorUsingIterator(HashMap map){
Set<Map.Entry<String,Integer>> entries = map.entrySet();
Iterator<Map.Entry<String,Integer>> i = entries.iterator();
While (i.hasNext()){
Map.Entry<String, Integer> entry = i.next();
System.out.println(entry.getkey() + " : " + entry.getValue());
}
|
cs |
iterable(이터러블)객체는 배열을 일반화한 객체로 이터러블이라는 개념을 사용하면 어떤 객체에든 반복문을 적용을 할 수 있다.
위와 동일하게 a는 map으로 데이터가 옮겨지고 map은 entries로 entries는 iterator()로 데이터를 전달했다.
iterator를 통해 반복을 얻어내고 그 값을 i에 담는다.
그리고 while문을 통해 반복문을 돌리는데 여기에 Next를 하게되면 iterator()의 값을 하나하나 꺼내게 되는데 그 값은 Map.Entry를 가지는 것으로 String과 Integer의 데이터타입에따라 getKey()와 getValue()가 반응하여 반복문이 돌아갈 수 있는 것이다.