JAVA Collection
Updated:
JAVA Collection Framework
Java에서 데이터를 저장하는 자료구조들을 한 곳에 모아 편리하게 관리하고 사용하기 위해 제공하는 것. 크게 List, Set, Map으로 구분할 수 있습니다.
List : 인터페이스
- 동일한 데이터의 중복을 허용한다.
- 데이터 저장 순서가 유지된다.
- 힙(heap) 영역 내에서 List는 객체를 일렬로 늘어놓은 구조를 하고 있다.
- 객체를 인덱스로 관리하기 때문에 객체를 저장하면 자동으로 인덱스가 부여되고 인덱스로 객체를 검색, 삭제할 수 있다. 이 때 List 컬렉션은 객체 자체를 저장하여 인덱스를 부여하는 게 아니라, 해당하는 인덱스에 객체의 주소값을 참조하여 저장한다.
- List 컬렉션에서 공통적으로 사용가능한 추가, 검색, 삭제 메소드를 갖고있다.
Arraylist
Arraylist는 List 인터페이스의 구현 클래스로, 여기서의 객체는 인덱스로 관리된다. ArrayList에 객체를 추가하면 객체가 인덱스로 관리되는 것이다.
이 점은 일반 배열과 별 다를바 없지만, 자바에서 배열은 초기화 시 그 크기가 고정되어야하고 사용 중에 크기를 변경할 수 없다는 점에서 Arraylist는 가치가 있다. 설정하고 싶은 초기 용량이 n이라고 했을 때에는 아래와 같이 선언한다.
List<T> list = new Arraylist<T>(n);
- 예시
import java.util.*;
public class Examples {
public static void main(String[] args){
int size;
List<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
list.add("E");
list.add("F");
list.add("G");
list.add("H");
list.add("I");
list.add(1, "J");
size = list.size();
System.out.println("저장된 객체 수 : "+ size);
for(int i=0; i<size; i++){
//데이터를 인덱스로 관리
System.out.print(i + "번째 : " + list.get(i));
System.out.println();
}
System.out.println();
System.out.println("----------------변경 후----------------");
System.out.println();
list.remove(1);
list.remove(1);
size = list.size();
for(int i=0; i<size; i++){
System.out.print(i + "번째 : " + list.get(i));
System.out.println();
}
}
}
↑arraylist를 이용한 간단한 데이터 저장과 삭제를 구현.
List
list.remove(1); : 이 코드를 두 번 실행하였으나 컴파일 도중 아무런 문제가 없었습니다. ArrayList는 중간에 위치한 객체를 삭제하여도, 그 인덱스를 자동으로 업데이트 해주기 때문에, 인덱스 1 이 처음 삭제된 후, 뒤의 객체들이 앞으로 한 칸씩 이동하며 그 자리를 자동으로 메꾸는 형태가 됩니다.
list.get(i) : 이렇게 얻어온 데이터를 바로 출력하는 것이 아니라, String 형의 변수에 다시 담는다고 생각해보자. 위에서도 언급했듯이, list.add(); 로 저장되는 모든 데이터는 Object 타입의 객체입니다. 따라서, String 형 변수에 get해온 값을 담고 싶다면 String alphabet = (String)list.get(i) 와 같은 형변환을 반드시 해주어야 합니다. 자신이 넣어준 데이터 타입으로 형변환해주는 것은 필수입니다.
위에서 언급한 저장소의 용량 확장에는 ArrayList
LinkedList
LinkedList는 List 구현 클래스이므로 ArrayList와 사용하는 메소드는 같지만 내부 구조는 완전히 다릅니다. ArrayList는 내부 배열 객체를 저장해서 인덱스로 관리하지만, LinkedList는 양방향 포인터 구조로, 각각마다 인접하는 참조를 링크해서 체인처럼 관리합니다.
따라서, LinkedList는 특정 인덱스의 객체를 제거하거나 삽입하면, 앞 뒤 링크만 변경되고 나머지 링크는 변경되지 않습니다. 그러므로 중간 삽입/삭제가 빈번할 수록 LinkedList를 쓰는 것이 효율적입니다. 반대로, 순차적인 삽입/삭제가 빈번하다면 ArrayList를 사용하는 것이 효율적입니다.
아래와 같은 코드로 생성할 수 있습니다.
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 선언은 ArrayList선언 방식과 같습니다 다만 LinkedList에서는 초기의 크기를 미리 생성할수는 없습니다.
LinkedList선언 시 LinkedList list = new LinkedList()로 선언하고 내부에 임의의 값을 넣어 사용할 수도 있지만 이렇게 사용할 경우 내부의 값을 사용할 때 캐스팅(Casting)을 해야 하고 잘못된 타입으로 캐스팅을 한 경우에는 에러가 발생하기에 위와 같은 방식은 추천하지 않습니다.
LinkedList를 생성할 때 LinkedList에 사용 타입을 명시해주는 것이 좋습니다.
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
public class LinkedListExam {
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
// add로 데이터 추가
System.out.println("add로 데이터 초기화");
for(int i=0;i<10;i++)
list.add(i);
// size()로 데이터 크기 반환
System.out.println("리스트 size : "+list.size());
// get()으로 데이터에 접근 출력
for(int i=0;i<list.size();i++)
System.out.print(list.get(i)+" ");
// remomve()로 데이터 삭제
System.out.println("\nremove(4) 수행");
list.remove(4);
// 이터레이터로 출력
Iterator<Integer> it = list.iterator();
while(it.hasNext())
System.out.print(it.next()+" ");
// desc sort
System.out.println("\n내림차순 소팅 : ");
Collections.reverse(list);
for(int i=0;i<list.size();i++)
System.out.print(list.get(i)+" ");
System.out.println("\n오름차순 소팅 : ");
// asc sort
Collections.sort(list);
for(int i=0;i<list.size();i++)
System.out.print(list.get(i)+" ");
}
}
Set : 인터페이스
- 데이터의 저장 순서를 유지하지 않습니다.
- 같은 데이터의 중복 저장을 허용하지 않습니다. 따라서 null도 하나의 null만 저장할 수 있습니다.
- Set 컬렉션은 List 컬렉션처럼 인덱스로 객체를 검색해서 가져오는 메소드가 없습니다. 대신 전체 객체를 대상으로 한 번 씩 다 가져오는 반복자, Iterator을 제공합니다.
Set<String> setExample = new...;
Iterator<String> iterator = setExample.iterator();
이 코드를 구현하여 iterator객체를 통해 사용할 수 있습니다.
Set<String> setExample = new...;
Iterator<String> iterator = setExample.iterator();
while(iterator.hasNext()){
String getin = iterator.next();
}
보통 위 같은 방식으로, iterator 인터페이스에 선언된 hasNext()와 next() 메소드를 사용하여 구현합니다.
Class | 특징 |
---|---|
HashSet | 순서가 필요없는 데이터를 hash table에 저장. Set 중에 가장 성능이 좋음. |
TreeSet | 저장된 데이터의 값에 따라 정렬됨. red-black tree 타입으로 값이 저장됨. HashSet보다 성능이 느림. |
LinkedHashSet | 연결된 목록 타입으로 구현된 hash table에 데이터 저장. 저장된 순서에 따라 값이 정렬되나 셋 중 가장 느림 |
Map : 인터페이스
Map 컬렉션에는 키(key)와 값(value)으로 구성된 Entry 객체를 저장하는 구조를 가지고 있습니다. 여기서 키와 값은 모두 객체입니다.
값은 중복 저장이 가능하지만, 키는 중복 저장이 불가능합니다. Set과 마찬가지로, Map 컬렉션에서는 키 값의 중복 저장이 허용되지 않는 데, 만약 중복 저장 시 먼저 저장된 값은 저장되지 않은 상태가 됩니다. 즉, 기존 값은 없어지고 새로운 값으로 대체되는 것입니다.
HashMap
HashMap은 Map 인터페이스 구현을 위해 해시테이블을 사용한 클래스입니다. 또한 중복을 허용하지 않고 순서를 보장하지 않습니다. 중요한 점은, HashTable과 다르게 HashMap은 키와 값으로 null값이 허용된다는 것입니다.
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] ar) {
Map<String,Integer> map=new HashMap(); //<키 자료형, 값 자료형>
map.put("A", 100);
map.put("B", 101);
map.put("C", 102);
map.put("C", 103); //중복된 key가 들어갈때는 이전 키,값을 지금의 것으로 업데이트
System.out.println(map);
System.out.println(map.get("A"));
System.out.println(map.get("B"));
System.out.println(map.get("C"));
}
}
결과:
{A=100, B=101, C=103}
100
101
103
Leave a comment