Java Category/Java

[Java] 컬렉션 프레임워크(LIFO & FIFO, 동기화된 컬렉션, 수정할 수 없는 컬렉션)

ReBugs 2023. 8. 4.

이 게시글은 이것이 자바다(저자 : 신용권, 임경균)의 책과 동영상 강의를 참고하여 개인적으로 정리하는 글임을 알립니다. 


LIFO & FIFO

LIFO는 스택(Stack) 자료구조를 뜻하고, FIFO는 큐(Queue) 자료구조를 뜻한다.

컬렉션 프레임워크는 LIFO 자료구조를 제공하는 스택 클래스와 FIFO 자료구조를 제공하는 큐 인터페이스를 제공하고 있다.

 

 

Stack

Stack 클래스는 LIFO 자료구조를 구현한 클래스이다.

아래는 Stack 객체를 생성하는 방법이다.

Stack<E> stack = new Stack<E>();
Stack<E> stack = new Stack<>();

 

Stack 클래스는 Vector 클래스를 상속했다.
따라서 Stack 객체는 동기화 처리가 되어있다.
즉, 멀티스레딩 환경에서 안전하게 처리 가능하다.

 

아래는 Stack 클래스의 주요 메소드이다.

리턴 타입 메소드 설명
E push(E item) 주어진 객체를 스택에 넣는다.
E pop() 스택의 맨 위 객체를 빼낸다.
E lastElement() 스택의 맨 위 객체를 빼내지 않고 리턴만 한다.

 

사용 예제

더보기

Coin.java

public class Coin {
	private int value;

	public Coin(int value) {
		this.value = value;
	}

	public int getValue() {
		return value;
	}
}

 

StackExample.java

import java.util.Stack;

public class StackExample {
	public static void main(String[] args) {
		//Stack 컬렉션 생성
		Stack<Coin> coinBox = new Stack<Coin>();
		
		//동전 넣기
		coinBox.push(new Coin(100));
		coinBox.push(new Coin(50));
		coinBox.push(new Coin(500));
		coinBox.push(new Coin(10));

		//동전을 하나씩 꺼내기
		while(!coinBox.isEmpty()) {
			Coin coin = coinBox.pop();
			System.out.println("꺼내온 동전 : " + coin.getValue() + "원");
		}
	}
}
/*
꺼내온 동전 : 10원
꺼내온 동전 : 500원
꺼내온 동전 : 50원
꺼내온 동전 : 100원
*/

 


 

Queue

Queue 인터페이스는 FIFO 자료구조에서 사용되는 메소드를 정의하고 있다.

아래는 Queue 인터페이스에 정의되어 있는 메소드이다.

리턴 타입 메소드 설명
boolean offer(E e) 또는 add(E e) 주어진 객체를 큐에 넣는다.
E poll() 큐에서 객체를 빼낸다.
E peek() 큐의 맨 앞의 객체를 빼내지 않고 리턴만 한다.

 

add()와 offer()의 차이
add()메소드와 offer() 메소드의 차이점은 문제 상황에서 예외를 발생시키는가 아니면 false를 리턴하느냐에 있다.
add()는 삽입에 실패할 경우 예외를 발생시키지만, offer는 false를 리턴한다.

 

Queue 인터페이스를 구현한 대표적인 클래스는 LinkedList이다.

그렇기 때문에 LinkedList 객체를 Queue 인터페이스 변수에 아래와 같이 대입할 수 있다.

Queue<E> queue = new LinkedList<E>();
Queue<E> queue = new LinkedList<>();

 

사용 예제

더보기

Message.java

public class Message {
	public String command;
	public String to;

	public Message(String command, String to) {
		this.command = command;
		this.to = to;
	}
}

 

QueueExample.java

import java.util.LinkedList;
import java.util.Queue;

public class QueueExample {
	public static void main(String[] args) {
		//Queue 컬렉션 생성
		Queue<Message> messageQueue = new LinkedList<>();
		
		//메시지 넣기
		messageQueue.offer(new Message("sendMail", "홍길동"));
		messageQueue.offer(new Message("sendSMS", "신용권"));
		messageQueue.offer(new Message("sendKakaotalk", "감자바"));
		
		//메시지를 하나씩 꺼내어 처리
		while(!messageQueue.isEmpty()) {
			Message message = messageQueue.poll();
			switch(message.command) {
				case "sendMail":
					System.out.println(message.to + "님에게 메일을 보냅니다.");
					break;
				case "sendSMS":
					System.out.println(message.to + "님에게 SMS를 보냅니다.");
					break;
				case "sendKakaotalk": 
					System.out.println(message.to + "님에게 카카오톡를 보냅니다.");
					break;
			}
		}
	}
}
/*
홍길동님에게 메일을 보냅니다.
신용권님에게 SMS를 보냅니다.
감자바님에게 카카오톡를 보냅니다.
*/

 


 

동기화된 컬렉션

컬렉션 프레임워크의 대부분의 클래스들은 싱글 스레드 환경에서 사용할 수 있도록 설계되어 있다.

Vector와 HashTable은 동기화된 메소드로 구성되어 있기 때문에 멀티 스레드 환경에서 안전하게 처리할 수 있다.

하지만 ArrayList와 HashSet, HashMap은 동기화된 메소드로 구성되어 있지 않아 멀티 스레드 환경에서 안전하지 않다.

 

경우에 따라서 ArrayList와 HashSet, HashMap을 멀티 스레드 환경에서 사용해야 할 때가 있다.

이런 경우를 대비해서 컬렉션 프레임워크는 비동기화된 메소드를 동기화된 메소드로 래핑하는 Collections의 synchronizedXXX() 메소드를 제공한다.

리턴 타입 메소드(매개변수) 설명
List<T> synchronized(List<T> list) List를 동기화된 List로 리턴
Map<K, V> synchronized(Map <K, V> m) Map을 동기화된 Map으로 리턴
Set<T> synchronized(Set<T> s) Set을 동기화된 Set으로 리턴

 

이 메소드들은 매개값으로 비동기화된 컬렉션을 대입하면 동기화된 컬렉션을 리턴한다.

 

사용 예제

더보기

SynchronizedMapExample.java

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class SynchronizedMapExample {
	public static void main(String[] args) {
		//Map 컬렉션 생성
		Map<Integer, String> map = Collections.synchronizedMap(new HashMap<>());
			
		//작업 스레드 객체 생성
		Thread threadA = new Thread() {
			@Override
			public void run() {
				//객체 1000개 추가
				for(int i=1; i<=1000; i++) {
					map.put(i, "내용"+i);
				}
			}
		};
			
		//작업 스레드 객체 생성
		Thread threadB = new Thread() {
			@Override
			public void run() {
				//객체 1000개 추가
				for(int i=1001; i<=2000; i++) {
					map.put(i, "내용"+i);
				}
			}
		};
		
		//작업 스레드 실행
		threadA.start();
		threadB.start();
		
		//작업 스레드들이 모두 종료될 때까지 메인 스레드를 기다리게 함
		try {
			threadA.join();
			threadB.join();
		} catch(Exception e) {
		}
			
		//저장된 총 객체 수 얻기
		int size = map.size();
		System.out.println("총 객체 수: " + size);
		System.out.println();
	}
}

 

 


 

수정할 수 없는 컬렉션

수정할 수 없는 컬렉션이란 요소를 추가 또는 삭제를 할 수 없는 컬렉션을 뜻한다.

 

정적 메소드 of()

List, Set, Map 인터페이스의 정적 메소드인 of()로 수정할 수 없도록 할 수 있다.

List<E> immutableList = List.of(E...elements);
Set<E> immutableSet = Set.of(E...elements);
Map<K, V> immutableMap = Map.of(K k1, V v1, K k2, V v2 ...);

 

정적 메소드 copyOf()

List, Set, Map 인터페이스의 정적 메소드인 copyOf()로 수정할 수 없도록 할 수 있다.

List<E> immutableList = List.copyOf(E...elements);
Set<E> immutableSet = Set.copyOf(E...elements);
Map<K, V> immutableMap = Map.copyOf(K k1, V v1, K k2, V v2 ...);

 

배열로부터 수정할 수 없는 List 컬렉션 만들기

String[] arr = {"A", "B", "C"};
List<String> immutableList = Arrays.asList(arr);

 

사용예제

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class ImmutableExample {
	public static void main(String[] args) {
		//List 불변 컬렉션 생성
		List<String> immutableList1 = List.of("A", "B", "C");
		//immutableList1.add("D"); (x)

		//Set 불변 컬렉션 생성
		Set<String> immutableSet1 = Set.of("A", "B", "C");
		//immutableSet1.remove("A"); (x)

		//Map 불변 컬렉션 생성
		Map<Integer, String> immutableMap1 = Map.of(
				1, "A",
				2, "B",
				3, "C"
				);
		//immutableMap1.put(4, "D"); (x)
		
		//List 컬렉션을 불변 컬렉션으로 복사
		List<String> list = new ArrayList< >();
		list.add("A");
		list.add("B");
		list.add("C");
		List<String> immutableList2 = List.copyOf(list);
		
		//Set 컬렉션을 불변 컬렉션으로 복사
		Set<String> set= new HashSet< >();
		set.add("A");
		set.add("B");
		set.add("C");
		Set<String> immutableSet2 = Set.copyOf(set);
			
		//Map 컬렉션을 불변 컬렉션으로 복사
		Map<Integer, String> map = new HashMap< >();
		map.put(1, "A");
		map.put(2, "B");
		map.put(3, "C");
		Map<Integer, String> immutableMap2 = Map.copyOf(map);
		
		//배열로부터 List 불변 컬렉션 생성
		String[] arr = { "A", "B", "C" };
		List<String> immutableList3 = Arrays.asList(arr);
	}
}

댓글