컬렉션 자료구조

이동주·2025년 3월 14일

JAVA

목록 보기
25/30
post-thumbnail

컬렉션 프레임워크

  • 널리 알려진 자료구조를 바탕으로 객체들을 효율적으로 추가, 삭제, 검색할 수 있도록 관련 인터페이스와 클래스들을 포함시켜 놓은 java.util 패키지
  • 컬렉션 프레임워크를 잘 활용하면 프로그램 유지 보수에 더 좋음!
  • 메모리에서 데이터를 관리하므로 속도가 빠름
  • 주요 인터페이스 : List, Set, Map

List 컬렉션

  • 객체를 인덱스로 관리하기 때문에 객체를 저장하면 인덱스가 부여되고 인덱스로 객체를 검색하고 삭제할 수 있는 기능을 제공함
  • 순서를 유지하고 저장하며, 중복 저장이 가능함

ArrayList (반드시 알아야함!)

  • ArrayList에 객체를 추가하면 내부 배열에 객체가 저장되고 제한 없이 객체를 추가할 수 있음
  • 객체의 번지를 저장하는데, 동일한 객체를 중복 저장 시에는 동일한 번지가 저장됨.

  • ArrayList 컬렉션에 객체를 추가 시 인덱스 0번부터 차례대로 저장됨
  • 특정 인덱스의 객체를 제거하거나 삽입하면 전체가 앞/뒤로 1씩 당겨지거나 밀림
  • 중간에 삽입 또는 삭제가 불가능하므로 빈번한 객체 삭제와 삽입이 일어나는 곳에는 사용하지 않는 것이 좋음

package ch15.sec02.exam01;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data // lombok 데이터 사용
@AllArgsConstructor // lombok으로 인자를 모두 가지는 생성자 만듦
public class Board {
	private String subject;
	private String content;
	private String writer;
	
	public void print() {
		System.out.println(getSubject() + "\t" + getContent() +
				"\t" + getWriter());
	}
}
package ch15.sec02.exam01;

import java.util.ArrayList;
import java.util.List;

public class ArrayListExample {
	public static void main(String[] args) {
		// 파라미터를 Board 객체로 가지는 ArrayList 컬렉션 생성
		List<Board> list = new ArrayList<>();
		
		// Board 생성자에 인자 추가
		// add : 생성된 Board 객체를 List 컬렉션에 추가
		list.add(new Board("제목1", "내용1", "글쓴이1"));
		list.add(new Board("제목2", "내용2", "글쓴이2"));
		list.add(new Board("제목3", "내용3", "글쓴이3"));
		list.add(new Board("제목4", "내용4", "글쓴이4"));
		list.add(new Board("제목5", "내용5", "글쓴이5"));
		
		// size : 저장된 객체 수를 얻기
		int size = list.size();
		System.out.println("총 객체 수: " + size);
		System.out.println();
		
		// List에 3번째로 추가된 객체를 출력함
		Board board = list.get(2);
		board.print();
		
		// List에 저장된 길이만큼 for문 실행
		for(int i=0;i<list.size();i++) {
			// list에 배열에 저장되어 있는 값들을 Board 객체에 저장
			Board b = list.get(i);
			b.print();
		}
		System.out.println();
		
		// List 객체에 저장되어있는 값 중에 인덱스 2(3번째) 값 삭제
		list.remove(2);
		
		// List 객체에 저장되어있는 값 중에 인덱스 2(3번째) 값 삭제
		// 위의 구문으로 인덱스 2의 값이 삭제되었으므로, 
		// 인덱스 3번의 값이 인덱스 2번으로 옮겨짐. 그리고 인덱스 3번의 값도 삭제됨
		list.remove(2);
		
		// 향상된 for loop
		for(Board b : list) {
			b.print();
		}
	}
}

Vector

  • 동기화된 메소드로 구성되어 있어 멀티 스레드가 동시에 Vector() 메소드를 실행할 수 없음
  • 멀티 스레드 환경에서는 안전하게 객체를 추가 또는 삭제가 가능

package ch15.sec02.exam02;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data // lombok 데이터 사용
@AllArgsConstructor // lombok으로 인자를 모두 가지는 생성자 만듦
public class Board {
	private String subject;
	private String content;
	private String writer;
	
	public void print() {
		System.out.println(getSubject() + "\t" + getContent() +
				"\t" + getWriter());
	}
}
package ch15.sec02.exam02;

import java.util.List;
import java.util.Vector;

import ch15.sec02.exam01.Board;

public class VectorExam {
	public static void main(String[] args) {
		// Vector 컬렉션 생성하여 List 클래스의 파라미터를 Board 객체로 사용
		// ArrayList보다 정확도가 더 좋음
		List<Board> list = new Vector<>();
		
		// Vector 대신 ArrayList도 사용할 수 있지만
		// 스레드의 갯수가 여러개일 경우 정확도가 떨어짐
		
		// 작업 쓰레드 객체 생성
		Thread threadA = new Thread() {
			public void run() {
				// 1000개의 객체 생성(1번부터 1000번까지)
				for(int i=1;i<=1000;i++) {
					list.add(new Board("제목"+i, "내용"+i, "글쓴이"+i));
				}
			}
		};
		
		// 작업 쓰레드 객체 생성
		Thread threadB = new Thread() {
			public void run() {
				// 1000개의 객체 생성(1001번부터 2000번까지)
				for(int i=1001;i<=2000;i++) {
					list.add(new Board("제목"+i, "내용"+i, "글쓴이"+i));
				}
			}
		};
		
		// 실행 시작 시간을 잡는 위치
		long start = System.nanoTime();
		
		// 스레드의 start 메소드 : 스레드를 실행할 수 있도록 하는 메소드
		// 스레드를 선언하면 반드시 실행하는 메소드를 선언해주기
		threadA.start();
		threadB.start();
		
		// 예외가 발생할 수 있는 구문
		try {
			// join : 해당 스레드의 실행이 끝날 때 까지 기다려줌
			threadA.join();
			threadB.join();
		}
		// 예외가 발생했을 때 실행되는 구문
		catch(Exception e) {
			System.out.println("예외가 발생하였습니다");
		}
		
		// 실행 종료 시간을 잡는 위치
		long end = System.nanoTime();
		System.out.println("실행시간: " + (end-start));
		
		// 저장된 객체 수 얻기
		int size = list.size();
		System.out.println("총 객체 수: " + size);
		System.out.println();
	}
}

LinkedList

  • 인접 객체를 체인처럼 연결해서 관리
  • 객체 삭제와 삽입이 빈번한 곳에서 ArrayList보다 유리함

package ch15.sec02.exam03;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class LinkedListExam {
	public static void main(String[] args) {
		// ArrayList 객체 생성
		List<String> list1 = new ArrayList<String>();
		
		// LinkedList 객체 생성
		List<String> list2 = new LinkedList<String>();
		
		// 실행 시작시간과 종료시간 필드를 만듦
		long startTime;
		long endTime;
		
		// 시작시간
		startTime = System.nanoTime();
		for(int i=0; i<10000; i++) {
			// for루프 실행 내내 값을 0번째에 넣음으로써
			// 값이 내림차순으로 나옴
			list1.add(0, String.valueOf(i));
			// ArrayList는 배열을 사용함
			// 값을 0번째에 넣을 때 나머지 값들을 한칸씩 뒤로 미루는 과정에서
			// 성능이 떨어짐
		}
		endTime = System.nanoTime();
		
		// ArrayList의 저장 시간
		System.out.printf("%-17s %8d ns \n", "ArrayList 걸린 시간: ", (endTime-startTime));
		
		startTime = System.nanoTime();
		for(int i=0; i<10000; i++) {
			list2.add(0, String.valueOf(i));
			// LinkedList는 연결 리스트 구조
			// 앞에 값을 추가하는 작업이 상대적으로 빠름
		}
		endTime = System.nanoTime();
		
		// LinkedList의 저장 시간
		System.out.printf("%-17s %8d ns \n", "LinkedList 걸린 시간: ", (endTime-startTime));
	}
	
}

Set 컬렉션

  • 수학의 집합 개념으로 객체를 중복해서 저장 불가
  • 저장 순서가 유지되지 않으며 하나의 null만 저장할 수 있다
  • Map에서 사용하는 키를 Set에서 관리함

HashSet (꼭 알아야함)

  • 동등 객체를 중복 저장하지 않음
  • 다른 객체라도 hashCode() 메소드의 리턴값이 같고, equals() 메소드가 true를 리턴하면 동일한 객체라고 판단하고 중복 저장하지 않음

package ch15.sec03.exam01;

import java.util.HashSet;
import java.util.Set;

public class HashSetExam {
	public static void main(String[] args) {
		
		// HashSet 컬렉션 생성
		// Set : 순서가 없고 중복을 허용하지 않는 데이터를 저장하는 객체
		// HashSet : 내부적으로 해시테이블을 사용하여 데이터를 저장
		// 빠른 검색, 추가, 삭제가 가능함
		Set<String> set = new HashSet<String>();
		
		// 객체 저장
		set.add("Java");
		set.add("JDBC");
		set.add("JSP");
		set.add("Spring");
		
		// Set의 경우 중복 객체 저장을 허용하지 않음
		set.add("Java");
		
		int size = set.size();
		System.out.println("총 객체 수: " + size);
		// 마지막 Java가 중복 저장이 되지 않기 때문에 결과는 4
	}
}
package ch15.sec03.exam02;

import java.util.HashSet;
import java.util.Set;

public class HashSetExam {
	public static void main(String[] args) {
		// HashSet 컬렉션 생성 후 Set 객체에 저장!
		Set<Member> set = new HashSet<Member>();
		
		set.add(new Member("홍길동", 30));
		set.add(new Member("홍길동", 30));
		
		// 중복 저장이 되지 않으므로 결과는 1
		System.out.println("총 객체 수: " + set.size());
	}
}
package ch15.sec03.exam02;

public class Member {
	public String name;
	public int age;
	
	public Member(String name, int age) {
		this.name = name;
		this.age = age;
	}

	// hashCode를 반환하는 메소드
	// name과 age 값이 같으면 동일한 해시코드가 리턴됨
	@Override
	public int hashCode() {
		return name.hashCode() + age;
	}
	
	// name과 age가 같으면 true 리턴함
	@Override
	public boolean equals(Object obj) {
		// obj(Object) 타입의 변수를 Member 타입의 변수로 변환 가능하면 변수 target에 저장
		if(obj instanceof Member target) {
			// 
			return target.name.equals(name) && (target.age==age);
		}
		
		else {
			return false;
		}
	}
	
}

iterator() 메소드 (반복 메소드)

  • 반복자를 얻어 Set 컬렉션의 객체를 하나씩 가져옴
  • 현업에서 많이 쓰이는 구문

package ch15.sec03.exam03;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class HashSetExam {
	public static void main(String[] args) {
		// HashSet 컬렉션을 생성하여 Set 클래스에 저장
		Set<String> set = new HashSet<String>();
		
		// Set 객체에 문자열 삽입
		set.add("Java");
		set.add("JDBC");
		set.add("JSP");
		set.add("Spring");
		
		// Iterator : 객체를 하나씩 가져와서 처리하는 기능을 함
		// .iterator : 컬렉션에서 Iterator 객체를 생성하고 반환하는 메소드
		Iterator<String> iterator = set.iterator();
		
		// iterator.hasNext() : 리스트 등의 요소를 하나씩 순회할 때 사용
		while(iterator.hasNext()) {
			// 다음 요소를 가져올 때 사용
			String element = iterator.next();
			System.out.println(element);
			if(element.equals("JSP")) {
				//해당 요소를 삭제할 때 사용
				iterator.remove();
			}
		}
		System.out.println();
		
		// Set에 저장된 JDBC라는 문자열 삭제
		set.remove("JDBC");
		
		// set에 저장된 구문들을 모두 출력
		for(String element : set) {
			System.out.println(element);
		}
	}
}

Map 컬렉션 (많이 중요)

  • 키와 값으로 구성된 엔트리 객체를 저장함
  • 키는 중복 저장할 수 없지만 값은 중복 저장이 가능
  • 기존에 저장된 키와 동일한 키로 값을 저장하면 새로운 값으로 대치
  • 프로그래밍에서 제일 많이 사용됨

HashMap

  • 키로 사용할 객체가 hashCode() 메소드의 리턴값이 같고 equals() 메소드가 true를 리턴할 경우 동일 키로 보고 중복 저장을 허용하지 않음

package ch15.sec04.exam01;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class HashMapExam {
	public static void main(String[] args) {
		// Map<>() : 키와 값을 쌍으로 저장하는 자료구조
		// 키를 String 형태, 값을 Integer 형태로 저장함
		// HashMap<>() : 빠른 데이터 조회 및 추가/삭제가 가능함
		Map<String, Integer> map = new HashMap<>();
		
		map.put("신용권", 85);
		map.put("홍길동", 90);
		map.put("동장군", 80);
		// 키가 같을 경우 최근에 지정한 값으로 저장됨
		map.put("홍길동", 95);
		// Map에 저장된 값들의 갯수를 출력함
		// 홍길동 키의 값이 업데이트 됐으므로 개수는 3개가 됨
		System.out.println("총 Entry 수: " + map.size());
		System.out.println();
		
		String key = "홍길동";
		// 홍길동 키에 저장된 값을 출력
		int value = map.get(key);
		System.out.println(key + ": " + value);
		System.out.println();
		
		// ketSet : Map에 저장된 모든 키들을 Set으로 반환하는 메소드
		// Set은 중복 값 저장 x
		Set<String> keySet = map.keySet();
		// Iterator : 컬렉션의 요소들을 하나씩 순회할 수 있는 인터페이스
		// Map에 저장된 키와 값을 얻을 수 있는 역할을 함
		Iterator<String> keyIterator = keySet.iterator();
		
		// hasNext() 다음 값이 있는지 확인
		while (keyIterator.hasNext()) {
			// next() : Set의 다음 키 값이 나옴
			String k = keyIterator.next();
			// .get(k) : 해당 키의 값을 읽어옴
			Integer v = map.get(k);
			System.out.println(k + ": " + v);
		}
		System.out.println();
		
		// map.entrySet() : Map의 모든 키-값의 쌍을 Set으로 반환함
		// Entry<> : Map의 단일요소로, 키-값의 쌍을 나타내는 인터페이스
		Set<Entry<String, Integer>> entrySet = map.entrySet();
		
		// entrySet의 값들을 순차적으로 접근하기 위한 선언
		Iterator<Entry<String, Integer>> entryIterator = entrySet.iterator();
		
		// entryIterator의 다음 값이 있는지 확인
		while(entryIterator.hasNext()) {
			// 현재 값을 반환 후 커서를 다음 요소로 이동시킴
			Entry<String, Integer> entry = entryIterator.next();
			// 현재 키 값을 반환
			String k = entry.getKey();
			// 현재 값을 반환
			Integer v = entry.getValue();
			System.out.println(k + ": " + v);
		}
		System.out.println();
		
		// map에서 홍길동 키를 삭제
		map.remove("홍길동");
		// map 안에 저장된 값들의 개수
		System.out.println("총 Entry 수: " + map.size());
		System.out.println();
	}
}

Hashtable

  • 동기화된 메소드로 구성되어 있어 멀티 스레드가 동시에 Hashtable의 메소드들을 실행 불가
  • 멀티 스레드 환경에서도 안전하게 객체를 추가하고 삭제할 수 있음

package ch15.sec04.exam02;

import java.util.Hashtable;
import java.util.Map;

public class HashtableExam {
	public static void main(String[] args) {
		// Hashtable : 동기화된 자료구조
		// 스레드가 여러 개 있어도 안전하게 동작할 수 있도록 함
		Map<String, Integer> map = new Hashtable<>();
		
		// Thread : 하나의 프로그램이 여러 작업을 동시에 처리할 수 있는 기능
		Thread threadA = new Thread() {
			public void run() {
				for(int i=1;i<=1000;i++) {
					// Map에 문자열과 정수값을 넣음
					map.put(String.valueOf(i), i);
				}
			}
		}; // Thread 블록에는 꼭 세미콜론 붙이기
		
		
		// Thread : 하나의 프로그램이 여러 작업을 동시에 처리할 수 있는 기능
		Thread threadB = new Thread() {
			public void run() {
				for(int i=1001;i<=2000;i++) {
					// Map에 문자열과 정수값을 넣음
					map.put(String.valueOf(i), i);
				}
			}
		}; // Thread 블록에는 꼭 세미콜론 붙이기
		
		// 스레드 실행
		threadA.run();
		threadB.run();
		
		// 예외가 발생할 수 있는 구문 처리
		try {
			// join : 스레드의 실행이 끝날 때 까지 기다리는 역할
			threadA.join();
			threadB.join();
		}
		// 예외 처리
		catch(Exception e) {
			System.out.println("예외 발생");
		}
		
		// map 내부의 값의 갯수를 출력
		int size = map.size();
		System.out.println("총 엔트리 수: " + size);
		System.out.println();
	}
}

Properties

  • Hashtable의 자식 클래스로, 키와 값을 String 타입으로 제한한 컬렉션
  • 주로 확장자가 .properties인 프로퍼티 파일을 읽을 때 사용
  • 프로퍼티 파일은 키와 값이 = 기호로 연결된 텍스트 파일 (ISO 8859-1 문자셋, 한글은 \u+유니코드)
  • Properties 객체를 생성하고, load() 메소드로 프로퍼티 파일의 내용을 메모리로 로드
driver=oracle.jdbc.OracleDirver
url=jdbc:oracle:thin:@localhost:1521:orcl
username=user
password=pwd1234
admin=\uD64D\uAE38\uB3D9
package ch15.sec04.exam03;

import java.util.Properties;

public class PropertiesExample {
	public static void main(String[] args) throws Exception{
		// Properties : Hashtable의 자식 클래스로, 키와 값을 String 값으로 제한한 컬렉션
		Properties properties = new Properties();
		
		// 해당 클래스에 있는 database.properties 파일을 불러옴
		properties.load(PropertiesExample.class.getResourceAsStream("database.properties"));
		
		// database.properties에 저장된 문자열들 얻어옴
		String driver = properties.getProperty("driver");
		String url = properties.getProperty("url");
		String username = properties.getProperty("username");
		String password = properties.getProperty("password");
		String admin = properties.getProperty("admin");
		
		System.out.println("driver : " + driver);
		System.out.println("url : " + url);
		System.out.println("username : " + username);
		System.out.println("password : " + password);
		System.out.println("admin : " + admin);
	}
}

TreeSet

  • 이진 트리(현재 값을 기준으로 좌항과 우항을 더해 2로 나눔)를 기반으로 한 Set 컬렉션
  • 여러 개의 노드가 트리 형태로 연결된 구조
  • 루트 노드에서 시작해 각 노드에 최대 2개의 노드를 연결할 수 있음
  • TreeSet에 객체를 저장하면 부모 노드의 객체와 비교해서 낮은 것은 왼쪽 자식 노드에, 높은 것은 오른쪽 자식 노드에 저장
  • 검색이 빠르다는 특징을 가짐

TreeSet 컬렉션을 생성하는 방법

  • Set 타입 변수에 대입해도 되지만 TreeSet 타입으로 대입한 이유
    -> 검색 관련 메소드가 TreeSet에만 정의되어 있기 때문임

TreeMap

  • 이진 트리를 기반으로 한 Map 컬렉션
  • 키와 값이 저장된 엔트리를 저장함
  • 부모 키 값과 비교해서 낮은 것은 왼쪽, 높은 것은 오른쪽 자식 노드에 Entry 객체를 저장
  • 데이터베이스가 없을 경우 이를 사용하면 성능이 좋아짐

Compareable과 Comparator (매우 중요)

  • TreeSet에 저장되는 객체와 TreeMap에 저장되는 키 객체를 정렬

Compareable 인터페이스

  • compareTo() 메소드가 정의됨
  • 사용자 정의 클래스에서 이 메소드를 재정의해서 비교 결과를 정수 값으로 리턴
  • 소스를 수정할 수 있는 권한이 있어야 함
  • 인터페이스의 형태이기 때문에 사용 시 재정의 메소드를 꼭 구현 해줘야함

Comparator 클래스

  • 비교 기능이 없는 Comparable 비구현 객체를 저장하려면 비교자 Comparator를 제공함
  • 비교자는 compare() 메소드를 재정의해서 비교 결과를 정수 값으로 리턴함
  • 소스를 수정할 수 있는 권한이 없을 때 사용

LIFO와 FIFO 컬렉션

  • 후입선출(LIFO) : 나중에 넣은 객체가 먼저 빠져나가는 구조
  • 선입선출(FIFO) : 먼저 넣은 객체가 먼저 빠져나가는 구조
    -> 스케쥴링, 메세지(채팅) 시스템에 많이 사용됨
  • 컬렉션 프레임워크는 LIFO 자료구조를 제공하는 스택 클래스와 FIFO 자료구조를 제공하는 큐인터페이스를 제공함

Stack

  • Stack 클래스 : LIFO 자료구조를 구현한 클래스

Queue

  • Queue 인터페이스 : FIFO 자료구조에서 사용되는 메소드를 정의
  • LinkedList : Queue 인터페이스를 구현한 대표적인 클래스
  • offer(주어진 객체를 큐에 넣음), poll(큐에서 객체를 빼냄), empty 함수 주로 사용

동기화된 컬렉션

  • 동기화된 메소드로 구성된 Vector와 Hashtable은 멀티스레드 환경에서 안전하게 요소를 처리
  • Collections의 synchronizedXXX() 메소드
    -> ArrayList, HashSet, HashMap 등 비동기화된 메소드를 동기화된 메소드로 래핑

수정할 수 없는 컬렉션

  • 요소를 추가, 삭제할 수 없는 컬렉션
  • 컬렉션 생성 시 저장된 요소를 변경하고 싶지 않을 때 유용함
  • List, Set, Map 인터페이스의 정적메소드인 of()로 생성
  • List, Set, Map 인터페이스의 정적메소드인 copyOf()를 이용해 기존 컬렉션을 복사
  • 배열로부터 수정할 수 없는 List 컬렉션을 만듦 : asList() 함수
profile
끄작끄작

0개의 댓글