꼬물꼬물

[자바의 정석]11. 컬렉션 프레임웍-1 본문

스터디/JAVA

[자바의 정석]11. 컬렉션 프레임웍-1

멩주 2022. 10. 27. 23:21

1. Collections Framework

  • 데이터 군을 저장하는 클래스들을 표준화한 설계
  • Java API 문서에서는 **‘데이터군을 다루고 표현하기 위한 단일화된 구조’**라고 한다.
  • JDK1.2이전까지는 Vector, HashTable, Properties와 같은 컬랙션 클래스, 다수의 데이터를 저장할 수 있는 클래스를 다른 각자의 방식으로 처리했어햐 했다.
  • JDK1.2부터 컬렉션 프레임웍이 등장하면서 다양한 종류의 컬렉션 클래스가 추가되고 모든 컬렉션 클래스를 표준화된 방식으로 다룰 수 있도록 체계화 됨.
    • Vector와 같이 다수의 데이터를 저장할 수 있는 클래스 == 컬렉션 클래스
  • 컬렉션 프레임웤은 컬렉션, 다수의 데이터를 다루는 다양한 클래스를 제공한다. 인터페이스와 다형성을 이용한 객체지향적 설계를 통해 표준화되어 있기 때문에 편리하고, 재사용성 높은 코드를 작성할 수 있다.

1.1 컬렉션 프레임웍의 핵심 인터페이스

  • 컬렉션 데이터 그룹을 크게 3가지 타임이 존재한다고 인식하고 인터페이스를 만들었다.

컬렉션 프레임웍의 핵심 인터페이스 계층도

  • 인터페이스 List Set을 구현한 컬렉션 클래스들은 서로 많은 공통부분이 있어, 공통된 부분을 뽑아 Collection인터페이스로 정의할 수 있었지만,
public interface Set<E> extends Collection<E> { ... }

public interface List<E> extends Collection<E> { ... }

public interface Map<K, V> { ... }
  • Map 인터페이스는 이들과 전혀 다른 형태로 컬렉션을 다루기 때문에 같은 상속계층도에 포함되지 않는다.

 

인터페이스 특징
List - 순서가 있는 데이터 집합
- 데이터 중복 허용
구현클래스) ArrayList, LinkedList, Stack, Vector 등
Set - 순서가 있는 데이터 집합
- 데이터 중복 허용
구현클래스) ArrayList, LinkedList, Stack, Vector 등
Map - 순서가 있는 데이터 집합
- 데이터 중복 허용
구현클래스) ArrayList, LinkedList, Stack, Vector 등
  • 컬렉션의 특징을 파악하고 어떤 인터페이스를 구현한 컬렉션 클래스를 사용할지 결정해야한다.
  • 보통 이름으로 클래스를 구분할 수 있으나, Vector, Stack, Hashtable, Properties는 컬렉션 프레임웍이 만들어지기 전부터 존재해서 이름으로 특징을 알 수 없다.
  • Vector Hashtable과 같은 기존의 컬렉션 클래스들은 호환을 위해 남겨두었지만, 가능하면 사용하지 않는 것이 좋다.
    • ArrayList와 HashMap을 사용하자.

<Collection 인터페이스>

메서드 설명
boolean add(Object o)  
boolean addAll(Collection c) 지정된 객체 or 컬렉션의 객체들을 추가한다.
void clear() 컬렉션의 모든 객체 삭제
boolean contains(Object o)  
boolean containsAll(Collections c) 지정된 객체 or 컬렉션의 객체들이 컬렉션에 포함되어있는지 확인하기
boolean equals(Object o) 동일 컬렉션인지 확인한다.
boolean remove(Object o) 지정된 객체를 컬렉션에서 삭제한다.
boolean remmoveAll(Collection c) 지정된 Collection에 포함된 객체 삭제
boolean retainAll(Collection c) 지정된 Collection에 포함된 객체만 남기고 다른 객체를 컬렉션에서 삭제한다.
Collection에 변화가 있으면 true, 없으면 false 반환
  • 더 많은데 일단 간단하게..
  • Collection 인터페이스는 컬렉션 클래스에 저장된 데이터를 읽고, 추가하고 삭제하는 등 컬렉션을 다루는데 가장 기본적인 메서드들을 정의하고 있다.
  • boolean 반환값은 작업에 성공하거나 사실이면 true, 그렇지 않으면 false를 반환한다.
  • Java API 문서를 보면 Object가 아닌 E로 표기되어 있는데, E(Element)는 특정 타입을 의미하는 것으로 JDK 1.5부터 추가된 제너릭스에 의한 표기.

<List 인터페이스>

  • 중복 허용, 저장순서 유지

메서드 설명
void add(int index, Object element)
boolean addAll(int indexm Collection c)
지정된 위치(index)에 객체 또는 컬렉션에 포함된 객체를 추가한다.
Object get(int index) 지정된 위치에 있는 객체 반환
int indexOf(Object o) 지정된 객체의 위치를 반환 (첫번째 요소부터 순방향으로 찾는다)
int lastIndexOf(Object o) 지정된 객체의 뤼치를 반환 (마지막 요소부터 역방향으로 찾는다)
Object remove(int index) 지정된 위치에 있는 객체를 삭제하고, 삭제된 객체 반환
Object set(int index, Object element) 지정된 위치에 객체를 저장한다.
void sort(Comparator c) 지정된 비교자로 List 정렬
List subList(int fromIdx, int toIdx) 지정된 번위에 있는 객체를 반환. (fronIdx부터 toIdx전까지)

<Set 인터페이스>

  • 중복 비허용, 저장순서 비유지

 

<Map 인터페이스>

  • key(키)와 value(값)을 하나의 쌍으로 묶어 저장.
    • 키는 중복 비허용, 값은 중복 허용
    • 기존에 저장된 데이터와 중복된 키를 저장하면 기존 값은 없어지고 마지막에 저장된 값이 남는다.

메서드 설명
void clear() Map의 모든 객체 삭제
boolean containsKey(Object key) 지정된 key 객체와 일치하는 Map의 key가 있는지 확인
boolean containsValue(Object value) 지정된 value 객체와 일치하는 Map의 value객첵 있는지 확인
Set entrySet() Map에 저장되어 있는 key-value 쌍을 Map.Entry 타입의 객체로 저장한 Set으로 반
boolean equals(Object o) 동일한 Map인지 비교
Object get(Object key) 지정한 key에 대응하는 value객체를 반환
int hashCode() 해시코드 반환
Set keySet() Map에 저장된 모든 key객체를 반환한다.
Object put(Object key, Object value) Map에 value객체를 key객체와 연결해(mapping) 저장한다.
void putAll(Map t) 지정된 Map의 모든 key-value 쌍을 추가한다.
Object remove(Object key) 지정한 key객체와 일치하는 key-value객체를 삭제한다.
Collection values() Map에 저장된 모든 value객체 반환
  • values()에서는 반환타입이 Collection이고, keySet()에서는 반환타입이 Set이다.
    • Map인터페이스에서 값(value)은 중복 허용이기 때문에, Collection 타입으로,
    • 키(key)는 중복 비허용이므로 Set 타입으로 반환한다.

<Map.Entry 인터페이스>

interface Entry<K, V> { ... }

  • Map 인터페이스의 내부 인터페이스(inner interface)
  • Map에 저장되는 key-value 쌍을 다루기 위해 내부적으로 Entry 인터페이스를 정의해 놓았다.
  • 보다 객체지향적으로 설계하도록 유도하기 위해 Map을 구현하는 인터페이스에서는 Map.Entry 인터페이스도 함께 구현해야 한다.

 

1.2 ArrayList

  • List 인터페이스를 구현하기 때문에 데이터의 저장 순서가 유지되고 중복이 허용된다.
  • ArrayList는 기존의 Vector를 개선한 것.
  • Object 배열을 이용해 데이터를 순차적으로 저장한다. 배열에 저장할 공간이 없다면 보다 큰 새로운 배열을 생성해 복사한 다음 저장한다.
public class ArrayList<E> extends AbstractList<E>
      implements List<E>, RandomAccess, Cloneable, java.io.Serializable { 
	private static final int DEFAULT_CAPACITY = 10;
  transient Object[] elementData; // object 배열
}
  • elementData라는 이름의 Object 배열을 멤버변수로 선언한다.
  • 최고 조상인 Object이므로 모든 종류의 객체를 담을 수 있다.
메서드 설명
ArrayList() 크기가 10인 ArrayList 생성
ArrayList(Collection c) 주어진 컬렉션이 저장된 ArrayList 생성
ArrayList(int initialCapacity) 지정된 초기용량을 갖는 ArrayList 생성
boolean add(Object o) 마지막 객체 추가. 성공하면 true
void add(int idx, Object o) 지정된 위치에 객체 저장
boolean addAll(Colection) 컬렉션의 모든 객체 저장
boolean addAll(int idx, Collection c) 지정된 위치부터 주어진 컬렉션의 모든 객체를 저장한다.
Object clone() ArrayList를 복사한다. cloneable을 상속받고 있음
boolean contains(Object o) 지정된 객체의 ArrayList 포함 여부
Object remove(int idx) 지정된 위치에 있는 객체 삭제
boolean remove(Object o) 지정한 객체 삭제
boolean removeAll(Collection c) 지정한 컬렉션에 저장된 것과 동일한 객체를 ArrayList에서 제거한다.
boolean retainAll(Collection c) ArrayList에 저장된 객체 중 주어진 컬렉션과 공통된 것만 남기고 나머지는 삭제한다.
Object[] toArray() ArrayList에 저장된 모든 객체를 객체배열로 담아 반환
void trimToSize() 용량을 크기에 맞게 줄인다.

Collection은 인터페이스과 Collections는 클래스

 

<trimToSize>

public void trimToSize() {
    modCount++;
    if (size < elementData.length) {
        elementData = (size == 0)
          ?EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
    }
}
  • 선언한 실제 용량보다 크기가 작다면 용량을 크기와 맞춘다.
    • 전혀 새로운 배열이 만들어지므로 기존의 인스턴스는 GC에 의해 삭제된다.
  • ArrayList나 Vector 같이 배열을 이용한 자료구조는 데이터를 읽어오고 저장하는 데는 효율이 좋지만,
  • 용량을 변경해야할 때는 새로운 배열을 생성한 후, 기존의 배열로 부터 새로 생성된 배열로 데이터를 복사해야하기 때문에 효율이 떨어진다.
    • 그래서 처음 인스턴스 생성시, 용량을 고려해 생성하는 것이 좋다.

<System.arraycopy()>

public static native void arraycopy(Object src,  int  srcPos, Object dest, int destPos,int length);
  • System.arraycopy(data, 3, data, 2, 2);
  • data[3]부터 2개의 길이를 data[2]부터 2개의 길이에 복사
  • 순차적으로 지울 때는 호출하지 않지만, 중간에 위치한 객체를 추가하거나 삭제하는 경우 System.arraycopy를 호출해야 하기 때문에, 작업시간이 오래걸린다.

1.3 LinkedList

  • 배열은 가장 기본적인 형태의 자료구조로 구조가 간단하며 사용하기 쉽고 데이터를 읽어오는데 걸리는 시간(접근 시간)이 빠르다는 장점이 있지만
  • 단점으로는
    1. 크기를 변경할 수 없다.
      • 새로운 배열을 생성해 데이터를 복사해야한다.
      • 실행속도 향상을 위해서는 충분히 큰 크기의 배열을 생성해야해서 메모리 낭비
    2. 비순차적인 데이터의 추가/삭제에 시간이 오래 걸린다.
      • 차례대로 데이터를 추가하고 마지막부터 삭제하는 것은 빠르지만,
      • 배열의 중간에 데이터를 추가하려면 빈자리를 만들기 위해 다른 데이터들을 복사해서 이용해야한다.

<단순 링크드 리스트>

  • 이러한 배열의 단점을 보완하기 위해 LinkedList 자료구조가 등장했다.
  • 배열은 모든 데이터가 연속적으로 존재하지만 링크드 리스트는 불연속적으로 존재하는 데이터를 서로 연결한 형태로 구성된다.
class Node {
	Node next; // 다음 요소의 주소를 저장
	Object obj; // 데이터를 저장
}
  • LinkedList의 각 요소(Node)들은 자신과 연결된 다음 요소에 대한 참조(주소값)와 데이터로 구성되어 있다.
  • 데이터 삭제가 간단하다.
    • 삭제하고자 하는 요소의 이전 요소가 삭제하고자하는 요소의 다음 요소를 참조하도록 변경하기만 하면 된다.
    • 배열처럼 데이터를 이동하기 위해 복사하는 과정이 없어 처리속도가 매우 빠르다.
  • 데이터 추가
    • 새로운 요소를 생성한 다음, 추가하고자 하는 위치의 이전 요소의 참조를 새로운 요소에 대한 참조로 변경하고,
    • 새로운 요소가 그 다음 요소를 참조하도록 변경하기만 하면 된다. 또한 처리속도가 빠르다.
  • 링크드 리스트는 이동 방향이 단방향이기 때문에 다음 요소에 대한 접근은 귑지만 이전 요소에 대한 접근이 어렵다.
  • 이를 보완한 것이 더블 링크드 리스트(doubly linked list)

<더블 링크드 리스트>

class Node {
	Node next; // 다음 요소의 주소를 저장
	Node previous; // 이전 요소의 주소를 저장
	Object obj; // 데이터를 저장
}
  • 단순 링크드 리스트에 참조변수를 하나 더 추가해 다음 요소에 대한 참조뿐만 아니라 이전 요소에 대한 참조가 가능하게 한다.

<더블 써큘러 링크드 리스트(이중 원형 연결리스)>

  • 맨 처음 노드의 이전 노트가 맨 마지막 노드가 된다.
public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
	...
	private static class Node<E> {
	    E item; // 값
	    Node<E> next; // 다음 요소의 주소
	    Node<E> prev;
	
	    Node(Node<E> prev, E element, Node<E> next) {
	        this.item = element;
	        this.next = next;
	        this.prev = prev;
	    }
	}
	...
}

  • 실제 LinkedList는 Doubly Linked List(더블 연결리스트)로 구현되어 있다.
  ArrayList LinkedList
순차적 추가 빠름 느림
중간에 추가 느림 빠름
중간에 삭제 느림 빠름
순차적 삭제 빠름 느림
  • 순차적인 경우 ArrayList가 빠르다.
    • 추가하는 경우 ArrayList의 크기가 작다면 LinkedList가 빠를 수도 있다.
    • 삭제의 경우 크기는 변하지 않는다. 그냥 안의 요소값이 null이 될 뿐
  • 중간 데이터를 추가/삭제하는 경우 LinkedList가 빠르다.
    • LinkedList의 경우, 각 요소간의 연결만 변경해주면 되기 때문에 처리속도가 매우 빠르다.
    • ArrayList는 각 요소들을 재배치하여 추가할 공간을 확보하거나 빈 공간을 채워야해 처리속도가 느리다.
    • 데이터의 개수가 그리 크지 않으면 어느 것을 사용해도 큰 차이가 나지 않는다.

<접근 시간>

  • 배열(ArrayList)의 경우, 인덱스가 n인 요소의 값을 얻고자 한다면, 단순히
    • 인덱스가 n인 데이터 주소 = 배열의 주소 + n * 데이터 타입의 크기
  • 로 빠르게 접근 가능하다.
    • 배열은 각 요소들이 연속적으로 메모리상에 존재하기 때문이다.
  • LinkedList의 경우, 불연속적으로 위치한 각 요소들이 서로 연결된 것이라 처음부터 n번째 데이터까지 차례대로 따라가야 한다.
    • 데이터의 개수가 많아질수록 접근시간이 길어진다.
  접근시간(읽기) 추가/삭제  
ArrayList 빠름 느림 - 순차적인 추가/삭제는 빠르다.
- 비효율적인 메모리 사용
LinkedList 느림 빠름 - 데이터가 많을 수록 접근성이 떨어진다.
  • 비효율적인 메모리 사용 | | LinkedList | 느림 | 빠름 | - 데이터가 많을 수록 접근성이 떨어진다. |
  • 다루고자하는 데이터의 개수가 변하지 않는 경우라면 ArrayList
  • 데이터 개수의 변경이 잦다면 LinkedList
  • 데이터 저장에는 ArrayList를 작업할때는 LinkedList를 사용할 수도 있다.
    • 컬렉션끼리 변환이 가능한 생성자를 제공하고 있다.

 

1.4 Stack과 Queue

Stack과 Queue

  • 스택은 마지막에 저장한 데이터를 가장 먼저 꺼내는 LIFO 구조
  • 큐는 처음 저장한 데이터를 가장 먼저 꺼내는 FIFO 구조

💡 Stack과 Queue를 구현하기 위해선 어떤 컬렉션 클래스를 사용하는게 좋을까?

  • 순차적으로 데이터를 추가하고 삭제하는 스택에는 ArrayList와 같은 배열기반의 클래스가 적합하지만,
  • 큐는 데이터를 꺼낼 때, 항상 첫 번째 저장된 데이터를 삭제하므로 ArrayList를 사용하면 데이터를 꺼낼 때마다 데이터의 복사가 발생해 비효율적이다.
    • 그래서 큐는 데이터의 추가/삭제가 쉬운 LinkedList로 구현하는 것이 더 적합하다.
Stack<Integer> stack = new Stack<>();
Queue<Integer> queue = new LinkedList<>();

Stack은 구현체 클래스가 존재하지만 Queue는 인터페이스로만 정의해놓아 구현체를 골라 사용할 수 있다.

큐의 구현체

<스택과 큐의 활용>

  • Stack
    • 수식계산, 수식괄호검사, 웹브라우저의 뒤로/앞으로
  • Queue
    • 최근 사용 문서, 인쇄작업 대기목록, 버퍼

<PriorityQueue>

  • Queue 인터페이스의 구현체 중 하나로, 저장한 순서에 관계없이 우선순위(priority)가 높은 것부터 꺼내게 된다.
  • 가장 우선순위가 높은 값을 가져온다.
  • null은 저장할 수 없다. NullPointerException 발생
  • PriorityQueue는 저장공간으로 배열을 사용하며, 각 요소를 **힙(heap)**이라는 자료구조의 형태로 저장한다.
    • 힙은 이진트리의 한 종류로 가장 큰 값이나 작은 값을 빠르게 찾을 수 있다는 특징을 갖는다.

<Deque(Double-Ended Queue)>

  • Queue의 변형으로, 한쪽 끝으로만 추가/삭제할 수 있는 Queue와 달리, Deque(덱)은 양쪽 끝에 추가/삭제가 가능하다.

 

1.5 Iterator, ListIterator, Enumeration

  • 모두 컬렉션에 저장된 요소를 접근하는데 사용되는 인터페이스이다.
  • Enumeration은 Iterator의 구버전이며, ListIterator는 Iterator의 기능을 향상시킨 것

<Iterator>

  • 컬렉션 프레임웍은 컬렉션에 저장된 요소를 읽어오는 방법을 표준화했다.
  • 컬렉션에 저장된 각 요소에 접근하는 기능을 가진 Iterator 인터페이스를 정의하고 Collection 인터페이스에서는 Iterator(구현한 클래스의 인스턴)를 반환하는 iterator()를 정의한다.
public interface Collection<E> extends Iterable<E> {
	...
	Iterator<E> iterator();
	...
}

public interface Iterator<E> {
	boolean hasNext();
	Object next();
	void remove();
}
  • iterator()는 Collection 인터페이스에 정의된 메서드로 Collection 인터페이스의 자손인 List와 Set에도 포함되어 있다.
  • List와 Set을 구현하는 컬렉션은 iterator()가 특징에 맞게 작성되어있다.
    • 컬렉션 클래스에 대해 iterator()를 호출해 Iterator을 얻은 다음 반복문을 사용해 각 클래스의 요소를 읽어온다.
메서드 설명
boolean hasNext() - 읽어올 요소가 남아있는지 확인
- 있으면 true, 없으면 false
Object next() - 다음 요소를 읽어온다. next() 호출 전, hasNext를 호출해 읽어올 요소가 있는지 확인하기
void remove() - 다음 요소를 읽어온다. next() 호출 전, hasNext를 호출해 읽어올 요소가 있는지 확인하기
💡 참조변수의 타입은 인터페이스로 사용하자.
구현체에만 있는 메서드를 사용하는 것이 아니라면, 인터페이스로 참조변수를 선언하는 것이 좋다. 아래 코드가 전부 해당 인터페이스안에 있는 것만 사용하고 있다고 확신할 수 있기 때문에, 후에 구현체 변경에도 편하다.

 

<Map의 iterator()>

  • Map 인터페이스를 구현한 컬렉션 클래스는 key와 value를 쌍으로 저장하기 때무에 iterator()를 직접 호출할 수 없다
  • 대신 keySet()이나 entrySet()을 사용해 키와 값을 각각 따로 Set의 형태로 받아 다시 iterator()를 호출해야 Iterator를 얻을 수 있다.

<ListIterator와 Enumeration>

  • Enumeration은 컬렉션 프레임웍이 만들어지기 이전에 사용하던 것
  • ListInterator는 Iterator를 상속받아 기능을 추가한 것으로, 컬렉션의 요소에 접근할 때, Iterator는 단방향으로 이동할 수 있는데 반해 ListIterator는 양방향으로의 이동이 가능하다.
  • 다만, ArrayList나 LinkedList와 같이 List인터페이스를 구현한 컬렉션에만 사용할 수 있다.

 

1.6 Arrays

  • Arrays 클래스에는 배열을 다루는데 유용한 메서드가 정의되어 있다.
  • 같은 기능의 메서드가 배열의 타입만 다르게 오버로딩되어 있다.
  • Arrays에 정의된 메서드는 모두 static 메서드이다.

 

<배열의 복사 - copyOf(), copyOfRange()>

  • copyOf()는 배열 전체를, copyOfRange()는 배열의 일부를 복사해 새로운 배열을 만들어 반환한다.
    • range의 경우 마지막은 포함되지 않는다. copyOfRage(arr, 1, 3); // 1,2
    • copyOf(복사할 arr, 복사할 길이)가 복사할 arr보다 복사할 길이가 크다면, 기본값으로 채워진다.
    • 예)
      • Arrays.copyOf(arr, arr.length);
      • Arrays.copyOfRange(arr, 1, 3);

<배열 채우기 - fill(), setAll()>

  • fill()은 배열의 모든 요소를 지정된 값으로 채운다.
  • setAll()은 배열을 채우는데 사용할 함수형 인터페이스를 매개변수로 받는다.
    • 예)
      • Arrays.fill(arr, 9);
      • Arrays.setAll(arr, () → (int) (Math.random() * 5) + 1); // 람다식

<배열의 정렬과 검색 - sort(), binarySearch()>

  • sort()는 배열을 정렬할 때
  • binarySearch()는 배열에 저장된 요소를 검색할 때 사용한다.
  • binarySearch()는 배열에서 지정된 값이 저장된 위치(idx)를 찾아 반환하는데, 이때 반드시 배열이 정렬되어야 올바른 결과를 얻는다.
    • 여러개 일치하는 경우, 어떤 것이 올지는 모른다.
    • 예)
      • Arrays.binarySearch(arr, 2); // 2가 있는 index를 반환한다.
  • 순차 검색의 경우, 배열이 정렬되어 있을 필요는 없지만, 배열의 요소를 하나씩 비교하기 때문에 시간이 오래 걸림
  • 반면 이진 검색은 배열의 검색 범위를 반복적으로 절반씩 줄어가며 검색하기 때문에 검색속도가 매우 빠르다.
    • 단, 배열이 정렬되어 있는 경우만 사용할 수 있다는 단점이 있다.

<배열의 비교와 출력 - equals(), toString()>

  • toString()은 배열의 모든 요소를 문자열로 현하게 출력할 수 있다.
  • 일차원 배열에만 사용할 수 있으므로 다차원 배열에는 deepToString을 사용해야 한다.
    • 배열의 모든 요소를 재귀적으로 접근해 문자열을 구성해 2,3차원 가능
  • equals()는 두 배열에 저장된 모든 요소를 비교해 같으면 true, 다르면 false
  • 일차원 배열에만 사용할 수 있으므로 다차원 배열 비교에는 deepEquals()를 사용한다.

<배열을 List로 변환 - asList(Object … a)>

  • asList()는 배열을 List에 담아 반환한다.
  • 매개변수의 타입이 가변인수라 배열 생성없이 저장할 요소만 나열하는 것도 가능하다.
  • 🚨 asList()가 반환한 List의 크기는 변경할 수 없다.
    • 추가/삭제가 불가능하다.
    • 저장된 내용은 변경가능하다.
    • 크기를 변경할 수 있는 List가 필요하다면?
      • List list = new ArrayList(Arrays.asList(1,2,3,4,5));

<parallelXXX(), spliterator(), steam()>

  • parallel로 시작하는 메서드들은 보다 빠른 결과를 얻기 위해 여러 쓰레드가 작업을 나누어 처리한다.
  • spliterator()는 여러 스레드가 처리할 수 있게 하나의 작업을 여러 작업으로 나누는 Spliterator를 반환한다.
  • stream()은 컬렉션을 스트림으로 변환한다.