Collection 인터페이스 주요 메소드
List와 Set인터페이스의 많은 공통된 부분을 Collection 인터페이스에서 정의하고 두 인터페이스에서 상속받습니다.
List
List는 우리가 배열처럼 흔히 쓰는 순서(인덱스)를 가지는 원소들의 모임으로, 중복값을 가질 수 있는 자료구조 이다.
- 객체들을 삽입, 삭제, 검색할 수 있는 컨테이너 클래스이다.
- 배열의 길이 제한 단점을 극복할 수 있다.
- 삽입되는 객체의 수가 많아지면 자동으로 크기가 조절된다.
- 아이템을 List의 맨 마지막이나 중간에 삽입할 수 있다.
- List 맨 뒤에 객체 추가: 공간이 모자라면 자동 늘림
- List 중간에 객체 삽입: 뒤에 존재하던 객체는 한칸씩 뒤로 이동.
- 임의의 위치에 있는 객체 삭제: 객체 삭제 후 한칸씩 앞으로 자동 이동.
- List는 내부에 삽입된 요소들을 인덱스로 관리하며 인덱스는 0부터 시작한다.
List 컬렉션 클래스에 속하는 클래스
- ArrayList
- LinkedList
- Vector
- Stack
#### List 인터페이스에서 제공하는 주요 메소드 List 인터페이스는 Collection 인터페이스를 상속받으므로, Collection 인터페이스에서 정의한 메소드도 모두 사용할 수 있습니다. 
List 인터페이스의 기능
ArrayList<Integer> arrList = new ArrayList<Integer>();
// add() 메소드를 이용한 요소의 저장
arrList.add(40);
arrList.add(20);
arrList.add(30);
arrList.add(10);
// remove() 메소드를 이용한 요소의 제거
arrList.remove(1);
// for 문과 get() 메소드를 이용한 요소의 출력
for (int i = 0; i < arrList.size(); i++) {
System.out.print(arrList.get(i) + " ");
}
// Enhanced for 문과 get() 메소드를 이용한 요소의 출력
for (int e : arrList) {
System.out.print(e + " ");
}
// Collections.sort() 메소드를 이용한 요소의 정렬
Collections.sort(arrList);
// iterator() 메소드와 get() 메소드를 이용한 요소의 출력
Iterator<Integer> iter = arrList.iterator();
while (iter.hasNext()) {
System.out.print(iter.next() + " ");
}
// set() 메소드를 이용한 요소의 변경
arrList.set(0, 20);
// size() 메소드를 이용한 요소의 총 개수
System.out.println("리스트의 크기 : " + arrList.size());
Collection 인터페이스에서는 Iterator 인터페이스를 구현한 클래스의 인스턴스를 반환하는 iterator() 메소드를 정의하여 각 요소에 접근하도록 하고 있습니다.
ArrayList
원소들을 인덱스로 접근하여 배열과 사용법이 매우 유사합니다.
배열은 초기에 생성할 때 크기를 정해줘야 하므로 유동적인 사이즈로 초기화할 수 없는 불편함이 있지만
대안으로 쓰이는 ArrayList는 저장되는 데이터의 갯수에 따라 자동적으로 크기가 변경되어 배열대신 자주 사용합니다.
배열은 크기를 변경할 수 없는 인스턴스이므로, 크기를 늘리기 위해서는 새로운 배열을 생성하고 기존의 요소들을 옮겨야 하는 복잡한 과정을 거쳐야 합니다.
물론 이 과정은 자동으로 수행되지만, 요소의 추가 및 삭제 작업에 걸리는 시간이 매우 길어지는 단점을 가지게 됩니다.
자바에서 ArrayList를 사용하기 위해 아래의 구문을 먼저 추가해야 합니다.
import java.util.ArrayList;
ArrayList의 생성
ArrayList<Integer> integers1 = new ArrayList<Integer>(); // 타입 지정
ArrayList<Integer> integers2 = new ArrayList<>(); // 타입 생략 가능
ArrayList<Integer> integers3 = new ArrayList<>(10); // 초기 용량(Capacity) 설정
ArrayList<Integer> integers4 = new ArrayList<>(integers1); // 다른 Collection값으로 초기화
ArrayList<Integer> integers5 = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5)); // Arrays.asList()
보통 생성할 때는 new ArrayList<>()와 같이 타입을 생략해서 작성합니다.
ArrayList를 생성할 때 Set이나 다른 ArrayList를 전달하면 해당 Collections의 값들로 초기화됩니다.
마지막으로 가변 인자를 전달받는 Arrays.asList()를 사용하면 기본 값들로 생성 가능합니다.
ArrayList 요소 추가 / 변경
//ArrayList 생성하기
ArrayList<String> arr = new ArrayList<String>();
//insert
//ArrayList의 index는 0부터 위치합니다.
//add()는 기본적으로 리스트의 가장 끝에 값을 추가합니다.
arr.add("aaa");
arr.add("hello");
//특정 index에 삽입
//별도로 인덱스를 지정하면 해당 인덱스에 값이 추가되고 그 인덱스부터의 값들이 1 칸씩 밀립니다.
arr.add("1","ccc");
//특정 index의 원소를 바꿈
//set()메소드를 통해 (index : 2)의 "hello"가 "ddd"로 변경됩니다.
arr.set("2","ddd");
//특정 index의 원소 삭제
arr.remove(3);
//특정 index의 값을 가져옴
arr.get(0);
//원소로 index찾기, 원소가 존재하지 않으면 -1을 반환한다.
int index = arr.indexOf("bbb");
//맨 뒤에서부터 원소로 index찾기
int index2 = arr.lastIndexOf("ccc");
//ArrayList 안의 값을 전체 삭제합니다.
arr.clear();
add()는 기본적으로 리스트의 가장 끝에 값을 추가합니다.
별도로 인덱스를 지정하면 해당 인덱스에 값이 추가되고 그 인덱스부터의 값들이 1 칸씩 밀립니다.
ArrayList안에 원하는 값 존재 유무 확인
먼저 값이 존재하는지만 알고 싶은 경우 contains()를 사용합니다.
값이 존재할 때 어느 위치에 존재하는지 알고 싶은 경우 indexOf()를 사용할 수 있습니다.
import java.util.ArrayList;
import java.util.Arrays;
public class ArrayListTest {
public static void main(String[] args) {
ArrayList<String> colors = new ArrayList<>(Arrays.asList("Black", "White", "Green", "Red"));
//Contains()는 값이 있는 경우 true, 없는 경우 false를 리턴합니다.
boolean contains = colors.contains("Black");
System.out.println(contains);
//indexOf()는 값이 존재하는 경우 해당 요소의 index를 리턴합니다.
int index = colors.indexOf("Blue");
System.out.println(index);
index = colors.indexOf("Red");
System.out.println(index);
}
}
값이 존재하지 않을 경우 -1을 리턴하기 때문에 별도로 처리가 가능합니다.
ArrayList클래스 구현
//임의로 구현한 Arraylist클래스
//어떤 방식으로 동작하는지 알아두는게 좋을 것 같다.
class MyArrayList {
private int index;
private String[] list;
//생성자
public MyArrayList() {
this.index = -1;
this.list = new String[100];// 임의로 100칸짜리 만들기
}
//삭제 메소드
public void remove(int index) {
// 파라미터 index부터 시작해서 this 인덱스까지.
for (int i = index; i < this.index; i++) {
list[i] = list[i + 1];
}
this.index--;
}
//수정 메소드
public void set(int index, String value) {
this.list[index] = value;
}
//list의 크기
public int size() {
return this.index + 1;
}
//삽입 메소드(남은 list에 삽입)
public void add(String value) {
index++;
// 배열에 value를 추가.
// index방에 추가.
this.list[index] = value;
}
//삽입 메소드(해당 파라미터 index에 삽입)
public void add(int index, String value) {
// index 방에 value를 추가.
this.index++; // 먼저 방의 인덱스를 하나 늘려주고.
// 뒤쪽 값부터 차례대로 한칸씩 이동시킨다.
for (int i = this.index; i >= index; i--) {
list[i + 1] = list[i];
} // 이동 완료.
list[index] = value;
}
//list배열의 index요소를 반환.
public String get(int index) {
return this.list[index];
}
//왼쪽에서부터 value값을 찾아서 인덱스를 반환
public int indexOf(String value) {
// list 배열의 index까지만
for (int i = 0; i <=this.index; i++) {
if (list[i].equals(value)) {
return i;
}
}
return -1;
}
//오른쪽에서부터 value값을 찾아서 인덱스를 반환
public int lastIndexOf(String value) {
// list 배열의 index까지만
for (int i = this.index; i <=0; i--) {
if (list[i].equals(value)) {
return i;
}
}
return -1;
}
//초기화
public void clear() {
this.index=-1;
for(int i =0; i<list.length;i ++) {
list[i]=null;
}
}
}
LinkedList
LinkedList
ArrayList와 장단점이 비교되는 리스트입니다.
LinkedList는 각각의 데이터가 노드로 구성되어 연결이 되는 구조입니다.
단일 연결 리스트는 요소의 저장과 삭제 작업이 다음 요소를 가리키는 참조만 변경하면 되므로, 아주 빠르게 처리될 수 있습니다.
하지만 단일 연결 리스트는 현재 요소에서 이전 요소로 접근하기가 매우 어렵습니다
양방향 연결 리스트(Doubly Linked List)는 이전 요소들을 참조로 가지고 있어 단일 연결 리스트보다 좀 더 많이 사용됩니다.
일반적으로 LinkedList의 장점은 원소가 삽입되고 삭제될 때 바로 앞에 있는 원소의 링크값만을 변경하면 되기 때문에 데이터를 추가하거나 삭제하는 것이 원활하다는 점입니다.
LinkedList 클래스 역시 List 인터페이스를 구현하므로, ArrayList 클래스와 사용할 수 있는 메소드가 거의 같습니다
*즉, 인덱스를 가지고 원소를 접근하는 원소는 ArrayList의 성능이 더 좋고, 중간에 원소의 삽입, 삭제가 빈번히 일어나는 경우에는 LinkedList의 성능이 더 좋다고 할 수 있습니다.
//LinkedList생성
LinkedList<String> lnkList = new LinkedList<String>();
LinkedList의 값이 추가되는 방식
LinkedList의 값이 추가되는 방식은 위의 그림과 같습니다. 먼저 인자로 받은 값을 가지고 Node를 생성하여 생성한 노드는 이전 노드는 추가되는 노드를 가리키게 하고 추가되는 노드는 그다음 노드를 가리키도록 지정합니다.
Single Linked List구현
public class LinkedList {
//첫번째 노드가 저장될 필드
private Node head;
private Node tail;
private int size = 0;
private class Node {
//데이터가 저장될 필드
private Object data;
//연결할 다음 객체의 데이터 타입을 저장한다
private Node next;
//data의 값
public Node(Object input) {
this.data = input;
this.next = null;
}
//노드의 내용을 쉽게 출력해서 확인해볼 수 잇는 기능
public String toStrig() {
return String.valueOf(this.data);
}
}
public void addFirst(Object input){
// 노드를 생성합니다.
Node newNode = new Node(input);
// 새로운 노드의 다음 노드로 해드를 지정합니다.
newNode.next = head;
// 헤드로 새로운 노드를 지정합니다.
head = newNode;
size++;
if(head.next == null){
tail = head;
}
}
public void addLast(Object input){
// 노드를 생성합니다.
Node newNode = new Node(input);
// 리스트의 노드가 없다면 첫번째 노드를 추가하는 메소드를 사용합니다.
if(size == 0){
addFirst(input);
} else {
// 마지막 노드의 다음 노드로 생성한 노드를 지정합니다.
tail.next = newNode;
// 마지막 노드를 갱신합니다.
tail = newNode;
// 엘리먼트의 개수를 1 증가 시킵니다.
size++;
}
}
//특정 위치의 노드를 찾아내는 방법
Node node(int index){
Node x = head;
for(int i=0; i<index;i++)
x = x.next;
return x;
}
public void add(int k, Object input) {
//만약 k가 0이라면 첫번째 노드에 추가하는 것이기 때문에 addFirst를 사용합니다.
if(k==0) addFirst(input);
else{
Node temp1 = node(k-1);//index의 k-1번째의 위치를 받아온다
Node temp2 =temp1.next; //k번째 노드를 temp2를 생성합니다.
Node newNode = new Node(input); //새로운 노드를 생성합니다.
temp1.next = newNode; //temp1의 다음노드로 새로운 노드를 지정합니다
newNode.next = temp2; //새로운 노드의 다음 노드로 temp2를 지정합니다.
size++;
//새로운 노드의 다음 노드가 없다면 새로운 노드가 마지막 노드이기 때문에 tail로 지정합니다.
if(newNode.next==null){
tail = newNode;
}
}
}
public String toString(){
if(head == null) return "[]";//노드가 없다면 []을 리턴합니다.
Node temp = head;
String str = "[";
//다음 노드가 없을 때까지 반복문 실행(마지막 노드는 포함x)
while(temp.next!=null){
str += temp.data+", ";
temp = temp.next;
}
//마지막 노드를 출력 결과에 포함시킵니다.
str+=temp.data;
return str+"]";
}
public Object removeFirst() {
//첫번째 노드를 temp로 지정하고 head의 값을 두번째 노드로 변경합니다.
Node temp = head;
head = temp.next;
//데이터를 삭제하기 전에 리턴할 값을 임시 변수에 담습니다.
Object returnData = temp.data;
temp = null;
size--;
return returnData;
}
public Object remove(int k){
if(k==0) return removeFirst();
Node temp = node(k-1); //k-1번째 노드를 temp의 값으로 지정합니다.
//삭제할 노드를 todoDeleted로 기록해둡니다.
Node todoDeleted = temp.next;
temp.next = temp.next.next; //삭제 앞 노드의 다음 노드로 삭제 뒤 노드를 지정합니다.
Object returnData = todoDeleted.data; //삭제된 데이터를 리턴하기 위해서 returnData에 데이터를 저장합니다.
if(todoDeleted==tail){
tail = temp;
}
todoDeleted = null;
size--;
return returnData;
}
public int size(){
return size;
}
public Object get(int k){
Node temp = node(k);
return temp.data;
}
public int indexOf(Object data){
//탐색 대상이 되는 노드를 temp로 지정합니다.
Node temp = head;
//탐색 대상이 볓번째 엘리먼트에 있는지를 의미하는 변수로 index를 사용합니다.
int index = 0;
//탐색값과 탐색 대상의 값을 비교합니다.
while(temp.data!=data){
temp = temp.next;
index++;
//temp의 값이 null이라는 것은 더이상 탐색 대상이 없다는 것을 의미합니다. 이따 -1을 리턴
if (temp == null) {
return -1;
}
}
//탐색 대상을 찾았다면 대상의 인덱스 값을 리턴합니다.
return index;
}
public ListIterator listIterator(){
return new ListIterator();
}
public class ListIterator{
private Node next;
ListIterator(){
next = head;
}
public Object next(){
}
}
}
Vector
벡터에는 String, Integer, Person등의 다양한 타입의 객체가 삽입가능합니다.
경로는 import java.util.Vector;로 선언합니다.
Vector v = new Vector(10);//벡터의 초기 용량 10
Vector<Integer> list = new Vector<Integer>(Arrays.asList(1,2,3));
v.add("hello");//벡터에 값을 추가
v.get(0); //벡터의 0번째 인덱스의 값을 캐스팅
int a = v.size(); //vector 자료 개수 :1
int b = v.capacity(); //vector 물리적 크기 : 10
for(Integer i : list){ //for문을 활용한 전체 출력
System.out.println(i);
}
Iterator iter = list.iterator(); //iterator 선언
while(iter.hasNext()){ //다음값이 있는지 체크
System.out.println(iter.next()); //값 출력
}
Vector, Stack, Hashtable, Properties와 같은 클래스들은 컬렉션 프레임웍이 만들어지기 이전부터 존재하던 것이기 때문에 컬렉션 프레임웍의 명명법을 따르지 않는다. 그렇기 때문에 새로 추가된 ArrayList와 HashMap을 사용하는 것이 좋다.
Set
Set 인터페이스를 구현한 모든 Set컬렉션 클래스는 다음과 같은 특징을 가집니다.
- 요소의 저장 순서를 유지하지 않습니다.
- 같은 요소의 중복 저장을 허용하지 않습니다.
Set은 위 그림과 같이 주머니 형태로 되어있습니다. 비선형 구조이기 때문에 순서가 없으며, 그렇기에 인덱스도 존재하지 않습니다.
값을 추가하거나 삭제할 때에는 사용자가 추가 혹은 삭제하고자 하는 값이 Set내부에 있는지 검색한 뒤 추가나 삭제를 해야 하므로 속도가 List구조에 비해 느립니다.
Set 컬렉션 클래스에 속하는 클래스
- HashSet
-자동정렬을 해주지 않는다. - TreeSet
- 자동정렬을 해준다.
HashSetHashSet 클래스는 Set 인터페이스를 구현하므로, 요소를 순서에 상관없이 저장하고 중복된 값은 저장하지 않습니다.
만약 요소의 저장 순서를 유지해야 한다면 JDK 1.4부터 제공하는 LinkedHashSet 클래스를 사용하면 됩니다. - HashSet에서 중복을 걸러내는 과정
- JDK 1.2부터 제공된 HashSet 클래스는 해시 알고리즘(hash algorithm)을 사용하여 검색 속도가 매우 빠릅니다.
이러한 HashSet 클래스는 내부적으로 HashMap 인스턴스를 이용하여 요소를 저장합니다. - 해당 요소에서 hashCode() 메소드를 호출하여 반환된 해시값으로 검색할 범위를 결정합니다.
- 해당 범위 내의 요소들을 equals() 메소드로 비교합니다.
HashSet사용법
HashSet<Integer> set1 = new HashSet<Integer>();//HashSet생성
HashSet<Integer> set2 = new HashSet<>();//new에서 타입 파라미터 생략가능
HashSet<Integer> set3 = new HashSet<Integer>(set1);//set1의 모든 값을 가진 HashSet생성
HashSet<Integer> set4 = new HashSet<Integer>(10);//초기 용량(capacity)지정
HashSet<Integer> set5 = new HashSet<Integer>(10, 0.7f);//초기 capacity,load factor지정
HashSet<Integer> set6 = new HashSet<Integer>(Arrays.asList(1,2,3));//초기값 지정
HashSet도 List와 마찬가지로 저장공간보다 값이 추가로 들어오면 List처럼 저장공간을 늘리는데 Set은 한 칸씩 저장공간을 늘리지 않고 저장용량을 두배로 늘리기 때문에 과부하가 많이 발생합니다. 그렇기에 초기에 저장할 데이터의 갯수를 알고 있다면 Set의 초기 용량을 지정해주는 것이 좋습니다.
HashSet 예제
HashSet<String> hs01 = new HashSet<String>();
HashSet<Integer> set = new HashSet<Integer>(Arrays.asList(1,2,3));//HashSet생성
// add() 메소드를 이용한 요소의 저장
hs01.add("홍길동");
hs01.add("이순신");
System.out.println(hs01.add("임꺽정")); //(true)
System.out.println(hs01.add("임꺽정")); // 중복된 요소의 저장 (false)
// Enhanced for 문과 get() 메소드를 이용한 요소의 출력
for (String e : hs01) {
System.out.print(e + " ");
//result : 홍길동 이순신 임꺽정
}
// iterator() 메소드를 이용한 요소의 출력
Iterator<String> iter = hs01.iterator();
while (iter.hasNext()) {
System.out.print(iter.next() + " ");
}
// size() 메소드를 이용한 요소의 총 개수
System.out.println("집합의 크기 : " + hs01.size());
//set내부에 값 1이 있는지 check : true
System.out.println(set.contains(1));
[참조]
http://tcpschool.com/java/java_collectionFramework_list
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=ppuagirls&logNo=221560996691
https://psychoria.tistory.com/767
https://sas-study.tistory.com/73?category=769494