컬렉션 프레임워크란?
- 배열을 사용하다 보면 여러가지 비효율적인 문제가 생긴다. 가장 큰 문제점은 크기가 고정적이라는 것이다.
- 배열의 크기는 생성할 때 결정되며 그 크기를 넘어가게 되면 더 이상 데이터를 저장할 수 없다.
또 데이터를 삭제하면 해당 인덱스의 데이터는 비어있는 메모리 낭비되는 등 여러 문제점들 발생한다. - 그렇기에 자바는 배열의 이러한 문제점을 해결하기 위해, 널리 알려져 있는 자료자료를 바탕으로 객체나 데이터들을 효율적으로 관리(추가, 삭제, 검색, 저장) 할 수 있는 자료구조들을 만들어 놓았다.
이러한 자료구조들이 있는 라이브러리를 컬렉션 프레임워크라고한다. - 컬렉션 프레임워크는 자바의 인터페이스(interface)를 사용하여 구현된다.
컬렉션 프레임워크 주요 인터페이스
- List 인터페이스
- Set 인테페이스
- Map 인터페이스
List와 Set은 모두 Collection 인터페이스를 상속받지만, 구조상의 차이로 인해 Map 인터페이스는 별도 정의
Collection을 구현한 클래스 및 인터페이스들은 모두 java.util 패키지에 있다
주요 인터페이스의 간략한 특징
인터페이스 | 설명 | 구현클래스 |
List<E> | 순서가 있는 데이터 집합 데이터 중복 허용함 |
Vector, ArrayList, LinkedList,Stack,Queue |
Set<E> | 순서가 없는 데이터 집합 데이터의 중복 허용하지 않음 |
HashSet,TreeSet |
Map<K,V> | 키와 값이 한 쌍으로 이루어진 데이터의 집합 순서가 없음 키는 중복을 허용하지 않고, 값은 중복허용 |
HashMap, TreeMap,HashTable,Properties |
List 컬렉션
- List 컬렉션은 객체를 일렬로 늘어놓은 구조를 가지고 있다.
- 선형 자료구조로 주로 순서가 있는 데이터를 목록으로 이용할 수 있도록 만들어진 인터페이스
- 객체를 인덱스로 관리하기 때문에 객체를 저장하면 자동 인덱스가 부여되고 인덱스로 객체를 검색, 삭제할 수 있는 기능 제공한다.
- List를 통해 구현된 클래스들은 '동적 크기'를 갖으며 배열처럼 사용할 수 있게 되었습니다.
한마디로 배열의 기능 + 동적 크기 할당이 합쳐져 있다고 보면 됩니다.
- List컬렉션은 객체 자체를 저장하는 것이 아니라 위와 같이 객체의 번지 참조
- 동일한 객체를 중복 저장할 수 있는데 이 경우 동일한 번지가 참조된다.
- null 저장도 가능한데, 이 경우 해당 인덱스는 객체를 참조하지 않는다.
- List 컬렉션을 구현하는 대표적인 클래스 : ArrayList, LinkedList, Vector
세 가지 클래스는 List 인터페이스를 같이 상속하므로 공통적으로 사용할 수 있는 메소드들이 많다.
List Interface를 선언된 대표적인 메소드
메소드 | 리턴타입 | 설명 |
add(E e) | boolean | 요소를 추가합니다. |
remove(Object o) | boolean | 지정한 객체와 같은 첫 번째 객체를 삭제합니다. |
contains(Object o) | boolean | 지정한 객체가 컬렉션에 있는지 확인합니다. 있을 경우 true, 없을 경우 false 반환 |
size() | int | 현재 컬렉션에 있는 요소 개수를 반환 |
get(int index) | E | 지정된 위치에 저장된 원소 반환 |
set(int index, E elements) | E | 지정된 위치에 있는 요소를 지정된 요소로 바꿉니다. |
isEmpty() | boolean | 현재 컬렉션에 요소가 없다면 true, 요소가 존재 한다면 false 반환 |
equals(Object o) | boolean | 저장된 객체와 같은지 비교 |
indexOf(Object o) | int | 지정된 객체가 있는 첫 번째 요소의 위치를 반환합니다. 만일 없을 경우 -1 반환합니다. |
clear() | void | 모든 요소들을 제거합니다. |
List<String> list = ...; //list 객체 생성
list.add("홍길동");//맨끝에 객체 추가
list.add(1, "홍길동");// 지정된 인덱스에 객체 삽입
String str = list.get(1);//인덱스로 객체 찾기
list.remove(0);//인덱스로 객체 삭제
list.remove("홍길동");//객체 삭제
ArrayList란?
- ArrayList는 List 인터페이스를 상속받은 클래스로 크기 가변적으로 변하는 선형 리스트
- 일반적인 배열과 같은 순차리스트이며 인덱스로 내부의 객체를 관리한다는 점 등이 유사하지만
한 번 생성되면 크기가 변하지 않는 배열과는 달리 ArrayList는 객체들이 추가되어 저장 용량을 초과한다면 자동으로 부족한 크기 만큼 저장 용량이 늘어난다는 특징있다 - Object[] 배열을 사용하면서 내부 구현을 통해 동적으로 관리를 한다.
- 최상위 타입인 Object 타입으로 배열을 생성하여 사용하기 때문에 요소 접근(access elements)에서는 탁월한 성능을 보이나, 중간의 요소가 삽입, 삭제가 일어나는 경우 그 뒤의 요소들을 한 칸씩 밀어야 하거나 당겨야 하기 때문에 삽입 삭제,에서는 비효율적인 모습 보인다.
ArrayList 사용법
ArrayList 선언
ArrayList list = new ArrayList(); // 타입 미설정 Object로 선언
ArrayList<Student> members = new ArrayList<Student>(); // 타입설정 Student객체만 사용가능
ArrayList<Integer> num = new ArrayList<Integer>(); //타입설정 int타입만 사용가능
ArrayList<Integer> num2 = new ArrayList<>();// new에서 타입 파라미터 생략 가능
ArrayList<Integer> num3 = new ArrayList<Integer>(10);//초기 용량(capacity)지정
ArrayList<Integer> list2 = new ArrayList<Integer>(Arrays.asList(1,2,3));//생성시 값추가
- ArrayList 선언시 ArrayList 변수명 = new ArrayList();로 선언 후 내부에 임의 값을 넣고 사용할 수도 있지만 이렇게 사용할 경우
값을 뽑아내기 위해서는 캐스팅(Casting)연산이 필요하고 잘못된 타입으로 캐스팅을 한 경우에는 에러가 발생하기에 위와 같은 방식은 추천하지 않는다. - ArrayList를 사용할 시에는 ArrayList에 타입을 명시해주는 것이 좋다. JDK 5.0 이후부터 자료형의 안정성을 위해 제네릭스(Generics)라는 개념이 도입되었다.
ArrayList<String> list = new ArrayList<String>(); 이라고 되어 있다면
String객체들만 add 되어질 수 있고 다른 타입의 객체는 사용 불가능하다.
제네릭스 : 선언할 수 있는 타입이 객체 타입입니다. int는 기본 자료형이기 때문에 들어갈 수 없으므로 int를 객체화시킨 wrapper 클래스를 사용해야 한다.
ArrayList 값 추가
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(3); //값 추가
list.add(null); //null값도 add 추가
list.add(1,10); //index 1뒤에 10 삽입
ArrayList<Student> members = new ArrayList<Student>(); // members 객체 생성
Student student = new Student(name,age); // student 객체 생성
members.add(student);
mebers.add(new Member("홍길동",15));
- ArrayList에 값을 추가하려면 ArrayList의 add(index,value) 메소드를 사용하면 된다.
- index를 생략하면 ArrayList 맨 뒤에 데이터가 추가되면 index 중간에 값을 추가하면 해당 인덱스 부터 마지막 인덱스까지 모두 1씩 뒤로 밀려난다.
- 이 경우 데이터가 늘어나면 늘어날수록 성능에 악영향이 미치기에 중간에 데이터를 insert를 해야하는 경우가 많다면 ArrayList 보다는 LinkedList를 활용하는 것이 좋은 방법이다.
ArrayList 값 삭제
ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(1,2,3));
list.remove(1); //index 1 제거
list.clear(); //모든 값 제거
ArrayList 크기 구하기
ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(1,2,3));
System.out.println(list.size()); //list크기 :3
ArrayList 값 출력
ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(1,2,3));
System.out.println(list.get(0)); // 0번째 index 출력
for(Integer i : list) {// for문 통한 전체 출력
System.out.println(i);
}
Iterator iter = list.iterator(); //Iterator 선언
while(iter.hasNext()) { //다음값이 있는지 체크
System.out.println(iter.next()); //값 출력
}
ArrayList 값 검색
ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(1,2,3));
System.out.println(list.contains(1)); //list에 1이 있는지 검색 : true
System.out.println(list.indexOf(1)); //1이 있는 index 반환 없으면 -1
LinkedList
- 연결 리스트(LinkedList)는 각 노드가 데이터와 포인터(주소)를 가지고 한 줄로 연결되어 있는 방식의 자료구조이다.
- 데이터를 담고 있는 노드들이 연결되어 있고, 노드의 포인터가 이전 노드와 다음 노드와의 연결을 담당한다.
- Node는 LinkedList에 객체를 추가하거나 삭제하면 앞뒤 링크만 변경되고 나머지 링크는 변경되지 않는다.
중간에 데이터를 추가나 삭제하더라도 전체의 인덱스가 한 칸 씩 뒤로 밀리거나 당겨지는 일이 없기에 ArrayList에 비해서 데이터의 추가나 삭제가 용이하나
인덱스가 없기에 특정 요소에 접근하기 위해서 순차 탐색(처음노드부터 찾음)이 필요로 하여 탐색 속도가 떨어진다는 단점이있다. - 결론 : 검색 또는 정렬을 자주 하는 경우엔 배열을 사용하고 데이터의 추가/삭제가 많은 경우 연결 리스트를 사용하는 것이 좋다.
- ArrayList는 내부 배열에 객체를 저장해서 인덱스로 관리하지만 LinkedList는 위와 같이 인접 참조를 링크해서 체인처럼 관리한다.
LinkedList 사용법
LinkedList 선언
LinkedList list = new LinkedList(); //타입 미설정시 Object로 선언
LinkedList<Student> members = new LinkedList<Student>(); //타입설정 Student객체만 사용가능
LinkedList<Integer> num = new LinkedList<Integer>();// 타입설정 int 타입만 사용가능
LinkedList<Integer> num2 = new LinkedList<>();// new에서 타입 파라미터 생략가능
LinkedList<Integer> list2 = new LinkedList<Integer>(Arrays.asList(1,2)); // 생성시 값 추가
- LinkedList는 초기의 크기를 미리 생성할 수 없다. LinkedList 선언 시 LinkedList list = new LinkedList(); 로 선언하고 내부에 임의의 값을 넣어 사용할 수 있지만 이렇게 사용할 경우 내부의 값을 사용할 때 캐스팅(Casting)을 해야하고 잘못된 타입으로 캐스팅을 한 경우엔 에러가 발생하기 위와 같은 방식은 추천하지 않는다.
- LinedList를 생성할 때 사용 타입을 명시해주는 것이 좋다.
- LinkedList<Integer> list = new LinkedList<Integer>(); 라면 int만 사용할 수 있고 다른 타입은 사용 불가능
LinkedList 값 추가
LinkedList<Integer> list = new LinkedList<Integer>();
list.addFirst(1); //가장 앞에 데이터 추가
list.addLast(2); //가장 뒤에 데이터 추가
list.add(3); //데이터 추가
list.add(1,10); //index 1뒤에 데이터 10 추가
LinkedList<Student> list = new LinkedList<Student>();
Student student = new Student(name,age);
members.add(student);
members.add(new Member("홍길동",15));
- LinkedList의 값이 추가되는 방식은 위의 그림과 같다.
- 먼저 인자로 받은 값을 가지고 Node를 생성
- 이전 노드는 추가되는 노드를 가리키게 한다
- 추가된 노드는 그 다음 노드를 가리키도록 지정한다.
LinkedList 값 삭제
LinkedList<Integer> list = new LinkedList<Integer>(Arrays.asList(1,2,3,4,5));
list.removeFirst(); //가장 앞의 데이터 제거
list.removeLast(); //가장 뒤의 데이터 제거
list.remove(); //생략시 0번째 index 제거
list.remove(1); //index 1wprj
list.clear(); //모든 값 제거
LinkedList의 값이 제거되는 방식은 위의 그림과 같다.
- 삭제 대상 노드의 이전 노드가 삭제 대상 노드의 다음 노드를 가리키게 하고
- 삭제 대상 노드는 삭제된다.
LinkedList 크기 구하기
LinkedList<Integer> list = new LinkedList<Integer>(Arrays.asList(1,2,3));
System.out.println(list.size()); //list크기는 3
LinkedList 값 출력
LinkedList<Integer> list = new LinkedList<Integer>(Arrays.asList(1,2,3));
System.out.println(list.get(0));//0번째 index 출력
for(Integer i : list) { //for문을 통한 전체출력
System.out.println(i);
}
Iterator<Integer> iter = list.iterator(); //Iterator 선언
while(iter.hasNext()){//다음값이 있는지 체크
System.out.println(iter.next()); //값 출력
}
Vector
- Vector는 ArrayList와 동일한 내부구조를 가지고 있다. ArrayList와 마찬가지로 Vector 내부에 값이 추가되면 자동으로 크기가 조절되며 그 다음 객체들은 한 자리씩 뒤로 이동된다.
- 하지만 Vector와 ArrayList의 한 가지 다른 점이 있는데 Vector는 동기화된 메소드로 구성되어 있기 때문에 멀티스레드가 동시에 이 메소드를 실행할 수 없고, 하나의 스레드가 실행을 완료해야만 다른 쓰레드들이 실행할수 있다.
- 그래서 멀티 쓰레드 환경에서 안전하게 객체를 추가하고 삭제할 수 있다.
Vector의 단점 (ArrayList와의 비교)
- 벡터는 항상 동기화되는 장점이자 단점을 가지고 있다. 쓰레드가 1개일 때도 동기화를 하기 때문에 ArrayList보다 성능이 떨어진다.
- ArrayList는 기본적인 기능은 벡터와 동일하나 자동 동기화 기능이 빠져 있고, 동기화 옵션이 존재한다.
그리도 벡터에 비해 속도가 더 빠르기 때문에 벡터에 비해 많이 쓰이고 있다.
Vector 사용법
Vector 선언
Vector v = new Vector(); //타입 미설정 Object로 선언
Vector<Student> student = new Vector<Student>(); //타입설정 Student객체만 사용가능
Vector<Integer> num2 = new Vector<Integer>();//타입설정 int타입만 설정가능
Vector<Integer> num3 = new Vector<>();//new에서 타입 파라미터 생략가능
Vector<String> v2 = new Vector<String>(10);//초기 용량(capacity)지정
Vector<Integer> v3 = new Vector<Integer>(Arrays.asList(1,2,3)); //초기값 지정
- Vector 선언시 타입을 지정하지 않고 임의의 타입의 값을 넣고 사용할 수 있지만
이렇게 사용할 경우 벡터 내부의 값을 사용할려면 캐스팅(Casting) 연산이 필요하며 잘못된 타입으로 캐스팅을 한 경우에는 에러가 발생하기에 Vector를 사용할 때에는 타입을 명시 해주는 것이 좋다 - JDK 5.0이후 부터 자료형의 안정선을 위해 제네릭스(Generics) 라는 개념이 도입되었고
Vector<Integer> v = new Vector<Integer>(); 라고 되어있다면 int 객체만 add 될 수 있고 다른타입의 객체는 사용 불가능하다.
제네릭스 : 선언할 수 있는 타입이 객체 타입입니다. int는 기본 자료형이기 때문에 들어갈 수 없으므로 int를 객체화시킨 wrapper 클래스를 사용해야 한다.
Vector 값 추가
Vector<Integer> v = new Vector<Integer>();
v.add(3); //값 추가
v.add(null); // null 값도 add 가능
v.add(1,10); // index 1뒤에 10 삽입
Vector 값 삭제
Vector<Integer> v = new Vector<Integer>(Arrays.asList(1,2,3));
v.remove(1); // index 1제거
v.removeAllElements(); //모든 값 제거
v.clear(); //모든 값 제거
Vector 크기 구하기
Vector<Integer> v = new Vector<Integer>(10);//초기 용량 10
v.add(1); //값 추가
System.out.println(v.size()); //Vector 자료 개수 :1
System.out.println(v.capacity()); //Vector 물리적크기: 10
Vector 값 출력
Vector<Integer> list = new Vector<Integer>(Arrays.asList(1,2,3));
System.out.println(list.get(0)); //0번째 index 출력
for(Integer i : list) { //for문을 통한 전체출력
System.out.println(i);
}
Iterator iter = list.iterator(); //Iterator 선언
while(iter.hasNext()) { //다음값이 있는지 체크
System.out.println(iter.next()); //값 출력
}
Set 컬렉션
- List 컬렉션은 저장 순서를 유지하지만, Set 컬렉션은 저장 순서가 유지 되지 않는다.
- 또한 객체를 중복해서 저장할 수 없고, 하나의 null만 저장할 수 있다.
그렇기에 Set 컬렉션은 순서 자체가 없으므로 인덱스로 객체를 검색해서 가져오는 get(index) 메소드도 없다. - 대신 전체 객체를 대상으로 한 번씩 반복해서 가져오는 반복자(Iterator)제공한다.
- Set 컬렉션은 수학의 집합에 비유될 수 있다. 집합은 순서와 상관없고 중복이 허용되지 않기 때문이다.
- Set 컬렉션을 구현하는 대표적인 클래스들은 HashSet과 TreeSet 있다.
Set 클래스 주요 메소드
메소드 | 설명 |
boolean add(E e) | 주어진 객체를 저장 후 성공적이면 true를 중복 객체면 false리턴 |
boolean contains(Object o) | 주어진 객체가 저장되어 있는지 여부를 리턴 |
Iterator<E> iterator() | 저장된 객체를 한 번씩 가져오는 반복자 리턴 |
isEmpty() | 컬렉션이 비어있는지 조사 |
int size() | 저장되어 있는 전체 객체수를 리턴 |
void clear() | 저장된 모든 객체를 삭제 |
boolean remove(Object o) | 주어진 객체를 삭제 |
Iterator 인터페이스의 메소드
리턴타입 | 메소드 | 설명 |
boolean | hasNext() | 가져올 객체가 있으면 true를 리턴 없으면 false를 리턴 |
E | next() | 하나의 객체를 가져옴 |
void | remove() | 객체 제거 |
Set<String> set =...;
set.add("홍길동"); //객체 추가
set.add("코코");
set.remove("홍길동"); //객체 삭제
- Set 컬렉션은 인덱스로 객체를 검색해서 가져오는 메소드가 없다.
대신, 전체 객체를 대상으로 한 번씩 반복해서 가져오는 반복자(Iterator)를 제공한다. - Iterator에서 하나의 객체를 가져올 때는 next() 메소드를 사용한다.
next() 메소드를 사용하기 전에 먼저 가져올 객체가 있는지 hasNext() 메소드로 확인하는 것이 좋다.
hasNext()는 가져올 객체가 있으면 true를 리턴하고 없으면 false 리턴한다.
Set<String> set = ...;
Iterator<String> iterator = set.iterator();
while(iterator.hasNext()) {
String str = iterator.next();
}
Set 클래스의 자료구조들
HashSet
https://lavender1122.tistory.com/238
TreeSet
https://lavender1122.tistory.com/239
Map 컬렉션
- 키(key)와 값(value)으로 구성된 객체를 저장하는 구조
- 키는 중복 저장될 수 없지만 값은 중복저장될 수 있으며 중복된 key값이 들어온다면 기존의 값은 없어지고 새로운 값으로 대치된다
- Key와 Value라는 것을 한 쌍으로 갖는 자료형
- 자바의 Map은 대응관계를 쉽게 표현할 수 있게 해주는 자료형
- Map은 리스트나 배열처럼 순차적으로 해당 요소 값을 구하지 않고 key를 통해 value를 얻는게 큰 특징이다.
- 따라서 Map 컬렉션은 키(key)로 데이터를 관리한다.
Map<String,Integer> map =...;
map.put("홍길동",30); //객체 추가
int score = map.get("홍길동"); //객체 찾기
map.remove("홍길동"); //객체 삭제
- 키를 알고 있다면 get() 메소드로 간단하게 객체를 찾을 수 있지만, 저장된 전체 객체를 대상으로 하나씩 얻고 싶을 경우 두가지 방법을 사용할 수 있다.
- 첫 번째는 keySet() 메소드로 모든 키를 Set컬렉션으로 얻은 다음, 반복자를 통해 키를 하나씩 얻고 get()메소드를 통해 값을 얻는다.
Map<K,V> map = ...;
Set<K> keySet = map.keySet();
Iterator<K> keyIterator = keySet.iterator();
while(keyIterator.hasNext()) {
K key = keyIterator.next();
V value = map.get(key);
}
두번째는 entrySet() 메소드로 모든 Map.Entry를 Set컬렉션으로 얻은 다음, 반복자를 통해 Map.Entry를 하나씩 얻고 getKey()와 getValue() 메소드를 이용해 키와 값을 얻는다.
Set<Map.entry<K,V>> entrySet = map.entrySet();
Iterator<Map.Entry<K,V>> entryIterator = entrySet.iterator();
while(entryIterator.hasNext()) {
Map.Entry<K,V> entry = entryIterator.next();
K key = entry.getKey();
V value = entry.getValue();
}
Map 클래스의 자료구조
[Java] HashMap
https://lavender1122.tistory.com/240
[Java] TreeMap
https://lavender1122.tistory.com/241
어떤 상황에 어떤 자료구조가 쓰는 것이 알맞은가
- 맨 처음 갈래에서 "Will it contain keyvalue pairs or valus only?" 라고 묻는데,
간단하게 Map에 대해 말하자면 "키와 값이 쌍으로 이루어진 자료 구조를 의미한다." - 하지만 List,set 등 에 대한 것들은 모두 단일 value만 갖고 있기 때문에 첫 갈래에 왼쪽으로 뻗어나가는 것들을 아직은 무시해도 된다.
출처
https://dev-coco.tistory.com/32
https://st-lab.tistory.com/142
'이론 > 자바' 카테고리의 다른 글
[Java] TreeMap (0) | 2024.09.04 |
---|---|
[Java] HashMap (2) | 2024.09.04 |
[Java]TreeSet (0) | 2024.09.04 |
[Java] HashSet (0) | 2024.09.04 |
클래스 (0) | 2023.12.27 |