gugbab2's GitBook
  • Language
    • C++
      • 강의
        • C++ 언매니지드 프로그래밍
          • C++ 프로그래밍
          • 출력(Output)
          • 입력(Input)
          • bool 타입, Reference
          • 상수(const)
          • 문자열(string)
          • 파일 입출력
          • 개체지향 프로그래밍1
          • 개체지향 프로그래밍2
          • 개체지향 프로그래밍3
          • 캐스팅(형변환, casting)
          • 인라인 함수
          • static 키워드
          • 예외(Exception)
          • STL(Standard Template Library) 컨테이너(Container) - Vector
          • STL 컨테이너 - Map
          • STL 컨테이너 - Queue, Stack, Set, List
          • 템플릿(Template) 프로그래밍
          • 새로운 키워드(C++11 ~) 1
          • 새로운 키워드(C++11 ~) 2
          • 새로운 자료형
          • 새로운 STL 컨테이너
          • 스마트(smart) 포인터
          • 이동생성자 및 이동대입연산자
          • constexpr
          • Lamda Expression
      • 책
        • The C++ Programming Lanuaage
          • 2부 : 기본 기능
            • 6. 타입과 선언
            • 7. 포인터, 배열, 참조
            • 8. 구조체(struct), 공용체(union), 열거형(enum)
            • 10. 표현식
            • 11. 선택 연산
            • 12. 함수
            • 13. 예외 처리
            • 15. 소스 파일과 프로그램
          • 3부 : 추상화 메커니즘
            • 16. 클래스
            • 17. 생성, 소멸, 복사와 이동
            • 18. 연산자 오버로딩
            • 19. 특수 연산자
            • 20. 파생클래스
        • 씹어먹는 C++
          • 2. C++ 참조자(reference) 의 도입
          • 5.1 연산자 오버로딩(비교, 대입 연산자)
          • 5-2. 연산자 오버로딩(이항, 입출력, 타입변환, 증감 연산자)
          • 6-2. 가상(virtual) 함수와 다형성
          • 6-3. 가상 함수에 대한 지식들
          • 9-1. 코드를 찍어내는 틀 - C++ 템플릿(template)
          • 9-2. 가변 길이 템플릿(Variadic template)
          • 9-3. 템플릿 메타 프로그래밍 (Template Meta Programming)
          • 9-4. 템플릿 메타 프로그래밍2
          • 16.1 유니폼 초기화(Uniform Initialization)
          • 토막글 2. 람다(lambda)
    • Java
      • 강의
        • 김영한의 실전 자바 - 기본편
          • 절차 지향 vs 객체 지향
            • 절차 지향 프로그래밍
            • 객체 지향 프로그래밍
          • 변수
            • 클래스 변수 / 인스턴스 변수, 멤버 변수 / 지역 변수
            • 기본형 vs 참조형
          • 패키지
            • 패키지
            • CLI 환경에서 .java 파일 컴파일 && 실행
          • 접근 제어자
            • 접근 제어자 - 기본
            • 캡슐화
          • static
            • 자바 메모리 구조
            • static 기본
            • 스택 영역, 힙 영역
              • 스택 영역, 힙 영역 - 기본
              • 메소드가 실행될 때 어떤일이 일어나는가?
          • 상속
            • 상속 기본
          • 다형성(Pilymorphism)
            • 다형성 기본
            • 다형성의 활용
              • 다형성의 활용 - 기본
              • 다형성의 활용 - 추상클래스
              • 다형성의 활용 - 인터페이스
            • 다형성과 설계
              • 좋은 객체 지향 프로그래밍
        • 김영한의 실전 자바 - 중급1편
          • 1. Object 클래스
          • 2. 불변 객체
          • 3. String 클래스
          • 4. 래퍼, Class 클래스
          • 5. 열거형 - ENUM
          • 6. 날짜와 시간
          • 7. 중첩 클래스, 내부 클래스1
          • 8. 중첩 클래스, 내부 클래스2
          • 9. 예외 처리1 - 이론
          • 10. 예외 처리 - 실습
        • 김영한의 실전 자바 - 중급2편
          • 1. 제네릭 - Generic1
          • 2. 제네릭 - Generic2
          • 3. 컬렉션 프레임워크 - ArrayList
          • 4. 컬렉션 프레임워크 - LinkedList
          • 5. 컬렉션 프레임워크 - List
          • 6. 컬렉션 프레임워크 - 해시(Hash)
          • 7. 컬렉션 프레임워크 - HashSet
          • 8. 컬렉션 프레임워크 - Set
            • 레드 블랙 트리
          • 9. 컬렉션 프레임워크 - Map, Stack, Queue
            • 왜(?) Set 은 내부에서 Map 을 사용할까?
          • 10. 컬렉션 프레임워크 - 순회, 정렬, 전체 정리
        • 김영한의 실전 자바 - 고급 1편, 멀티스레드와 동시성
          • 프로세스와 스레드 소개
          • 스레드 생성과 실행
          • 스레드 제어와 생명 주기1
          • 스레드 제어와 생명 주기2
          • 메모리 가시성
          • 동기화 - synchronized
            • synchronized 키워드 이해도 체크
          • 고급 동기화 - concurrent.Lock
          • 생산자 소비자 문제1
          • 생산자 소비자 문제2
          • CAS - 동기화와 원자적 연산
          • 동시성 컬렉션
          • 스레드 풀과 Executor 프레임워크1
          • 스레드 풀과 Executor 프레임워크2
        • 김영한의 실전 자바 - 고급 2편, I/O, 네트워크, 리플렉션
          • 문자 인코딩
          • I/O 기본1
          • I/O 기본2
          • I/O 활용
          • File, Files
          • 네트워크 - 프로그램1
          • 네트워크 - 프로그램2
          • 채팅 프로그램
          • HTTP 서버 만들기
          • 리플렉션
          • 애노테이션
          • HTTP 서버 활용
        • 김영한의 실전 자바 - 고급3편, 람다, 스트림, 함형 프로그래밍
          • 람다가 필요한 이유
          • 람다
          • 함수형 인터페이스
          • 람다 활용
          • 람다 vs 익명 클래스
          • 메서드 참조
          • 스트림API1 - 기본
          • 스트림 API2 - 기능
          • 스트림 API3 - 컬렉터
          • Optional
          • 디폴트 메서드
          • 병렬 스트림
          • 함수형 프로그래밍
        • 기초 탄탄! 독하게 시작하는 Java - Part2: OOP 와 JVM
          • 2. 클래스 - 첫 번째
          • 3. 클래스 - 두번째
          • 4. 상속과 관계
          • 6. JVM(Java Virtual machine) 기본 이론
          • 7. JVM 과 GC 그리고 객체
          • 8. 불변 객체와 String 클래스
      • 책
        • 자바의 신
          • 변수
            • 클래스 변수(static) 사용 주의 케이스
            • Java volatile 과 Atomic 변수(+CAS)
          • 연산자
            • 비트 연산자 활용 예제
          • 배열
          • 참조 자료형
          • 상속
          • Object 클래스
          • interface, abstract class, enum
          • 예외
          • String 클래스
            • String 구조
            • String 문자열을 byte 로 변환하기
            • String 클래스에서 자주 사용되는 메서드
            • String 클래스로 살펴보는 불변(Immutable)객체
            • StringBuilder, StringBuffer
          • Nested 클래스
          • 어노테이션
            • 어노테이션 기본
            • 어노테이션의 사용
          • JVM 이해하기
            • 왜 JVM 을 사용해?
            • JVM, JRE, JDK
            • JVM 구조 이해하기
            • 클래스 로더 시스템
            • JIT(Just-In-Time) 컴파일러
            • GC(Garbage Collector)
              • GC Part.1
              • GC Part.2
              • GC 튜닝
          • java.lang
            • Wrapper 클래스
            • System 클래스
          • Generic
            • 제네릭 기본
            • 와일드카드
            • 와일드카드 GET / SET 경계
            • 와일드카드 extends / super 사용시기
            • 혼동할 수 있는 와일드카드 표현
          • Collection
            • 자료구조
              • 이진 탐색 트리 vs 레드 블랙 트리
            • Collection
            • List
              • ArrayList
              • Vector
              • Stack
              • LinkedList
            • Set, Queue
              • HashSet
              • LinkedHashSet
              • TreeSet
              • Priority Queue
              • ArrayDeque
            • Map
              • HashMap
              • Hashtable
              • LinkedHashMap
              • TreeMap
          • Thread
            • Thread 기본
            • Thread 와 관련이 많은, Synchronized
            • Thread 를 통제하는 메서드
            • ThreadGroup
          • I/O
            • InputStream, OutputStream
            • Reader, Writer
          • Serializable, NIO
            • Serializable
            • NIO (New IO)
          • 네트워크 프로그래밍
            • 네트워크 기본 & TCP 통신
            • UDP 통신
          • 람다
            • 함수형 인터페이스
            • 람다란?
        • 벨둥(Bealdung)
          • Java Concurrency
            • Java Concurrency Basics
              • Overview of the java.util.concurrent
              • Guide to the Synchronized Keyword in Java
              • Guide to the Volatile Keyword in Java
              • Guide to the java.util.concurrent.Future
              • ThreadLocal in Java
      • 그 외
        • 시스템 콜과 자바에서의 시스템 콜 사용례
        • 자바 NIO 의 동작원리 및 IO 모델
        • 함수형 인터페이스(FunctionInterface) - 자바8
  • Spring
    • 강의
      • 스프링 핵심 원리 - 기본편
        • 큰 흐름 잡기
        • 스프링 핵심 원리 이해1 - 예제 만들기
        • 스프링 핵심 원리 이해2 - 객체 지향 원리 적용
        • 스프링 컨테이너와 스프링 빈
        • 싱글톤 컨테이너
        • 컴포넌트 스캔
        • 의존관계 자동 주입
        • 빈 생명주기 콜백
        • 빈 스코프
      • 토비의 스프링6 - 이해와 원리
        • 3. 오브젝트와 의존관계1
        • 3. 오브젝트와 의존관계2
        • 4. 테스트
        • 5. 템플릿
        • 6.예외
        • 7. 서비스 추상화
    • 책
      • JSP 2.3 웹 프로그래밍
        • Servlet
        • JSP
        • 쿠키 / 세션
        • MVC 패턴
        • 실무 때 고민할 만한 부분
      • 스프링 입문을 위한 자바 객체지향의 원리와 이해
        • 자바와 절차적/구조적 프로그래밍
        • 객체지향의 4대 특성
        • 객체지향 설계의 5원칙
        • 스프링이 사랑한 디자인 패턴
        • IoC / DI
        • AOP(Aspect Oriented Programming), 관점 지향 프로그래밍
      • 토비의 스프링 3.1
        • Spring vs Spring Boot
        • 1. 오브젝트와 의존관계
          • 1.4 제어의 역전(IoC)
          • 1.5 스프링의 IoC
          • 1.6 싱글톤 레지스트리와 오브젝트 스코프
    • 그 외
      • 스프링 부트(SpringBoot) 탄생 배경
  • CS
    • DATA STRUCTURES
      • 선택 정렬(Selection Sort)
      • 버블 정렬(Bubble Sort)
      • 삽입 정렬(Insertion Sort)
    • OS
      • 강의
      • 책
        • 혼자 공부하는 컴퓨터구조 + 운영체제
          • 1. 컴퓨터 구조 시작하기
          • 2. 데이터
          • 3. 명령어
          • 4. CPU 의 작동원리
          • 5. CPU 성능 향상 기법
          • 6. 메모리와 캐시메모리
          • 7. 보조기억장치
          • 8. 입출력장치
          • 9. 운영체제 시작하기
          • 10. 프로세스와 스레드
    • NETWORK
      • 그 외
        • REST API
          • REST API
          • URI & MIME type
          • Collection Pattern
          • Collection Pattern 적용
          • Spring Web MVC 구현
        • SSL 인증 동작
        • DTO & JSON & CROS
          • DTO
          • 직렬화(Serialization)
          • Jackson ObjectMapper
          • CROS
        • Connection Timeout / Read Timeout
      • 강의
        • 외워서 끝내는 네트워크 핵심이론 - 기초
          • Internet 기반 네트워크 입문
            • Host 는 이렇게 외우자
            • 스위치가 하는 일과 비용
          • L2 수준에서 외울 것들
            • NIC, L2 Frame, LAN 카드 그리고 MAC 주소
            • L2 스위치에 대해서
            • LAN 과 WAN 의 경계 그리고 Broadcast
          • L3 수준에서 외울 것들
            • IPv4 주소의 기본 구조
            • L3 IP Packet 으로 외워라
            • 패킷의 생성과 전달 및 계층별 데이터 단위
            • 이해하면 인생이 바뀌는 TCP/IP 송, 수신 구조
            • IP 헤더 형식
            • 서브넷 마스크와 CIDR
            • Broadcast IP 주소와 Localhost
            • TTL 과 단편화
            • 인터넷 설정 자동화를 위한 DHCP
            • ARP 과 Ping(RTT : Round Trip Time)
          • L4 수준 대표주자 TCP 와 UDP
            • TCP 와 UDP 개요
            • TCP 연결 및 상태 변화
            • TCP 연결 종료 및 상태 변화
            • TCP, UDP 헤더 형식과 게임서버 특징
            • TCP 가 연결이라는 착각
            • TCP 연결과 게임버그
          • 웹을 이루는 핵심기술
            • DNS
            • URL, URI
        • 외워서 끝내는 네트워크 핵심 이론 - 응용
          • 네트워크 장치의 구조
            • 세 가지 네트워크 장치 구조
            • Inline 구조
            • Out of path 구조와 DPI 그리고 망중립
            • Proxy(클라이언트 입장) - 우회
            • Proxy(클라이언트 입장) - 보호와 감시
            • Reverse Proxy(서버 입장)
          • 인터넷 공유기의 작동 원리
            • 공유기 개요
            • Symmetric NAT
            • Full Cone 방식
            • Restricted Cone, Port Restricted Cone
            • 포트 포워딩
            • UPnP 와 NAT
          • 부하분산 시스템 작동 원리
            • L4 부하분산 무정지 시스템
            • 대규모 부하분산을 위한 GSLB
          • VPN과 네트워크 보안 솔루션
            • PN 과 VPN
            • IPSec VPN 과 터널링 개념
            • VPN 과 재택근무
        • 외워서 끝내는 SSL 과 최소한의 암호기술
          • 기초이론
            • Checksum (검사합)
            • Hash
          • 암호기술에 대한 이해
            • 대칭키
            • 비대칭키
          • PKI 시스템과 인터넷
            • 인터넷을 위한 비대칭키 체계
            • 공개키 신뢰를 위한 검증체계
            • 웹서비스와 공인인증서
      • 책
        • 그림으로 배우는 네트워크 원리
          • 1. 네트워크 기본
          • 2. 네트워크를 만드는 것
          • 3. 네트워크의 공통 언어 TCP/IP
    • SECURITY
      • 그 외
        • Basic Auth
        • HMAC 기반 인증
    • 그 외
      • 동기/비동기 & 블로킹/논블록킹
  • DB
    • 그 외
      • 인덱스(Index)
      • 트랜잭션(TRANSACTION)
      • 실무에서 외래키를 사용하지 않는 이유
      • ORM vs SQL Mapper
      • 문자열 vs DATE
      • EXPLAIN 명령어
    • 강의
      • Real MySQL 시즌 1
        • Part.1
          • 1강. CHAR vs VARCHAR
          • 2강. VARCHAR vs TEXT
          • 3강. COUNT(*) & COUNT(DISTINCT) 튜닝
          • 4강. 페이징 쿼리 작성
          • 5강. Stored Function
      • 토크온 41차. JPA 프로그래밍 기본 다지기
        • 1. JPA 소개
        • 2. JPA 기초와 매핑
        • 3. 필드와 컬럼 매핑
        • 4. 연관관계 매핑
        • 5. 양방향 매핑
        • 6. JPA 내부구조
        • 7. JPA 객체지향쿼리
        • 8. Spring Data JPA 와 QueryDSL 이해
    • 책
  • Software Development Methodology
    • TDD
      • 강의
        • Spring Boot TDD - 입문부터 실전까지 정확하게
          • 세션2. TDD 소개
          • 세션5. API 설계
          • 세션6. TDD 주기 첫 번째 경험
          • 세션7. TDD 주기 반복
      • 그 외
        • 단위 테스트(Unit Test) 작성의 필요성
        • JUnit5
          • A Guide to JUnit 5
          • Guide to JUnit 5 Parameterized Tests
          • AssertJ Exception Assertions
          • Testing in Spring Boot
          • Junit 과 Mockito 기반의 Spring 단위 테스트 코드 작성법
        • Code Coverage
          • Code Coverage?
    • DDD
      • 책
        • 도메인 주도 설계(Domain-Driven Design)
          • 04 - 도메인의 격리
          • 05 - 소프트웨어에서 표현되는 모델
          • 06 - 도메인 객체의 생명주기
          • 07 - 언어의 사용(확장 예제) (1)
          • 07 - 언어의 사용(확장 예제) (2)
        • 도메인 주도 개발 시작하기
          • 1. 도메인 모델 시작하기
          • 2. 아키텍처 개요
          • 3. 애그리거트
          • 4. 리포지터리와 모델 구현
            • DAO vs Repository
      • 강의
        • DDD 세레나데(NEXTSTEP)
          • 1주차
            • 도메인 주도 설계 등장 배경
            • 레거시 코드
            • 유연한 설계 - ASSERTION
          • 2주차
            • 전략적 설계 - UBIQUITOUS LANGUAGE
            • 전략적 설계 - BOUNDED CONTEXT
          • 3주차
            • 전술적 설계 - VALUE OBJECT 와 ENTITY
            • 전술적 설계 - AGGREGATE 와 REPOSITORY
            • 전술적 설계 - SERVICE
    • REFACTORING
      • 일급 컬렉션(First Class Collection) 소개와 사용해야하는 이유
  • ARCHITECTURE
    • Event Driven Architecture
  • 멘토링
    • F-Lab
      • 10회차(2024.12.29)
Powered by GitBook
On this page
  • 리스트 추상화1 - 인터페이스 도입
  • 리스트 추상화2 - 의존관계 주입
  • 리스트 추상화3 - 컴파일 타임, 런타임 의존관계
  • 컴파일 타임 의존관계 vs 런타임 의존관계
  • 정리
  • 직접 구현한 리스트의 성능 비교
  • 직접 구현한 리스트의 성능 비교
  • 시간 복잡도와 실제 성능
  • 자바 리스트
  • 컬렉션 프레임워크 - 리스트
  • 자바 ArrayList
  • 자바 LinkedList
  • 자바 리스트의 성능 비교
  1. Language
  2. Java
  3. 강의
  4. 김영한의 실전 자바 - 중급2편

5. 컬렉션 프레임워크 - List

Previous4. 컬렉션 프레임워크 - LinkedListNext6. 컬렉션 프레임워크 - 해시(Hash)

Last updated 27 days ago

리스트 추상화1 - 인터페이스 도입

List 자료구조

순서가 있고, 중복을 허용하는 자료구조를 리스트라고 한다.

우리가 지금까지 만든 MyArrayList 와 MyLinkedList 는 내부 구현만 다를 뿐 같은 기능을 제공하는 리스트이다. 물론 내부 구현이 다르기 때문에 상황에 따라 성능은 달라질 수 있다. 핵심은 사용자 입장에서 보면 같은 기능을 제공한 다는 것이다. 이 둘의 공통 기능을 인터페이스로 뽑아서 추상화하면 다형성을 활용한 다양한 이득을 얻을 수 있다.

같은 기능을 제공하는 메서드를 MyList 인터페이스로 뽑아보자.

  • MyArrayList, MyLinkedList 모두 메서드 이름이 같기 때문에, 오류가 발생하지 않는다.

  • 추가로 재정의한 메서드에 @Override 애노테이션도 넣어주자

package collection.list;

public interface MyList<E> {

    int size();

    void add(E e);

    void add(int index, E e);

    E get(int index);

    E set(int index, E elememt);

    E remove(int index);

    int indexOf(E o);
}
package collection.list;

import java.util.Arrays;

public class MyArrayList<E> implements MyList<E> {
    //...
}
package collection.list;

public class MyLinkedList<E> implements MyList<E> {
      //...
}

리스트 추상화2 - 의존관계 주입

MyArrayList 를 활용해서 많은 데이터를 처리하는 BatchProcessor 클래스를 개발하고 있다고 가정하자. 그런데, 막상 프로그램을 개발하고 보니 데이터를 앞에서 추가하는 일이 많은 상황이라고 가정해보자.

데이터를 앞에서 추가 하거나 삭제하는 일이 많다면 MyArrayList 보다는 MyLinkedList 를 사용하는 것이 훨씬 효율적이다.

데이터를 앞에서 추가하거나 삭제할 때 빅오 비교

  • MyArrayList : O(n)

  • MyLinkedList : O(1)

구체적인 MyArrayList 에 의존하는 BatchProcessor 예시

  • MyArrayList를 사용해보니 성능이 너무 느려서MyLinkedList를 사용하도록 코드를 변경해야 한다면BatchProcessor의 내부 코드도 MyArrayList 에서 MyLinkedList 를 사용하도록 함께 변경해야 한다.

  • BatchProcessor 는 구체적인 MyArrayList 또는 MyLinkedList 를 사용하고 있다. 이것을 BatchProcessor 가 구체적인 클래스에 의존한다고 표현한다. 이렇게 구체적인 클래스에 직접 의존하면MyArrayList 를 MyLinkedList 로 변경할 때 마다 여기에 의존하는 BatchProcessor 의 코드도 함께 수정해야 한다.

public class BatchProcessor {

      private final MyArrayList<Integer> list = new MyArrayList<>(); 
      // private final MyLinkedList<Integer> list = new MyLinkedList<>(); //코드 변경
      
      public void logic(int size) {
            for (int i = 0; i < size; i++) {
                  list.add(0, i); //앞에 추가 
            }
      }
}

추상적인 MyList에 의존하는 BatchProcessor 예시

  • BatchProcessor 를 생성하는 시점에 생성자를 통해 원하는 리스트 전략(알고리즘)을 선택해서 전달하면 된다.

  • 이렇게 하면 MyList 를 사용하는 클라이언트 코드인 BatchProcessor 를 전혀 변경하지 않고, 원하는 리스트 전략을 런타임에 지정할 수 있다.

  • 정리하면 다형성과 추상화를 활용하면 BatchProcessor 코드를 전혀 변경하지 않고 리스트 전략(알고리즘)을 MyArrayList 에서 MyLinkedList 로 변경할 수 있다.

  • 이것은 BatchProcessor 의 외부에서 의존관계가 결정되어서 BatchProcessor 인스턴스에 들어오는 것 같다. 마치 의존관계가 외부에서 주입되는 것 같다고 해서 이것을 의존관계 주입(Dependency Injection, DI)이라 한다.

  • 참고로 생성자를 통해서 의존관계를 주입했기 때문에 생성자 의존관계 주입이라 한다.

public class BatchProcessor {

      private final MyList<Integer> list;
      
      public BatchProcessor(MyList<Integer> list) {
          this.list = list;
      }
      
      public void logic(int size) {
          for (int i = 0; i < size; i++) {
              list.add(0, i); //앞에 추가 
          }
      
}
}
main() {
    new BatchProcessor(new MyArrayList()); //MyArrayList를 사용하고 싶을 때 
    new BatchProcessor(new MyLinkedList()); //MyLinkedList를 사용하고 싶을 때
}

리스트 추상화3 - 컴파일 타임, 런타임 의존관계

의존관계는 크게 컴파일 타임 의존관계, 런타임 의존관계로 나눌 수 있다.

  • 컴파일 타임 : 코드 컴파일 시점

  • 런타임 : 프로그램 실행 시점

컴파일 타임 의존관계 vs 런타임 의존관계

컴파일 타임 의존관계

  • 컴파일 타임 의존관계는 자바 컴파일러가 보는 의존관계이다. 클래스에 모든 의존관계가 다 나타난다.

  • 쉽게 이야기해서 클래스에 바로 보이는 의존관계이다. 그리고 실행하지 않은 소스 코드에 정적으로 나타나는 의존 관계이다.

  • BatchProcessor 클래스를 보면 MyList 인터페이스만 사용한다. 코드 어디에도 MyArrayList or MyLinkedList 같은 정보는 보이지 않는다. 따라서 BatchProcessor 는 MyList 인터페이스에만 의존한다.

런타임 의존관계

  • 런타임 의존관계는 실제 프로그램이 작동할 때 보이는 의존관계다. 주로 생성된 인스턴스와 그것을 참조하는 의존 관계이다.

  • 쉽게 이야기해서 프로그램이 실행될 때 인스턴스 간에 의존관계로 보면 된다.

  • 런타임 의존관계는 프로그램 실행 중에 계속 변할 수 있다.

정리

  • MyList 인터페이스의 도입으로 같은 리스트 자료구조를 사용하면서 원하는 구현을 변경할 수 있게 되었다.

  • BatchProcessor 에서 다음과 같이 처음부터 MyArrayList 를 사용하도록 컴파일 타임 의존관계를 지정했

    다면 이후에 MyLinkedList 로 수정하기 위해서는 BatchProcessor 의 코드를 변경해야 한다.

public class BatchProcessor {
    private final MyArrayList<Integer> list = new MyArrayList(); //코드 변경 필요
}
  • BatchProcessor 클래스는 구체적인 MyArrayList 나 MyLinkedList 에 의존하는 것이 아니라 추상적 인 MyList 에 의존한다. 따라서 런타임에 MyList 의 구현체를 얼마든지 선택할 수 있다.

  • BatchProcessor 에서 사용하는 리스트의 의존관계를 클래스에서 미리 결정하는 것이 아니라, 런타임에 객체 를 생성하는 시점으로 미룬다. 따라서 런타임에 MyList 의 구현체를 변경해도 BatchProcessor 의 코드는 전혀 변경하지 않아도 된다.

  • 이렇게 생성자를 통해 런타임 의존관계를 주입하는 것을 생성자 의존관계 주입 또는 줄여서 생성자 주입이라 한 다.

  • 클라이언트 클래스는 컴파일 타임에 추상적인 것에 의존하고, 런타임에 의존 관계 주입을 통해 구현체를 주입받아 사용함으로써, 이런 이점을 얻을 수 있다.

직접 구현한 리스트의 성능 비교

직접 구현한 리스트의 성능 비교

직접 만든 배열 리스트와 연결 리스트 - 성능 비교 표

추가, 삭제

  • 배열 리스트는 인덱스를 통해 추가나 삭제할 위치를 O(1)로 빠르게 찾지만, 추가나 삭제 이후에 데이터를 한칸씩 밀어야 한다. 이 부분이 O(n)으로 오래 걸린다.

  • 연결 리스트는 인덱스를 통해 추가나 삭제할 위치를 O(n)으로 느리게 찾지만, 실제 데이터의 추가는 간단한 참조 변경으로 빠르게 O(1)로 수행된다.

앞에 추가(삭제)

  • 배열 리스트: 추가나 삭제할 위치는 찾는데 O(1), 데이터를 한칸씩 이동 O(n) -> O(n)

  • 연결 리스트: 추가나 삭제할 위치는 찾는데 O(1), 노드를 변경하는데 O(1) -> O(1)

평균 추가(삭제)

  • 배열 리스트: 추가나 삭제할 위치는 찾는데 O(1), 인덱스 이후의 데이터를 한칸씩 이동 O(n/2) -> O(n)

  • 연결 리스트: 추가나 삭제할 위치는 찾는데 O(n/2), 노드를 변경하는데 O(1) -> O(n)

뒤에 추가(삭제)

  • 배열 리스트: 추가나 삭제할 위치는 찾는데 O(1), 이동할 데이터 없음 O(1) -> O(1)

  • 연결 리스트: 추가나 삭제할 위치는 찾는데 O(n), 노드를 변경하는데 O(1) -> O(n)

인덱스 조회

  • 배열 리스트: 배열에 인덱스를 사용해서 값을 O(1)로 찾을 수 있음

  • 연결 리스트: 노드를 인덱스 수 만큼 이동해야함 O(n)

검색

  • 배열 리스트: 데이터를 찾을 때 까지 배열을 순회 O(n)

  • 연결 리스트: 데이터를 찾을 때 까지 노드를 순회 O(n)

시간 복잡도와 실제 성능

  • 이론적으로 MyLinkedList 의 평균 추가(중간 삽입) 연산은 MyArrayList 보다 빠를 수 있다. 그러나 실제 성능은 요소의 순차적 접근 속도, 메모리 할당 및 해제 비용, CPU 캐시 활용도 등 다양한 요소에 의해 영향을 받는다.

  • MyArrayList 는 요소들이 메모리 상에서 연속적으로 위치하여 CPU 캐시 효율이 높고, 메모리 접근 속도가 빠르다.

  • 반면에, MyLinkedList 는 각 요소가 별도의 객체로 존재하고 다음 요소의 참조를 저장하기 때문에, CPU 캐시 효율이 떨어지고, 메모리 접근 속도가 상대적으로 느릴 수 있다.

  • MyArrayList 의 경우 CAPACITY 를 넘어서면 배열을 다시 만들고 복사하는 과정이 추가되지만 한번에 2배씩 늘어나기 때문에, 이 과정을 가끔씩 발생한다. (전체 성능에 큰 영향을 주지 않는다)

정리하면 이론적으로는 MyLinkedList 가 평균 추가(중간 삽입) 에 있어서 더 효율적일 수 있지만, 현대 컴퓨터 시스템의 메모리 접근 패턴, CPU 캐시 최적화 등을 고려할 때 MyArrayList 가 실제 사용 환경에서 더 나은 성능을 보여주는 경우가 많다.

배열 리스트 vs 연결 리스트

대부분의 경우 배열 리스트가 성능상 유리하다. 이런 이유로 실무에서는 주로 배열 리스트를 기본으로 사용한다. 만약 데이터를 앞쪽에서 자주 추가하거나 삭제할 일이 있다면 연결 리스트를 고려하자.

자바 리스트

List 자료 구조

순서가 있고, 중복을 허용하는 자료 구조를 리스트라 한다. 자바의 컬렉션 프레임워크가 제공하는 가장 대표적인 자료 구조가 바로 리스트이다. 리스트와 관련된 컬렉션 프레임워크는 다음 구조를 가진다.

컬렉션 프레임워크 - 리스트

Collection 인터페이스

Collection 인터페이스는 java.util 패키지의 컬렉션 프레임워크의 핵심 인터페이스 중 하나이다.

이 인터페이스는 자바에서 다양한 컬렉션, 즉 데이터 그룹을 다루기 위한 메서드를 정의한다. Collection 인터페이스는 List , Set , Queue 와 같은 다양한 하위 인터페이스와 함께 사용되며, 이를 통해 데이터를 리스트, 세트, 큐 등의 형태로 관리할 수 있다. 자세한 내용은 뒤에서 다룬다.

List 인터페이스 List 인터페이스는 java.util 패키지에 있는 컬렉션 프레임워크의 일부다.

List 는 객체들의 순서가 있는 컬렉션을 나타내며, 같은 객체의 중복 저장을 허용한다. 이 리스트는 배열과 비슷하지만, 크기가 동적으로 변화하는 컬렉션을 다룰 때 유연하게 사용할 수 있다.

List 인터페이스는 ArrayList , LinkedList 와 같은 여러 구현 클래스를 가지고 있으며, 각 클래스는 List 인 터페이스의 메서드를 구현한다.

자바 ArrayList

java.util.ArrayList 자바가 제공하는 ArrayList 는 우리가 직접 만든 MyArrayList 와 거의 비슷하다. 특징은 다음과 같다.

자바 ArrayList의 특징

  • 배열을 사용해서 데이터를 관리한다.

  • 기본 CAPACITY 는 10이다.( DEFAULT_CAPACITY = 10 )

    • CAPACITY 를 넘어가면 배열을 50% 증가한다.

    • 10 15 22 33 49로 증가한다. (최적화는 자바 버전에 따라 달라질 수 있다.)

  • 메모리 고속 복사 연산을 사용한다.

    • ArrayList 의 중간 위치에 데이터를 추가하면, 추가할 위치 이후의 모든 요소를 한 칸씩 뒤로 이동시켜야 한다.

    • 자바가 제공하는 ArrayList 는 이 부분을 최적화 하는데, 배열의 요소 이동은 시스템 레벨에서 최적화된 메모리 고속 복사 연산을 사용해서 비교적 빠르게 수행된다. 참고로 System.arraycopy() 를 사용한다.

데이터 추가 - 메모리 고속 복사 연산 사용

  • 시스템 레벨에서 배열을 한 번에 아주 빠르게 복사한다.

  • 이 부분은 OS, 하드웨어에 따라 성능이 다르기 때문에, 정확한 측정이 어렵지만, 한 칸씩 이동하는 방식(MyArrayList 방식) 과 비교하면 보통 수 배 이상의 빠른 성능을 제공한다.

자바 LinkedList

java.util.LinkedList 자바가 제공하는 LinkedList 는 우리가 직접 만든 MyLinkedList 와 거의 비슷하다. 특징은 다음과 같다.

자바의 LinkedList 특징

  • 이중 연결 리스트 구조

  • 첫 번째 노드와 마지막 노드 둘다 참조

이중 연결 리스트

  • 자바가 제공하는 LinkedList 는 이중 연결 구조를 사용한다. 이 구조는 다음 노드 뿐만 아니라 이전 노드로도 이동할 수 있다.

    • 예) node.next 를 호출하면 다음 노드로, node.prev 를 호출하면 이전 노드로 이동한다.

  • 마지막 노드에 대한 참조를 제공한다. 따라서 데이터를 마지막에 추가하는 경우에도 O(1)의 성능을 제공한다.

  • 이전 노드로 이동할 수 있기 때문에 마지막 노드부터 앞으로, 그러니까 역방향으로 조회할 수 있다.

    • 덕분에 인덱스 조회 성능을 최적화 할 수 있다.

    • 예를 들어 인덱스로 조회하는 경우 인덱스가 사이즈의 절반 이하라면 처음부터 찾아서 올라가고, 인덱스가 사이즈의 절반을 넘으면 마지막 노드 부터 역방향으로 조회해서 성능을 최적화 할 수 있다.

자바 리스트의 성능 비교

직접 만든 배열 리스트와 연결 리스트 - 성능 비교 표

자바가 제공하는 배열 리스트와 연결 리스트 - 성능 비교 표

데이터를 추가할 때 자바 ArrayList가 직접 구현한 MyArrayList보다 빠른 이유

  • 자바의 배열 리스트는 이때 메모리 고속 복사를 사용하기 때문에 성능이 최적화된다.

  • 메모리 고속 복사는 시스템에 따라 성능이 다르기 때문에 정확한 계산은 어렵지만 대략 O(n/10) 정도로 추정하자. (상수를 제거하면 O(n)이 된다)

  • 하지만 메모리 고속 복사라도 데이터가 아주 많으면 느려진다.

시간 복잡도와 실제 성능

  • 이론적으로 LinkedList 의 중간 삽입 연산은 ArrayList 보다 빠를 수 있다. 그러나 실제 성능은 요소의 순차 적 접근 속도, 메모리 할당 및 해제 비용, CPU 캐시 활용도 등 다양한 요소에 의해 영향을 받는다.

  • 추가로 ArrayList 는 데이터를 한 칸씩 직접 이동하지 않고, 대신에 메모리 고속 복사를 사용한다. ArrayList 는 요소들이 메모리 상에서 연속적으로 위치하여 CPU 캐시 효율이 좋고, 메모리 접근 속도가 빠르다.

  • 반면, LinkedList 는 각 요소가 별도의 객체로 존재하고 다음 요소의 참조를 저장하기 때문에 CPU 캐시 효율 이 떨어지고, 메모리 접근 속도가 상대적으로 느려질 수 있다.

  • ArrayList 의 경우 CAPACITY 를 넘어서면 배열을 다시 만들고 복사하는 과정이 추가된다. 하지만 한번에 50%씩 늘어나기 때문에 이 과정은 가끔 발생하므로, 전체 성능에 큰 영향을 주지는 않는다.

정리하면 이론적으로 LinkedList 가 중간 삽입에 있어 더 효율적일 수 있지만, 현대 컴퓨터 시스템의 메모리 접근 패턴,CPU 캐시 최적화, 메모리 고속복사 등을 고려할 때 ArrayList 가 실제 사용 환경에서 더 나은 성능을 보여주는 경우가 많다.

배열 리스트 vs 연결 리스트

대부분의 경우 배열 리스트가 성능상 유리하다. 이런 이유로 실무에서는 주로 배열 리스트를 기본으로 사용한다. 만약 데이터를 앞쪽에서 자주 추가하거나 삭제할 일이 있다면 연결 리스트를 고려하자.