[JAVA Algorithm] 컬렉션 프레임워크

2025. 10. 2. 21:04·JAVA

자료구조 중 List, Map, Set 인터페이스와 컬렉션 프레임워크의 종합적 활용 부분에 대해서 정리해보고자 한다.

 

컬렉션 프레임워크의 개념

 : Java에서 제공하는 데이터 구조를 표준화한 설계 구조. 다수의 데이터를 효율적이고 체계적으로 관리할 수 있는 방법을 제공

 

컬렉션 프레임워크 사용목적

  1. 프로그래밍 효율성
    • 이미 구현된 자료구조를 사용하여 개발 시간을 단축할 수 있음
    • 최적화된 알고리즘을 통해 성능을 보장받을 수 있음
  2. 재사용성
    • 표준화된 방식으로 데이터를 관리할 수 있음
    • 여러 프로젝트에서 일관된 방식으로 사용할 수 있음

 

주요 인터페이스와 클래스

  1. List 계열 : 데이터를 순차적으로 저장
    • ArrayList : 배열 기반으로 데이터를 저장하며, 조회 속도가 빠름
    • LinkedList : 노드 기반으로 데이터를 저장하며, 삽입과 삭제가 빠름
  2. Set 계열 : 중복을 허용하지 않는 데이터 집합
    • HashSet : 데이터를 해시 값으로 관리하여 검색 속도가 빠름
    • TreeSet : 데이터를 정렬된 상태로 유지하며, 이진 검색 트리 기반으로 동작
  3. Map 계열 : 키와 값을 쌍으로 저장하며, 키는 중복을 허용하지 않음
    • HashMap : 해시 기반으로 키-값 데이터를 저장하며, 빠른 검색 속도를 제공
    • TreeMap : 키를 기준으로 정렬된 키-값 쌍 저장

List 인터페이스

List 컬렉션의 특징과 사용 목적

 

더보기

 List 컬렉션은 순서를 유지하며 중복을 허용하는 요소들의 집합을 나타내는 인터페이스, 구현체에 따라 동작 방식이 달라짐

 

List 컬렉션의 특징

  • 순서 유지
    • 요소가 삽입된 순서가 유지되며, 인덱스를 통해 각 요소를 관리
    • 요소는 0부터 시작하는 인덱스를 가지며, 원하는 위치의 요소에 직접 접근 가능'
  • 유연한 크기
    • 배열과 달리 크기가 고정되지 않고, 동적으로 변함 
    • 요소를 추가하거나 제거할 때 자동으로 크기가 조정됨
  • 다양한 구현체
    • ArrayList : 배열 기반으로 빠른 접근 속도를 제공하지만, 중간 삽입/삭제는 느림
      • 접근이 빠름 O(1)
      • 중간 삽입/삭제 O(n)
      • 메모리 연속적
    • LinkedList : 이중 연결 리스트 기반으로 삽입/삭제가 빠르지만, 랜덤 접근은 느림
      • 접근이 느림 O(n)
      • 중간 삽입/삭제 O(n)
      • 메모리 분산

 

어떤 List를 사용해야 하는가?

ArrayList를 사용하면 좋은 경우

  1. 데이터 검색/조회가 빈번한 경우
    • 인덱스를 통해 데이터에 직접 접근할 수 있어 시간 복잡도가 O(1)
    • 특정 위치의 데이터를 빠르게 가져올 수 있음
  2. 데이터의 삽입/삭제가 주로 마지막 위치에서 일어나는 경우
    • 끝에서의 추가 및 삭제는 O(1)로 처리됨
    • 리스트 크기 조정이 필요한 경우에만 재할당이 발생
  3. 데이터가 자주 변경되지 않고 주로 읽기 작업이 많은 경우
    • 읽기 작업에서 ArrayList의 효율성이 극대화

LinkedList를 사용하면 좋은 경우

  1. 데이터의 삽입/삭제가 리스트의 시작이나 끝에서 빈번히 발생하는 경우
    • 리스트의 맨 앞이나 뒤에서의 삽입 및 삭제 작업이 O(1)로 효율적
    • ArrayList처럼 재할당이나 데이터 이동 비용이 발생하지 않음
  2. 데이터의 크기가 예측이 어렵고 변동이 심한 경우
    • 메모리 재할당이 필요 없으며, 노드 단위로 메모리를 관리

 

실제로 ArrayList가 선호되는 이유

  1. 현대 컴퓨터 환경에서 메모리 접근 속도가 매우 빠름
  2. ArrayList는 연속된 메모리 구조를 가지므로 CPU 캐시 활용이 효율적
  3. 대부분의 애플리케이션에서 데이터 조회 빈도가 매우 높으므로, 인덱스 접근이 빠른 ArrayList가 유리

 

배열과 ArrayList의 차이점

더보기

핵심 차이점 쉽게 이해하기

구분 배열(Array) ArrayList
크기 크기가 고정되어 있으며 생성 시 크기를 지정해야 함 크기가 동적으로 변경되며, 요소 추가 시 자동으로 확장 됨
타입 기본 데이터 타입(int[], double[] 등) 사용 가능 제네릭을 사용하여 객체만 저장 가능
(ArrayList<Integer>)
메모리 사용 연속적인 메모리 공간을 사용하여 효율적 내부적으로 배열을 사용하지만 크기 변경 시 새로운 배열을 생성해 데이터를 복사
성능 데이터 접근이 빠름(O(1)), 크기 변경 시 데이터 이동 불필요 데이터 접근이 빠르지만(O(1)), 크기 확장 시 복사가 필요해 성능 저하 가능
기능 단순한 데이터 저장/읽기만 가능하며 별도의 메서드가 없음 add(), remove(), contains() 등 다양한 메서드 제공
유연성 크기가 고정되어 유연성 부족 크기가 동적으로 변해 유연하게 사용 가능

 

언제 무엇을 사용할까?

  • 배열
    • 크기가 고정된 데이터 집합을 처리할 때 적합
    • 데이터 구조가 단순하고 성능(접근 속도)이 중요한 경우 유리
    • 메모리 사용이 효율적이어야 하거나 기본 타입 데이터를 다룰 때
  • ArrayList
    • 동적으로 데이터 크기가 변할 가능성이 있을 때
    • 요소 추가/삭제가 빈번하고, 코드를 간단하고 가독성 있게 작성하고 싶을 때
    • 데이터 조작에 다양한 메서드가 필요할 때

 

Collections 유틸리티 클래스 활용

Collections의 주요 기능

정렬(Sorting)

더보기
  • 컬렉션의 요소를 자연 정렬 순서 또는 사용자 정의기준으로 정렬할 수 있음
  • 주요 메서드
    • Collections.sort(List<T>) : 자연 정렬
    • Collections.sort(List<T>, Comparator<T>) : 사용자 정의 정렬
import java.util.*;

public class CollectionsSortingExamples {
    public static void main(String[] args) {
        // 1. 기본 리스트 정렬
        // === 기본 정렬 예제 ===
				// 숫자 오름차순 정렬: [1, 2, 5, 8, 9]
				// 숫자 내림차순 정렬: [9, 8, 5, 2, 1]
				// 문자열 정렬: [apple, banana, cherry]
        demonstrateBasicSorting();
        
        // 2. 중첩 리스트 정렬
        // === 중첩 리스트 정렬 예제 ===
				// 첫 요소 기준 정렬: [[1, 2, 8], [3, 5, 2], [4, 7, 6, 9]]
				// 합계 기준 정렬: [[3, 5, 2], [1, 2, 8], [4, 7, 6, 9]]
        demonstrateNestedListSorting();
        
        // 3. 객체 리스트 정렬
        // === 객체 정렬 예제 ===
				// 나이순 정렬: [Park(20), Kim(25), Lee(30)]
				// 이름순 정렬: [Kim(25), Lee(30), Park(20)]
        demonstrateObjectSorting();
        
        // 4. 다중 기준 정렬
        // === 다중 기준 정렬 예제 ===
				// 나이 + 이름 정렬: [Park(20), Kim(25), Lee(25)]
        demonstrateMultiCriteriaSorting();
    }

    private static void demonstrateBasicSorting() {
        System.out.println("\n=== 기본 정렬 예제 ===");
        
        // 숫자 리스트 정렬
        List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 9);
        Collections.sort(numbers);  // 자연 정렬 순서
        System.out.println("숫자 오름차순 정렬: " + numbers);
        
        Collections.sort(numbers, Collections.reverseOrder());  // 역순 정렬
        System.out.println("숫자 내림차순 정렬: " + numbers);
        
        // 문자열 리스트 정렬
        List<String> words = Arrays.asList("banana", "apple", "cherry");
        Collections.sort(words);
        System.out.println("문자열 정렬: " + words);
    }

    private static void demonstrateNestedListSorting() {
        System.out.println("\n=== 중첩 리스트 정렬 예제 ===");
        
        List<List<Integer>> nestedList = new ArrayList<>();
        nestedList.add(Arrays.asList(3, 5, 2));
        nestedList.add(Arrays.asList(1, 2, 8));
        nestedList.add(Arrays.asList(4, 7, 6, 9));

        // 첫 번째 요소 기준 정렬
        nestedList.sort(Comparator.comparing(list -> list.get(0)));
        System.out.println("첫 요소 기준 정렬: " + nestedList);

        // 리스트 합계 기준 정렬
        nestedList.sort(Comparator.comparing(list -> 
            list.stream().mapToInt(Integer::intValue).sum()));
        System.out.println("합계 기준 정렬: " + nestedList);
    }

    private static void demonstrateObjectSorting() {
        System.out.println("\n=== 객체 정렬 예제 ===");
        
        List<Person> people = Arrays.asList(
            new Person("Kim", 25),
            new Person("Lee", 30),
            new Person("Park", 20)
        );

        // 나이 기준 정렬
        people.sort(Comparator.comparing(Person::getAge));
        System.out.println("나이순 정렬: " + people);

        // 이름 기준 정렬
        people.sort(Comparator.comparing(Person::getName));
        System.out.println("이름순 정렬: " + people);
    }

    private static void demonstrateMultiCriteriaSorting() {
        System.out.println("\n=== 다중 기준 정렬 예제 ===");
        
        List<Person> people = Arrays.asList(
            new Person("Kim", 25),
            new Person("Lee", 25),
            new Person("Park", 20)
        );

        // 나이 기준 정렬 후, 이름으로 정렬
        people.sort(Comparator.comparing(Person::getAge)
                    .thenComparing(Person::getName));
        System.out.println("나이 + 이름 정렬: " + people);
    }

    // Person 클래스 정의
    static class Person {
        private String name;
        private int age;

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

        public String getName() { return name; }
        public int getAge() { return age; }

        @Override
        public String toString() {
            return name + "(" + age + ")";
        }
    }
}

최솟값, 최댓값 찾기 (Min/Max)

더보기
  • 컬렉션에서 최솟값과 최댓값을 쉽게 찾을 수 있음
  • 주요 메서드
    • Collections.min(Collection<T>)
    • Collections.max(Collection<T>)
List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 9);
System.out.println("최솟값: " + Collections.min(numbers)); // 출력: 1
System.out.println("최댓값: " + Collections.max(numbers)); // 출력: 9


빈도 계산 (Frequency)

더보기
  • 요소가 얼마나 자주 등장했는지 확인하는 문제에 유용
  • 메서드
    • Collections.frequency(Collection<T>, T)
List<String> words = Arrays.asList("apple", "banana", "apple", "cherry", "banana");
int count = Collections.frequency(words, "apple");
System.out.println("apple 등장 횟수: " + count);


이진 탐색 (Binary Search)

더보기
  • 정렬된 리스트에서 빠르게 값을 찾는 문제에 적합
  • 입력 리스트는 반드시 정렬 상태여야 함
  • 메서드
    • Collections.binarySearch(List<T>, T key)
List<Integer> numbers = Arrays.asList(1, 3, 5, 7, 9);
Collections.sort(numbers);  // 정렬 필수
int index = Collections.binarySearch(numbers, 5);
System.out.println("5의 위치: " + index);


요소 치환 (Replace)

더보기
  • 컬렉션에서 특정 값을 다른 값으로 변경
  • 주요 메서드
    • Collections.replaceAll(List<T> , T oldVal, T newVal)
List<String> words = Arrays.asList("apple", "banana", "apple");
Collections.replaceAll(words, "apple", "pear");
System.out.println(words); // 출력: [pear, banana, pear]


역순 변환 (Reversing)

더보기
  • 컬렉션의 순서를 뒤집음
  • 주요 메서드
    • Collections.reverse(List<T>)
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Collections.reverse(numbers);
System.out.println(numbers); // 출력: [5, 4, 3, 2, 1]


요소 회전 (Rotation)

더보기
  • 컬렉션의 요소를 지정된 거리만큼 이동
  • 주요 메서드
    • Collections.rotate(List<T>, int distance)
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Collections.rotate(numbers, 2); // 요소를 2칸 이동
System.out.println(numbers); // 출력: [4, 5, 1, 2, 3]

 

 

Map 인터페이스

Map 컬렉션의 특징과 사용 목적

더보기

Map은 키(Key)와 값(Value)을 하나의 쌍으로 묶어서 저장하는 자료구조. 특정한 키를 통해 대응하는 값을 빠르게 조회할 수 있는 구조를 제공

 

Map의 핵심 특징

구분 특징
구조 키와 값의 쌍으로 이루어진 데이터 구조
키(Key) 중복 불가, null 허용(HashMap의 경우)
값(Value) 중복 가능, null 허용
순서 입력 순서 보장되지 않음 (LinkedHashMap 제외)
구현체 HashMap, TreeMap, LinkedHashMap 등이 대표적
성능 HashMap: 평균적으로 O(1) 접근 속도, TreeMap : O(log n)

 

Map의 사용 목적과 활용 상황

  • 사용하는 이유
    • 빠른 검색/조회 : 키를 통해 O(1) 시간에 데이터 접근 가능
    • 연관 데이터 관리 : 관련된 데이터를 쌍으로 관리 가능
    • 중복 제거 : 키의 중복을 허용하지 않아 유니크한 데이터 관리 용이
  • 주요 활용 상황
    • 캐시/임시 저장소 : 특정 키로 빠르게 데이터를 가져와야 하는 경우
    • 빈도수 계산 : 데이터 출현 횟수 집계 (문자 빈도수, 단어 빈도수 등)
    • ID와 객체 매핑 : 회원 ID -> 회원 정보, 상품 코드 -> 상품 정보 등 매핑
    • 데이터 그룹화/분류 : 특정 기준으로 데이터를 묶어 관리

HashMap 주요 메서드 및 활용

더보기

HashMap은 Map 인터페이스를 구현한 대표적인 클래스이며, 가장 자주 사용됨

▼ 선언 및 주요 메서드

// 1. 선언
HashMap<String, Integer> map = new HashMap<>();

// 2. 데이터 추가 및 수정
map.put("A", 1);                     // 키가 "A"인 값에 1 저장
map.putIfAbsent("A", 10);            // "A"키가 없을 때만 10 저장
int value = map.getOrDefault("B", 0);// "B"키 조회, 없으면 기본값 0

// 3. 존재 여부 확인
if (map.containsKey("A")) { ... }    // "A"키 존재 여부 확인
if (map.containsValue(1)) { ... }    // 값 1 존재 여부 확인

// 4. 삭제
map.remove("A");                     // "A"키 삭제
map.clear();                         // 전체 삭제

// 5. 값 변경하기 (다양한 방법)
HashMap<String, Integer> scores = new HashMap<>();
scores.put("Kim", 80);

// 5-1. 일반적 방식
if (scores.containsKey("Kim")) {
    scores.put("Kim", scores.get("Kim") + 10);
}

// 5-2. compute 활용 (람다 사용)
scores.compute("Kim", (key, oldVal) -> oldVal + 10);

// 5-3. 키가 있을 때만 변경 (computeIfPresent)
scores.computeIfPresent("Kim", (key, oldVal) -> oldVal + 10);

// 6. 크기 확인
int size = map.size();
boolean isEmpty = map.isEmpty();

 

▼ 순회 방법

HashMap<String, Integer> map = new HashMap<>();
map.put("사과", 1000);
map.put("바나나", 2000);
map.put("오렌지", 3000);

// 1. entrySet() 활용 - (키, 값) 쌍 직접 접근
for (Map.Entry<String, Integer> entry : map.entrySet()) {
    String key = entry.getKey();
    int val = entry.getValue();
    // 처리 로직
}

// 2. keySet() 활용 - 키만 필요할 때
for (String key : map.keySet()) {
    // 키를 이용한 값 조회 또는 처리
}

// 3. values() 활용 - 값만 필요할 때
for (int val : map.values()) {
    // 값만을 이용한 처리
}

 

▼ HashMap 활용 패턴 실전 예시

▶︎ 패턴 1 : 등장 횟수 세기

  • 특정 요소가 몇 번 나왔는지 세는 경우
// 문제: 배열에서 각 숫자가 몇 번 나왔는지 세기
int[] nums = {1, 2, 3, 1, 2, 1};
HashMap<Integer, Integer> count = new HashMap<>();

// 방법 1: getOrDefault 사용
for (int num : nums) {
    count.put(num, count.getOrDefault(num, 0) + 1);
}
// 결과: {1=3, 2=2, 3=1}

 

▶︎ 패턴 2 : 그룹화 하기

  • 같은 특성을 가진 것들을 묶을 때 사용
// 문제: 같은 길이를 가진 단어들을 그룹화하기
String[] words = {"dog", "cat", "apple", "banana", "rat"};
HashMap<Integer, List<String>> groups = new HashMap<>();

// 방법: computeIfAbsent 사용
for (String word : words) {
    groups.computeIfAbsent(word.length(), k -> new ArrayList<>())
         .add(word);
}
// 결과: {3=[dog, cat, rat], 5=[apple], 6=[banana]}

 

▶︎ 패턴 3 : 첫 등장 위치 기록

  • 요소가 처음 나타난 위치를 기록할 때 사용
// 문제: 각 숫자가 처음 나온 위치 기록하기
int[] nums = {1, 2, 3, 1, 2, 1};
HashMap<Integer, Integer> firstPos = new HashMap<>();

// 방법: putIfAbsent 사용
for (int i = 0; i < nums.length; i++) {
    firstPos.putIfAbsent(nums[i], i);
}
// 결과: {1=0, 2=1, 3=2}  // 처음 나온 위치만 저장됨

 

Set 인터페이스

Set 컬렉션의 특징과 사용 목적

더보기

Set은 중보되지 않는 요소들을 저장하는 자료구조 집합이라는 이름처럼 같은 값이 2번 이상 등장하는 것을 허용하지 않으며 각 요소는 유일함

순서는 보장하지 않지만 LinkedHashSet을 사용하면 삽입 순서를 유지할 수 있음

 

Set의 핵심 특징

구분 특징
구조 중복을 허용하지 않는 요소들의 집합 구조
순서 일반적으로 입력 순서 보장 안 함 (단, LinkedHashSet은 유지)
null 처리 하나의 null 요소만 저장 가능
구현체 HashSet, TreeSet, LinkedHashSet 등이 대표적
성능 HashSet : 평균적으로 빠른 검색(O(1)), TreeSet: 정렬된 상태 유지(O(log n))

 

Set의 사용 목적과 활용 상황

  • 핵심 사용 목적
    • 중복 제거 : 대용량 데이터에서 같은 요소가 반복될 때, 효율적으로 중복을 제거할 수 있음
    • 유일성 보장 : 비즈니스 로직 상 고유해야 하는 값(예: 상품 코드, 회원 ID) 관리
    • 집합 연산 처리 : 합집합, 교집합, 차집합과 같은 집합 연산을 쉽게 수행
    • 메모리 절약 : 불필요한 중복데이터를 제거하여 메모리 사용량 감소
  • 실무 활용 예시
    • 데이터 처리 측면
      • 로그 분석 : 다양한 IP나 사용자 ID 중에서 유일한 값을 추출
    • 비즈니스 로직 측면
      • 회원 관리 : 중복 불가능한 식별자(이메일, 전화번호) 관리

HashSet 주요 메서드 및 활용

더보기

HashSet은 가장 널리 사용되는 Set 구현체 중 하나로, 해시 기반으로 요소를 관리. 일반적으로 순서를 보장하지 않으며, 빠른 검색과 삽입 성능을 자랑함

▼ 선언 및 주요 메서드

// 1. 선언
HashSet<String> set = new HashSet<>();

// 2. 데이터 추가 및 삭제
set.add("A");        // 요소 "A" 추가 (중복 요소는 추가되지 않음)
set.remove("A");     // "A" 삭제
set.clear();         // 모든 요소 삭제

// 3. 데이터 확인
boolean exists = set.contains("A");  // "A"가 존재하는지 확인
int size = set.size();               // Set의 크기 확인
boolean empty = set.isEmpty();       // 비었는지 확인

// 4. 집합 연산
Set<String> set1 = new HashSet<>();
Set<String> set2 = new HashSet<>();
// 합집합: set1에 set2의 요소 모두 추가
set1.addAll(set2);      
// 교집합: set1과 set2에 모두 있는 요소만 남김
set1.retainAll(set2);   
// 차집합: set1에서 set2의 요소 제거 (set1 - set2)
set1.removeAll(set2);

 

▼ 순회 방법

HashSet<String> fruits = new HashSet<>();
fruits.add("사과");
fruits.add("바나나");
fruits.add("오렌지");

// for-each문 활용
for (String fruit : fruits) {
    System.out.println(fruit);
}
// 출력 결과 (순서는 무작위):
// 바나나
// 오렌지
// 사과

 

▼ HashSet 활용 패턴 실전 예시

▶︎ 패턴 1 : 데이터 중복 제거

  • 대용량 데이터나 배열 내 중복 요소를 제거하는 데 유용
// 문제: 문자열 배열에서 중복된 이름 제거하기
String[] names = {"Tom", "John", "Tom", "Jane", "John", "Tom"};

// 방법 1: HashSet 생성자를 통한 중복 제거
Set<String> uniqueNames = new HashSet<>(Arrays.asList(names));
System.out.println("중복 제거된 이름: " + uniqueNames);
// 결과: [Jane, Tom, John] (순서는 보장 안 됨)

// 방법 2: 순차적으로 add하여 중복 체크
Set<String> nameSet = new HashSet<>();
for (String name : names) {
    if (nameSet.add(name)) {
        System.out.println("새로운 이름 추가: " + name);
    } else {
        System.out.println("중복된 이름 발견: " + name);
    }
}

 

▶︎ 패턴2 : 방문 체크(Visited Set) 활용

  • 어떤 데이터나 이벤트를 이미 처리했는지 빠르게 확인할 수 있음
// 문제: 작업 목록에서 중복 실행 방지하기
String[] tasks = {"Task1", "Task2", "Task1", "Task3", "Task2"};
Set<String> processedTasks = new HashSet<>();

for (String task : tasks) {
    if (!processedTasks.contains(task)) {
        System.out.println("작업 처리 중: " + task);
        // 실제 작업 처리 로직 수행
        processedTasks.add(task);
    } else {
        System.out.println("이미 처리된 작업: " + task);
    }
}
// 결과:
// 작업 처리 중: Task1
// 작업 처리 중: Task2
// 이미 처리된 작업: Task1
// 작업 처리 중: Task3
// 이미 처리된 작업: Task2

 

'JAVA' 카테고리의 다른 글

[JAVA] 불변객체(Immutable Object)  (0) 2025.10.13
[JAVA] 예외처리  (1) 2025.10.13
[Java Algorithm] 2차원 배열  (0) 2025.10.02
[JAVA Algorithm] 배열 개념 정리  (0) 2025.10.01
[JAVA] 완전탐색 Vs 그리디 알고리즘 이해하기  (0) 2025.09.30
'JAVA' 카테고리의 다른 글
  • [JAVA] 불변객체(Immutable Object)
  • [JAVA] 예외처리
  • [Java Algorithm] 2차원 배열
  • [JAVA Algorithm] 배열 개념 정리
stark77
stark77
하마의 IT 자기개발 이모저모, 백엔드 개발자로 거듭나기
  • stark77
    하마의 개발자 성장일기
    stark77
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 컴퓨터구조와 운영체제
        • 컴퓨터구조
        • 운영체제
      • SQL 기초
      • Spring
        • 백엔드 기초
        • Spring 실습
      • JAVA
        • Java 실습
      • HTML&CSS
        • HTML&CSS 실습
      • Git&GitHub
        • Git&GitHub 실습
      • 내배캠 끄적끄적
        • Today I Learned
      • 유용한 툴 및 사이트 정리
      • 취미
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    Spriingboot
    경합조건과 교착상태
    객체지향프로그래밍
    HTML&CSS
    Github
    실시간 데이터 처리
    for문
    백엔드 기초다지기
    git
    웹소켓
    SpringSecurity
    Til
    algorithm
    jsp
    WebSocket
    객체지향
    Spring
    네트워크 기초
    thymleaf
    JPA
    백엔드 기초
    java
    RestTemplate
    Java 문법기초
    다형성
    MVC
    BEAN
    String.format
    프로세스와 쓰레드
    Stomp
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
stark77
[JAVA Algorithm] 컬렉션 프레임워크
상단으로

티스토리툴바