반응형

Stack과 Queue

Stack<E> 클래스

  • Stack클래스는 List컬렉션 클래스의 Vector클래스를 상속받아, 전형적인 스택 메모리 구조의 클래스를 제공합니다.
  • 스택 메모리 구조는 선형 메모리 공간에 데이터를 저장하면서 후입선출(LIFO)의 시멘틱을 따르는 자료 구조입니다.
  • 즉, 가장 나중에 저장된(push) 데이터가 가장 먼저 인출(pop)되는 구조입니다.

Stack클래스는 스택 메모리 구조를 표현하기 위해, Vector클래스의 메소드를 5개만 상속받아 사용합니다.

메소드 설명
boolean empty() 해당 스택이 비어 있으면 true를 비어 있지 않으면 false를 반환함.
E peek() 해당 스택의 제일 상단에 있는(제일 마지막으로 저장된) 요소를 반환함.
E pop() 해당 스택의 제일 상단에 있는(제일 마지막으로 저장된) 요소를 반환하고, 해당 요소를 스택에서 제거함.
E push(E item) 해당 스택의 제일 상단에 전달된 요소를 삽입함.
int search(Object o) 해당 스택에서 전달된 객체가 존재하는 위치의 인덱스를 반환함. 이때 인덱스는 제일 상단에 있는(제일 마지막으로 저장된) 요소의 위치부터 0이 아닌 1부터 시작함.

더욱 복잡하고 빠른 스택을 구현하고 싶다면 Deque인터페이스를 구현한 ArrayDeque클래스를 사용하면 됩니다.

Deque<Integer> st = new ArrayDeque<Integer>();

다음 예제는 여러 Stack 메소드를 이용하여 스택 메모리 구조를 구현한 예제입니다.

Stack<Integer> st = new Stack<Integer>(); // 스택의 생성
//Deque<Integer> st = new ArrayDeque<Integer>();

// push() 메소드를 이용한 요소의 저장
st.push(4);
st.push(2);
st.push(3);
st.push(1);

// peek() 메소드를 이용한 요소의 반환
System.out.println(st.peek());
System.out.println(st);

// pop() 메소드를 이용한 요소의 반환 및 제거
System.out.println(st.pop());
System.out.println(st);

// search() 메소드를 이용한 요소의 위치 검색
System.out.println(st.search(4));
System.out.println(st.search(3));

실행 결과

Queue<E> 인터페이스

  • 클래스로 구현된 스택과는 달리 자바에서 큐 메모리 구조는 별도의 인터페이스 형태로 제공됩니다.
  • 이러한 Queue인터페이스를 상속받는 하위 인터페이스는 다음과 같습니다.
    1. Deque<E>
    2. BlockingDeque<E>
    3. BlockingQueue<E>
    4. TransferQueue<E>
  • 따라서 Queue인터페이스를 직간접적으로 구현한 클래스는 상당히 많습니다.
  • 그중에서도 Deque인터페이스를 구현한 LinkedList클래스가 큐 메모리 구조를 구현하는 데 가장 많이 사용됩니다.
  • 큐 메모리 구조는 선형 메모리 공간에 데이터를 저장하면서 선입선출(FIFO)의 시멘틱을 따르는 자료 구조입니다.
  • 즉, 가장 먼저 저장된(push) 데이터가 가장 먼저 인출(pop)되는 구조입니다.

Queue인터페이스는 큐 메모리 구조를 표현하기 위해, 다음과 같은 Collection 인터페이스 메소드만을 상속받아 사용합니다.

메소드 설명
boolean add(E e) 해당 큐의 맨 뒤에 전달된 요소를 삽입함.
만약 삽입에 성공하면 true를 반환하고, 큐에 여유 공간이 없어 삽입에 실패하면 IllegalStateException을 발생시킴.
E element() 해당 큐의 맨 앞에 있는(제일 먼저 저장된) 요소를 반환함.
boolean offer(E e) 해당 큐의 맨 뒤에 전달된 요소를 삽입함.
E peek() 해당 큐의 맨 앞에 있는(제일 먼저 저장된) 요소를 반환함.만약 큐가 비어있으면 null을 반환함.
E poll() 해당 큐의 맨 앞에 있는(제일 먼저 저장된) 요소를 반환하고, 해당 요소를 큐에서 제거함.만약 큐가 비어있으면 null을 반환함.
E remove() 해당 큐의 맨 앞에 있는(제일 먼저 저장된) 요소를 제거함.

더욱 복잡하고 빠른 큐를 구현하고 싶다면 Deque인터페이스를 구현한 ArrayDeque클래스를 사용하면 됩니다.

Deque<Integer> qu = new ArrayDeque<Integer>();

다음 예제는 여러 LinkedList메소드를 이용하여 큐 메모리 구조를 구현한 예제입니다.

LinkedList<String> qu = new LinkedList<String>(); // 큐의 생성
//Deque<String> qu = new ArrayDeque<String>();

// add() 메소드를 이용한 요소의 저장
qu.add("넷");
qu.add("둘");
qu.add("셋");
qu.add("하나");

// peek() 메소드를 이용한 요소의 반환
System.out.println(qu.peek());
System.out.println(qu);

// poll() 메소드를 이용한 요소의 반환 및 제거
System.out.println(qu.poll());
System.out.println(qu);

// remove() 메소드를 이용한 요소의 제거
qu.remove("하나");
System.out.println(qu);


[참고 자료]

http://www.tcpschool.com/java/java_collectionFramework_stackQueue

반응형
반응형

List 컬렉션 클래스

List인터페이스를 구현한 모든 List컬렉션 클래스는 다음과 같은 특징을 가집니다.

  1. 요소의 저장 순서가 유지됩니다.
  2. 같은 요소의 중복 저장을 허용합니다.

대표적인 List컬렉션 클래스에 속하는 클래스는 다음과 같습니다.

  1. ArrayList<E>
  2. LinkedList<E>
  3. Vector<E>
  4. Stack<E>

ArrayList<E> 클래스

  • ArrayList클래스는 가장 많이 사용되는 컬렉션 클래스 중 하나입니다.
  • JDK 1.2부터 제공된 ArrayList클래스는 내부적으로 **배열**을 이용하여 요소를 저장합니다.
  • ArrayList클래스는 배열을 이용하기 때문에 인덱스를 이용해 배열 요소에 빠르게 접근할 수 있습니다.
  • 하지만 배열은 크기를 변경할 수 없는 인스턴스이므로, 크기를 늘리기 위해서는 새로운 배열을 생성하고 기존의 요소들을 옮겨야 하는 복잡한 과정을 거쳐야 합니다.
  • 물론 이 과정은 자동으로 수행되지만, 요소의 추가 및 삭제 작업에 걸리는 시간이 매우 길어지는 단점을 가지게 됩니다.

다음 예제는 여러 ArrayList메소드를 이용하여 리스트를 생성하고 조작하는 예제입니다.

ArrayList<Integer> arrList = new ArrayList<Integer>();

// add() 메소드를 이용한 요소의 저장
arrList.add(40);
arrList.add(20);
arrList.add(30);
arrList.add(10);

// for 문과 get() 메소드를 이용한 요소의 출력
for (int i = 0; i < arrList.size(); i++) {
    System.out.print(arrList.get(i) + " ");
}
System.out.print("\n");

// remove() 메소드를 이용한 요소의 제거
arrList.remove(1);

// Enhanced for 문과 get() 메소드를 이용한 요소의 출력
// 반복 대상:arrList
// 반복 대상의 요소:e
for (int e : arrList) {
    System.out.print(e + " ");
}
System.out.print("\n");

// Collections.sort() 메소드를 이용한 요소의 정렬
Collections.sort(arrList);
 
// iterator() 메소드와 get() 메소드를 이용한 요소의 출력
Iterator<Integer> iter = arrList.iterator();
while (iter.hasNext()) {
    System.out.print(iter.next() + " ");
}
System.out.print("\n");

// set() 메소드를 이용한 요소의 변경
arrList.set(0, 20);

for (int e : arrList) {
    System.out.print(e + " ");
}
System.out.print("\n");

// size() 메소드를 이용한 요소의 총 개수
System.out.println("리스트의 크기 : " + arrList.size());

실행 결과

위 예제처럼 컬렉션 클래스의 요소를 출력하는 방법에는 for문과 enhanced for문, iterator()메소드를 이용한 방법 등 다양한 방법을 사용할 수 있습니다.

LinkedList<E> 클래스

  • LinkedList클래스는 ArrayList클래스가 배열을 이용하여 요소를 저장함으로써 발생하는 단점을 극복하기 위해 고안되었습니다.
  • JDK 1.2부터 제공된 LinkedList클래스는 내부적으로 연결 리스트(linked list)를 이용하여 요소를 저장합니다.
  • 배열은 저장된 요소가 순차적으로 저장됩니다.
  • 하지만 연결 리스트는 저장된 요소가 비순차적으로 분포되며, 이러한 요소들 사이를 링크(link)로 연결하여 구성합니다.

다음 요소를 가리키는 참조만을 가지는 연결 리스트를 단일 연결 리스트(singly linked list)라고 합니다.

이러한 단일 연결 리스트는 요소의 저장과 삭제 작업이 다음 요소를 가리키는 참조만 변경하면 되므로, 아주 빠르게 처리될 수 있습니다.

하지만 단일 연결 리스트는 현재 요소에서 이전 요소로 접근하기가 매우 어렵습니다.

따라서 이전 요소를 가리키는 참조도 가지는 이중 연결 리스트(doubly linked list)가 좀 더 많이 사용됩니다.

LinkedList클래스도 위와 같은 이중 연결 리스트를 내부적으로 구현한 것입니다.

또한, LinkedList클래스 역시 List 인터페이스를 구현하므로, ArrayList클래스와 사용할 수 있는 메소드가 거의 같습니다.

 

다음 예제는 여러 LinkedList메소드를 이용하여 리스트를 생성하고 조작하는 예제입니다.

LinkedList<String> lnkList = new LinkedList<String>();

// add() 메소드를 이용한 요소의 저장

lnkList.add("넷");
lnkList.add("둘");
lnkList.add("셋");
lnkList.add("하나");

// for 문과 get() 메소드를 이용한 요소의 출력
for (int i = 0; i < lnkList.size(); i++) {
    System.out.print(lnkList.get(i) + " ");
}
System.out.print("\n");

// remove() 메소드를 이용한 요소의 제거
lnkList.remove(1);

// Enhanced for 문과 get() 메소드를 이용한 요소의 출력
for (String e : lnkList) {
    System.out.print(e + " ");
}
System.out.print("\n");

// set() 메소드를 이용한 요소의 변경
lnkList.set(2, "둘");

for (String e : lnkList) {
    System.out.print(e + " ");
}
System.out.print("\n");

// size() 메소드를 이용한 요소의 총 개수
System.out.println("리스트의 크기 : " + lnkList.size());

위의 예제를 살펴보면 앞선 예제와 연결 리스트를 생성하는 한 줄의 코드만이 다른 것을 확인할 수 있습니다.

이처럼 ArrayListLinkedList의 차이는 사용 방법이 아닌, 내부적으로 요소를 저장하는 방법에 있습니다.

  • Vector클래스는 JDK 1.0부터 사용해 온 ArrayList클래스와 같은 동작을 수행하는 클래스입니다.
  • 현재의 Vector클래스는 ArrayList클래스와 마찬가지로 List인터페이스를 상속받습니다.
  • 따라서 Vector클래스에서 사용할 수 있는 메소드는 ArrayList클래스에서 사용할 수 있는 메소드와 거의 같습니다.
  • 하지만 현재에는 기존 코드와의 호환성을 위해서만 남아있으므로, Vector클래스보다는 ArrayList클래스를 사용하는 것이 좋습니다.

List 인터페이스 메소드

메소드 설명
boolean add(E e) 해당 리스트(list)에 전달된 요소를 추가함. (선택적 기능)
void add(int index, E e) 해당 리스트의 특정 위치에 전달된 요소를 추가함. (선택적 기능)
void clear() 해당 리스트의 모든 요소를 제거함. (선택적 기능)
boolean contains(Object o) 해당 리스트가 전달된 객체를 포함하고 있는지를 확인함.
boolean equals(Object o) 해당 리스트와 전달된 객체가 같은지를 확인함.
E get(int index) 해당 리스트의 특정 위치에 존재하는 요소를 반환함.
boolean isEmpty() 해당 리스트가 비어있는지를 확인함.
Iterator<E> iterator() 해당 리스트의 반복자(iterator)를 반환함.
boolean remove(Object o) 해당 리스트에서 전달된 객체를 제거함. (선택적 기능)
boolean remove(int index) 해당 리스트의 특정 위치에 존재하는 요소를 제거함. (선택적 기능)
E set(int index, E e) 해당 리스트의 특정 위치에 존재하는 요소를 전달 받은 객체로 대체함. (선택적 기능)
int size() 해당 리스트의 요소의 총 개수를 반환함.
Object[] toArray() 해당 리스트의 모든 요소를 Object 타입의 배열로 반환함.

[참고 자료]

http://www.tcpschool.com/java/java_collectionFramework_list

반응형
반응형

컬렉션 프레임워크(collection framework)란?

  • 자바에서 컬렉션 프레임워크(collection framework)란 다수의 데이터를 쉽고 효과적으로 처리할 수 있는 표준화된 방법을 제공하는 클래스의 집합을 의미합니다
  • 즉, 데이터를 저장하는 자료 구조와 데이터를 처리하는 알고리즘을 구조화하여 클래스로 구현해 놓은 것입니다.
  • 이러한 컬렉션 프레임워크는 자바의 인터페이스(interface)를 사용하여 구현됩니다.

컬렉션 프레임워크 주요 인터페이스

  • 컬렉션 프레임워크에서는 데이터를 저장하는 자료 구조에 따라 다음과 같은 핵심이 되는 주요 인터페이스를 정의하고 있습니다.
    1. List 인터페이스
    2. Set 인터페이스
    3. Map 인터페이스
  • 이 중에서 ListSet인터페이스는 모두 Collection 인터페이스를 상속받지만, 구조상의 차이로 인해 Map인터페이스는 별도로 정의됩니다.
  • 따라서 List인터페이스와 Set인터페이스의 공통된 부분을 Collection 인터페이스에서 정의하고 있습니다.

주요 인터페이스 간의 상속 관계

자바에서 컬렉션 프레임워크를 구성하고 있는 인터페이스 간의 상속 관계는 다음 그림과 같습니다.

위의 그림에서 <E>나 <K, V>라는 것은 컬렉션 프레임워크를 구성하는 모든 클래스가 제네릭으로 표현되어 있음을 알려줍니다.

제네릭의 개념에 대한 더 자세한 사항은 자바 제네릭 수업에서 확인할 수 있습니다.

주요 인터페이스의 간략한 특징

자바에서 컬렉션 프레임워크를 구성하고 있는 주요 인터페이스의 간략한 특징은 다음과 같습니다.

인터페이스 설명 구현 클래스
List<E> 순서가 있는 데이터의 집합으로, 데이터의 중복을 허용함. Vector, ArrayList, LinkedList, Stack, Queue
Set<E> 순서가 없는 데이터의 집합으로, 데이터의 중복을 허용하지 않음. HashSet, TreeSet
Map<K, V> 키와 값의 한 쌍으로 이루어지는 데이터의 집합으로, 순서가 없음.
이때 키는 중복을 허용하지 않지만, 값은 중복될 수 있음.
HashMap, TreeMap, Hashtable, Properties

컬렉션 클래스(collection class)

  • 컬렉션 프레임워크에 속하는 인터페이스를 구현한 클래스를 컬렉션 클래스(collection class)라고 합니다.
  • 컬렉션 프레임워크의 모든 컬렉션 클래스는 List와 Set, Map 인터페이스 중 하나의 인터페이스를 구현하고 있습니다.
  • 또한, 클래스 이름에도 구현한 인터페이스의 이름이 포함되므로 바로 구분할 수 있습니다.
  • Vector나 Hashtable과 같은 컬렉션 클래스는 예전부터 사용해 왔으므로, 기존 코드와의 호환을 위해 아직도 남아 있습니다.
  • 하지만 기존에 사용하던 컬렉션 클래스를 사용하는 것보다는 새로 추가된 ArrayList나 HashMap 클래스를 사용하는 것이 성능 면에서도 더 나은 결과를 얻을 수 있습니다.
  • 다음 예제는 ArrayList클래스를 이용하여 리스트를 생성하고 조작하는 예제입니다.
import java.util.*;

public class Collection01 {
    public static void main(String[] args) {
        // 리스트 생성
        ArrayList<String> arrList = new ArrayList<String>();

        // 리스트에 요소의 저장
        arrList.add("넷");
        arrList.add("둘");
        arrList.add("셋");
        arrList.add("하나");
 
        // 리스트 요소의 출력
        for(int i = 0; i < arrList.size(); i++) {
            System.out.println(arrList.get(i));
        }
    }
}

실행 결과

Collection 인터페이스

  • List와 Set인터페이스의 많은 공통된 부분을 Collection 인터페이스에서 정의하고, 두 인터페이스는 그것을 상속받습니다. 따라서 Collection 인터페이스는 컬렉션을 다루는데 가장 기본적인 동작들을 정의하고, 그것을 메소드로 제공하고 있습니다.
  • Collection 인터페이스에서 제공하는 주요 메소드는 다음과 같습니다.
메소드 설명
boolean add(E e) 해당 컬렉션(collection)에 전달된 요소를 추가함. (선택적 기능)
void clear() 해당 컬렉션의 모든 요소를 제거함. (선택적 기능)
boolean contains(Object o) 해당 컬렉션이 전달된 객체를 포함하고 있는지를 확인함.
boolean equals(Object o) 해당 컬렉션과 전달된 객체가 같은지를 확인함.
boolean isEmpty() 해당 컬렉션이 비어있는지를 확인함.
Iterator<E> iterator() 해당 컬렉션의 반복자(iterator)를 반환함.
boolean remove(Object o) 해당 컬렉션에서 전달된 객체를 제거함. (선택적 기능)
int size() 해당 컬렉션의 요소의 총 개수를 반환함.
Object[] toArray() 해당 컬렉션의 모든 요소를 Object 타입의 배열로 반환함.

[참고 자료]

http://www.tcpschool.com/java/java_collectionFramework_concept

반응형
반응형

람다식 표현

람다식 표현이란?

함수( 매서드 )를 간단한 식(expression)으로 표현하는 방법

int max(int a, int b){
    return a > b ? a : b;	 //  >>>	(a, b) -> a > b ? a : b
}

익명 함수(이름이 없는함수, anonymous function)

int max(int a, int b) -> { return a > b ? a : b; }

함수와 매서드의 차이

  • 근본적으로 동일. 함수는 일반적 용어, 매서드는 객체지향개념 용어
  • 함수는 클래스에 독립적, 매서드는 클래스에 종속적

람다식 작성하기

① 매서드의 이름과 반환 타입을 제거하고 '->'를 블록{} 앞에 추가한다.

int max(int a, int b){		
	return a > b ? a : b;	
}
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ //
(int a, int b) -> {		
	return a > b ? a : b;	
}

② 반환 값이 있는 경우, 식이나 값만 적고 return문 생략 가능 (끝에 ';' 안붙임)

(int a, int b) -> {		
	return a > b ? a : b;	
}
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ //
(int a, int b) -> a > b ? a : b

③ 매개변수의 타입이 추론 가능하면 생략 가능(대부분의 경우 생략 가능)

(int a, int b) -> a > b ? a : b
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ //
(a,  b) -> a > b ? a : b

※ 주의 사항

1. 매개변수가 하나인 경우, 괄호() 생략가능(타입이 없을 때만)

// (a) -> a * a
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ //
a -> a * a // 문제 없음

// (int a) -> a * a
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ //
int a -> a * a // 에러

2. 블록 안의 문장이 하나 뿐 일 때, 괄호{}생략 가능 (끝에 '';" 안 붙임)

(int i) -> {	
	System.out.println(i);
}
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ //
(int i) -> System.out.println(i)

3. 단, 하나 뿐인 문장이 return문이면 괄호{} 생략 불가

(int a, int b) -> { return a > b ? a : b; } // OK
(int a, int b) -> return a > b ? a : b      // 에러 발생

함수형 인터페이스

람다식은 익명 함수가 아니라 익명 객체이다.

( a, b ) -> a > b ? a : b
// 위의 람다식은 밑과 같이 적혀있는것과 비슷하다
new Object(){
	int max(int a, int b){
		return a > b ? a : b;
	}
}

람다식(익명 개체)을 다루기 위한 참조 변수가 필요. 참조 변수의 타입은?

Object obj = new Object(){		
	int max(int a, int b){	
		return a > b ? a : b;
	}
};
// 위와같이 정의한뒤 밑과 같이 사용하려고 해도 에러가 발생한다.
// 위에 정의한 max 메소드는 Object클래스에 정의되어 있지 않기 떄문에 에러가 발생함
int value = obj.max(3, 5);

타입 obj = ( a, b ) -> a > b ? a : b;

  • Object 타입은 람다식 표현을 입력할 수 없다. 그렇기 때문에 함수형 인터페이스를 이용해서 람다식을 사용한다.

함수형 인터페이스

단 하나의 추상 메서드만 선언된 인터페이스

@FunctionalInterface
interface MyFunction{
    public abstract int max(int a, int b);
}

// 익명클래스 클래스의 선언, 객체 생성 동시에 진행됨
MyFunction f = new MyFunction() {
        public int max(int a, int b){
            return a > b ? a : b;
        }
    };
                    
int value = f.max(3, 5); // OK, MyFunction에 max() 가 있음

함수형 인터페이스 타입의 참조 변수로 람다식을 참조할 수 있음

MyFunction f = (a, b) -> a > b ? a : b;
int value = f.max(3, 5); // 실제로는 람다식(익명 함수)이 호출됨
/*
//Ex1)
List<String> list = Arrays.asList("abc","aaa","bbb","ddd","aaa");

Collections.sort(list, new Comparator<String>(){
            public int compare(String s1, String s2) -> {            
                return s2.compareTo(s1);        
            }            
        });                

@FunctionalInterface
interface Comparator<T>{
    int compare(T o1, T o2);
}
*/                
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ //

List<String> list = Arrays.asList("abc","aaa","bbb","ddd","aaa");

Collections.sort(list, (s1,  s2) - > s2.compareTo(s1));

@FunctionalInterface
interface Comparator<T>{
    int compare(T o1, T o2);
}

함수형 인터페이스 타입의 매개변수, 반환 타입

// 함수형 매개변수    
// 함수형 인터페이스를 인수로 받는다는건 람다식 메소드을 받겠다는 이야기     
void aMethod( MyFunction f ){    
    f.myMethod(); // MyFunction에 정의된 매서드 호출
}    
    
@FunctionalInterface    
interface Comparator<T>{    
    int compare(T o1, T o2);
}    
    
MyFunction f = () -> System.out.println("myMethod()");
aMethod(f);    
// ↓ 메소드를 읽기전에 람다식을 직접 입력해주는 형태도 가능
aMethod(() -> System.out.println("myMethod()"));
// 함수형 반환타입
/*
MyFunction myMethod(){
	MyFunction f = () -> {};
	return f;
}
*/
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ //
MyFunction myMethod(){	
	return () -> {};
}

[참고 자료]

[자바의 정석 - 기초편] ch14-1~4 람다식이란? 람다식 작성하기

[자바의 정석 - 기초편] ch14-5,6 함수형인터페이스

반응형

+ Recent posts