[JAVA] chapter 11 컬랙션 프레임워크 - 1

WOOK JONG KIM·2022년 9월 27일

자바의 정석

목록 보기
14/25
post-thumbnail

컬렉션 프레임워크

데이터 군을 저장하는 클래스들을 표준화한 설계

다수의 데이터를 다루는데 필요한 다양한 클래스 제공

ex)ArrayList, HashSet ...

핵심 인터페이스

출처 : https://kevinntech.tistory.com/16

인터페이스 ListSet의 공통된 부분이 인터페이스인 Collection

Collection 인터페이스 메서드

List 인터페이스

중복을 허용하면서 저장순서가 유지되는 컬렉션을 구현하는 데 사용

Set 인터페이스

저장순서가 유지되지 않는 컬렉션 클래스를 구현하는데 사용

Map 인터페이스

키와 값을 하나의 쌍으로 묶어서 저장하는 컬렉션 클래스 구현에 사용

저장 순서를 유지해야 하는 경우, Map인터페이스의 구현체 중 LinkedHashMap 사용

값 중복 허용하기에 values()는 Collection 반환

키의 중복을 허용하지 않기에 KeySet()는 Set 반환


ArrayList

기존의 Vector에서 원리와 기능적인 부분에서 개선 된 것'

Object배열(모든 종류의 객체를 담을 수 있음)을 이용해서 데이터를 순차적으로 저장

활용 예시

package ch11;

import java.util.ArrayList;
import java.util.Collections;

public class Ex11_1 
{

	public static void main(String[] args) 
	{	
		//사이즈 지정하여 ArrayList 생성
		ArrayList list1 = new ArrayList(10);
		
		//컴파일러가 AutoBoxing에 의해 기본형을 참조형으로 변환
		// add(5) -> add(new Integer(5));
		list1.add(new Integer(5));
		list1.add(new Integer(4));
		list1.add(new Integer(2));
		list1.add(new Integer(0));
		list1.add(new Integer(1));
		list1.add(new Integer(3));
		
		// ArrayList(Collection c)
		ArrayList list2 = new ArrayList(list1.subList(1, 4));
		print(list1, list2);
		
		//Collections 클래스
		Collections.sort(list1);
		Collections.sort(list2);
		print(list1,list2);
		
		System.out.println("list1.containsAll(list2):" + list1.containsAll(list2));
		
		list2.add("B");
		list2.add("C");
		list2.add(3,"A");
		print(list1, list2);
		
		list2.set(3,  "AA"); // 변경
		print(list1, list2);
		
		// 겹치는 부분만 남기고 나머지 삭제
		System.out.println("list1.retainAll(list2):" + list1.retainAll(list2));
		
		print(list1,list2);
		
		// list2에서 list1에 포함된 객체들을 삭제
		for(int i = list2.size() -1; i >= 0; i--)
		{
			if(list1.contains(list2.get(i)))
				list2.remove(i);
		}
		print(list1,list2);
		
	}
	
	static void print(ArrayList list1, ArrayList list2)
	{
		System.out.println("list1:"+list1);
		System.out.println("list2:"+list2);
		System.out.println();
	}
}
list1:[5, 4, 2, 0, 1, 3]
list2:[4, 2, 0]

list1:[0, 1, 2, 3, 4, 5]
list2:[0, 2, 4]

list1.containsAll(list2):true
list1:[0, 1, 2, 3, 4, 5]
list2:[0, 2, 4, A, B, C]

list1:[0, 1, 2, 3, 4, 5]
list2:[0, 2, 4, AA, B, C]

list1.retainAll(list2):true
list1:[0, 2, 4]
list2:[0, 2, 4, AA, B, C]

list1:[0, 2, 4]
list2:[AA, B, C]

ArrayList의 추가 삭제

ArrayList에 제거된 data[2]를 삭제하는 경우

list.remove(2) 호출

  1. 삭제할 데이터의 아래에 있는 데이터를 한 칸 씩 위로 복사해서 삭제할 데이터 덮음

  2. 데이터가 모두 한칸 씩 위로 이동한 후, 마지막 데이터 null로 변경

  3. 데이터가 삭제되어 데이터 갯수가 줄었음으로 size 1 감소

배열의 중간에 객체를 추가하거나 삭제하는 경우 작업 시간 오래 걸림


LinkedList

배열 장점

배열의 구조가 간단하고 데이터를 읽는 시간(Access time)이 짧다

배열 단점

배열을 한번 생성하면 크기를 변경할 수 없다

-> 변경 시 새로운 배열 생성 후 데이터를 복사한 후, 참조를 변경해야 함

배열의 중간에 값을 추가 또는 삭제하는 경우 오래 걸림

-> 데이터 추가,삭제 시 다른 데이터를 복사해서 이동시켜야함

순자적인 데이터 추가(끝에 추가) + 삭제(끝부터 삭제)는 빠르다


이러한 배열의 단점을 보완하기 위해 나타난 자료구조 -> LinkedList

불 연속적으로 존재하는 데이터 서로 연결

하지만 첫 노드 부터 접근해야 하기에 데이터의 접근성이 나쁘다!

단일 연결 리스트

이동 방향이 단방향이기 때문에 다음 요소에 대한 접근은 쉽지만 이전 요소에 대한 `접근 어렵'

이중 연결 리스트

참조 변수를 하나 더 추가하여 다음 요소에 대한 참조 + 이전 요소 참조

Java의 LinkedList는 이중 연결 리스트로 구현

이중 원형 연결 리스트

이중 연결 리스트의 첫번째 요소와 마지막 요소를 연결시켜 접근성 향상

vs ArrayList

  • 순차적으로 데이터 추가/삭제 -> ArrayList가 빠름

  • 비순차적 데이터 추가/삭제 -> LinkedList가 빠름

  • 데이터 접근 -> ArrayList가 빠름

배열의 접근 시간

인덱스 n의 데이터 주소 = 배열의 주소 + n * 데이터 타입의 크기


Stack & Queue

스택

LIFO(Last in First Out)

위와 같이 0,1,2 순서로 넣었다면 꺼낼땐 2,1,0 순서로 꺼내게 된다

수식계산, 수식 괄호 검사, 워드프로세서의 undo/redo, 웹브라우저의 앞/뒤로

FIFO(First In First Out)

최근 사용 문서, 인쇄작업 대기 목록, 버퍼(buffer)

순차적으로 데이터 추가 삭제할 때(Stack)는 ArrayList가 적합

삭제 시 처음 데이터를 삭제하는 경우(Queue)는 LinkedList가 적합

Queue는 인터페이스라서 구현한 클래스를 사용하면 됨!

import java.util.*;

public class Ex11_2 {

	public static void main(String[] args) {
		Stack st = new Stack();
		Queue q = new LinkedList(); // Queue인터페이스의 구현체는 LinkedList 사용
		
		st.push("0");
		st.push("1");
		st.push("2");
		
		q.offer("0");
		q.offer("1");
		q.offer("2");
		
		System.out.println(" =stack=");
		while(!st.empty())
		{
			System.out.println(st.pop()); // 요소 하나씩 출
		}	
		
		System.out.println(" = Queue = ");
		while(!q.isEmpty())
		{
			System.out.println(q.poll());
		}

	}

}
 =stack=
2
1
0
 = Queue = 
0
1
2

인터페이스 구현한 클래스를 찾을 때는 Java API 문서에서
All Known Implementing Classes 항목 참고

활용

unix의 history 명령어 Queue로 구현

package ch11;

import java.util.*;

public class Ex11_4 {
	static Queue q = new LinkedList();
	static final int MAX_SIZE = 5; // Queue에 최대 5개만 저장되도록 

	public static void main(String[] args) {
		System.out.println("help를 입력하면 도움말을 볼 수 있습니다.");
		
		while(true)
		{
			System.out.print(">>");
			try
			{
				// 화면으로 부터 라인단위 입력 받음
				Scanner s = new Scanner(System.in);
				String input = s.nextLine().trim();
				
				if ("".equals(input)) continue;
				
				if(input.equalsIgnoreCase("q"))
				{
					System.exit(0);
				}else if(input.equalsIgnoreCase("help")) {
					System.out.println(" help - 도움말을 보여줍니다.");
					System.out.println(" q또는 Q - 프로그램을 종료합니다.");
					System.out.println(" history - 최근 입력한 명령어를 " + MAX_SIZE + "개 보여줍니다.");
				} else if(input.equalsIgnoreCase("history")) {
					int i = 0;
					// 입력받은 명령어를 저장
					save(input);
					
					//LinkedList의 내용을 보여줌
					LinkedList tmp = (LinkedList) q;
					ListIterator it = tmp.listIterator();
					
					while(it.hasNext())
						System.out.println(++i+"." + it.next());
				} else {
					save(input);
					System.out.println(input);
				} }catch (Exception e) {
				 System.out.println("입력오류입니다. ");
				 }
			
		}

	}
	
		public static void save(String input)
		{
			//queue에 저장
			if(!"".equals(input))
				q.offer(input);
			
			if(q.size() > MAX_SIZE)
				q.remove();
		}
}


Iterator

Iterator, ListIterator, Enumeration 모두 컬렉션에 저장된 요소접근하는데 사용되는 인터페이스

컬렉션에 저장된 요소들을 읽어오는 방법 표준화 (각각의 자료구조가 다르기에!)

  • Iterator : 컬렉션 요소 접근

  • ListIterator : Iterator의 양방향 조회 기능 추가

  • Enumeration : Iterator 구버젼

IteratorCollection 인터페이스에 정의 된 메서드이며, List,Set 인터페이스를 구현하는 컬렉션은 iterator()가 특성에 맞게 작성되어 있음

package ch11;

import java.util.*;

public class Ex11_5 {

	public static void main(String[] args) {
		ArrayList list = new ArrayList();
		list.add("1");
		list.add("2");
		list.add("3");
		list.add("4");
		list.add("5");
		
        // Iterator는 1회용이라 한번 쓰면 다시 호출해야함!
		Iterator it = list.iterator();
		
		while(it.hasNext())
		{
			Object obj = it.next();
			System.out.println(obj);
		}
	
		// list요소를 읽어 올때는 for문 또는 list.get(i)를 통해 모든 요소를 출력할 수도 있다.

	}

}

Map과 Iterator

Map에는 Iterator -> X , keySet() + entrySet() + values()와 같은 메서드 사용해야 함

여기서 entry는 <K,V> 형태

Map map = new HashMap();
...
Iterator it = map.entrySet().Iterator();

// 위 문장은 밑 두문장을 하나로 합친것
Set eSet = map.entrySet();
Iterator it = eSet.Iterator();

Arrays 메서드

배열을 다루는데 유용한 메서드(모두 static)

배열의 출력 - toString()

static String toString(boolean[] a)
static String toString(byte[] a)
static String toString(char[] a)
static String toString(short[] a)
static String toString(int[] a)
static String toString(long[] a)
static String toString(double[] a)
static String toString(Object[] a)

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

copyOf()는 배열 전체, copyOfRange()는 배열 일부

int[] arr = {0,1,2,3,4};
int[] arr2 = Arrays.copyOf(arr, arr.length); // arr2 = [0,1,2,3,4]
int[] arr3 = Arrays.copyOf(arr, 3);              // arr3 = [0,1,2]
int[] arr4 = Arrays.copyOf(arr, 7);              // arr4 = [0,1,2,3,4,0,0]
int[] arr5 = Arrays.copyOfRange(arr, 2, 4);  // arr5 = [2,3]
int[] arr6 = Arrays.copyOfRange(arr, 0, 7);  // arr6 = [0,1,2,3,4,0,0]

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

  • fill()은 배열의 모든 요소를 지정된 값으로 채운다.

  • setAll() 배열을 채우는데 사용할 함수형 인터페이스를 매개변수로 받는다.

  • 이 메서드를 호출할 때는 함수형 인터페이스를 구현한 객체를 매개변수로 지정하던가 아니면 람다식을 지정해야한다.

int [] arr = new int[5];
Arrays.fill(arr,9);   // arr = [9,9,9,9,9]
Arrays.setAll(arr, (i) -> (int)(Math.random()*5)+1); //arr=[1,5,2,1,1]

배열의 정렬과 검색

  • sort()는 배열을 정렬할 때

  • binarySearch()는 배열에 저장된 요소를 검색

  • 이진 검색은 배열의 검색할 범위를 반복적으로 절반으로 줄여 나가기에 검색속도가 빠르다

  • binarySearch()는 배열에서 지정된 값이 저장된 위치(index)를 찾아서 반환하는데, 반드시 배열이 정렬된 상태이어야 올바른 결과를 얻는다.

  • 만일 검색한 값과 일치하는 요소들이 여러 개 있다면, 이 중에서 어떤 것의 위치가 반환될지는 알 수 없다.

int [] arr = {3,2,0,1,4};
int idx = Arrays.binarySearch(arr,2); // idx = -5 <-- 잘못된 결과

Arrays.sort(arr); // 배열 arr을 정렬한다.
System.out.println(Arrays.toString(arr)); // [0,1,2,3,4]
int idx = Arrays.binarySearch(arr, 2);    // idx=2 <-- 올바른 결과

배열의 비교와 출력 - toString(), deepToString()

  • toString()배열의 모든 요소를 문자열로 편하게 출력할 수 있다(일차원 배열에만 사용)

  • deepToString() 다차원 배열에 사용

  • 2차원뿐만 아니라 3차원 이상의 배열에도 동작

int [] arr = {0,1,2,3,4};
int[][] arr2D = {{11,12}, {21,22}};

System.out.println(Arrays.toString(arr)); // [0,1,2,3,4]
System.out.println(Arrays.deepToString(arr2D)); // [[11,12],[21,22]]

다차원 배열 비교 - equals(), deepEquals()

  • equals()는 두 배열에 저장된 모든 요소를 비교해서 같으면 true, 다르면 false를 반환
  • 일차원 배열에만 가능
  • deepEquals() 다차원 배열 비교 사용
String[][] str2D = new String[][]{{"aaa","bbb"},{"AAA","BBB"}};
String[][] str2D2 = new String[][]{{"aaa","bbb"},{"AAA","BBB"}};

// equals()로 비교 시 문자열을 비교하는 것이 아니라 배열의 주소를 비교

System.out.println(Arrays.equals(str2D, str2D2));     // false
System.out.println(Arrays.deepEquals(str2D, str2D2);  // true

배열을 List로 변환 - asList(Object..a)

  • asList()는 배열을 List에 담아서 반환한다.
List list = Arrays.asList(new Integer[]{1,2,3,4,5});  // list =[1,2,3,4,5]
List list = Arrays.asList(1,2,3,4,5);                 // lsit =[1,2,3,4,5]
list.add(6); // UnsupportedOperationException 예외 발생
  • asList()가 반환한 List의 크기를 변경할 수 없다는 것

  • 추가 또는 삭제가 불가능하다.

  • 저장된 내용은 변경가능

List list = new ArrayList(Arrays.asList(1,2,3,4,5));
profile
Journey for Backend Developer

0개의 댓글