Skip to content

Latest commit

 

History

History
296 lines (244 loc) · 11 KB

2023-05-14.md

File metadata and controls

296 lines (244 loc) · 11 KB

2023-05-14

키워드

  • Final
    • 클래스 : 더 이상 상속이 안된다
    • 메소드 : 더 이상 오버라이딩이 안된다
    • 필드 : 더 이상 바꿀 수 없다
      • 기본 자료형에 final : 할당한 값을 바꿀 수 없다
      • 참조 자료형에 final : 할당한 값을 바꿀 수 없거나 새로 생성자를 사용해서 초기화 할 수
  • Enum
  • Enum 상수 저장 영역
  • Enum을 쓰는 이유
  • Java에서 Enum 장점
  • LinkedList
  • Array의 한계점 : 배열의 크기는 고정된다는 것, 새로운 요소를 삽입하거나 삭제하기 위해 빈 공간 관리를 해주어야 한다
  • 배열과 비교한 Linked List의 장점 : 삽입 삭제가 수월함 (맨 앞에 아이템 삽입 : O(1))
  • LinkedList의 삽입 / 삭제 방식

Final 키워드

  1. 클래스
  2. 메소드
  3. 필드

1. 클래스에 final

public final class FinalClass {
}
  • 더 이상 확장해서는 안되는 클래스, 상속 받아서 내용을 변경해서는 안되는 클래스를 선언할 때 final

2. 메소드에 final

public abstract class FinalMethodClass {
  public final void printLog(String data) {
    System.out.println("Data=" + data); 
  }
}
  • 더 이상 오버라이딩할 수 없게 할 때 final

3. 필드에 final

public class FinalVariable {
  final int instanceVariable = 1;
}
  • 더 이상 값을 바꿀 수 없는 변수를 선언할 때 final
    • 기본 자료형에 final : 값을 바꿀 수 없다
    • 참조 자료형에 final : 두 번 이상 값을 할당하거나 새로 생성자를 사용해서 초기화할 수 없다
  • 변수 생성과 동시에 초기화를 해야만 컴파일 시 에러가 발생하지 않는다
  • 변수 선언만 하고 생성자나 메소드에서 초기화하면 되는 것 아닌지?
    • 중복되어 변수값이 선언될 수도 있으니까 final의 기본 의도를 벗어난다

매개변수나 지역변수에 final

public class FinalVariable {
  final int instanceVariable = 1;
  
  public void method(final int parameter) {
    final int localVariable;
    localVariable = 2;
    // localVariable = 3; // 재할당 불가능하다 (컴파일에러)
    // parameter = 4; // 재할당 불가능하다 (컴파일에러)
  }
}
  • 매개변수나 지역변수를 final로 쓸 때는, 선언과 동시에 반드시 초기화 할 필요는 없음 (매개변수는 어차피 초기화되어 넘어오는 값이고, 지역 변수는 메소드를 선언하는 중괄호 내에서만 참조되니까)
  • 재할당만 안하면 된다

참조 자료형에서의 final 주의점

public class FinalReferenceType {
  final MemberDTO dto = new MemberDTO(); // 선언과 동시에 초기화
  
  public static void main(String[] args) {
    FinalReferenceType referenceType = new FinalReferenceType();
    referenceType.checkDTO();
  }
  
  public void checkDTO() {
    System.out.println(dto);
    // dto = new MemberDTO(); // 두 번 이상 값을 할당할 수 없다
    dto.name = "Sangmin";
    System.out.println(dto);
  }
}
  • final로 선언함과 동시에 초기화 필요하고

  • 값을 다시 할당할 수 없다

  • dto 객체는 두 번 이상 생성할 수 없지만, 그 객체 안의 객체들은 final로 선언된 것이 아니므로 이런 제약이 없다!!!

  • 해당 클래스가 final 이라고 해서 그 안에 있는 인스턴스 변수나 클래스 변수가 final은 아니다

Enum

  • 열거 타입 (Enumeration Type)
  • 한정된 값을 갖는 데이터 타입
public enum 열거타입이름 { ... }
public enum Week {
  MONDAY,
  TUESDAY,
  WEDNESDAY,
  ...
}

Enum Type 변수

// 열거타입 변수;
Week today;
  • 열거 타입 변수를 선언하면, Enum 상수를 지정해서 Enum 클래스 객체 생성을 완료할 수 있다
// 열거타입 변수 = 열거타입.열거상수;

Week today = Week.SUNDAY;
  • Enum 타입은 참조 타입이기 때문에 null도 저장할 수 있다
Week birthday = null;
  • Enum 상수도 Enum 객체로 생성된다
  • 그래서, 열거 상수는 총 7개의 Enum 객체로 Heap 영역에 생성된다
  • 그리고 메소드 영역에 생성된 열거 상수가 Enum 객체를 각각 참조하게 된다

Enum

Week today = Week.SUNDAY;
  • 열거 타입 변수 (today) 는 스택 영역에 생성된다
  • today 에 저장되는 값은 Week.SUNDAY (열거 상수)가 참조하는 객체의 번지를 가진다

Enum_area

Enum 클래스의 부모 java.lang.Enum

  • Enum 클래스는 무조건 java.lang.Enum 이라는 클래스의 상속을 받는다
  • extends 하지 않아도 컴파일러가 알아서 이 문장을 추가해서 컴파일한다
  • 누가 만들어 놓은 enum을 extends 할 수도 없다

Enum 클래스의 생성자 (컴파일러에서 자동으로 호출되도록 해놓은 생성자, 개발자가 이 생성자를 호출할 수는 없다)

protected Enum(String name, int ordinal) { ... }
  • name : enum 상수의 이름
  • ordinal : 상수가 선언된 순서대로 0부터

왜 굳이 Enum을 쓰는지?

  • 문자열과 비교해서, IDE의 지원을 받을 수 있다
  • 허용 가능한 값들을 제한할 수 있다
  • 리팩토링 시 변경 범위가 최소화된다 (내용의 추가가 필요해도, Enum만 수정하면 된다) (참고 : https://techblog.woowahan.com/2527/)

Java 에서의 Enum 장점 (추가 공부 필요)

  1. 데이터들 간의 연관 관계 표현
  2. 상태와 행위를 한 곳에서 관리할 수 있다
  3. 데이터 그룹을 관리할 수 있다
  4. 관리 주체를 DB에서 객체로 전환할 수 있다

Linked List

Array (배열)의 한계점

  • 배열의 크기는 고정,
  • 새로운 요소를 삽입하거나 삭제 시, 배열의 경우 삽입 시에는 새로운 요소가 들어갈 빈 공간을 만들어 주어야 하고, 삭제 시에는 요소가 삭제되어 빈 공간이 생기면, 이를 없애주어야한다

배열과 비교하여 Linked List의 장점

  • Dynamic Array
  • 삽입 / 삭제가 수월하다
  • Linked List의 맨 앞에 새로운 아이템을 삽입하는 경우의 시간 복잡도는 O(1) 이다
  • 배열의 맨 앞에 새로운 아이템을 삽입하는 경우의 시간 복잡도는 O(n) 이다

Linked List의 단점

  • Random access 가 허용되지 않는다
    • 배열의 경우, 인덱스만 알면 random access 를 할 수 있는데, Linked List의 경우 맨 앞의 head node부터 순차적으로 접근을 해야하므로 linked list에서는 이진 탐색을 할 수 없다
  • list 내 각 노드에 값, 포인터가 들어가서 포인터를 저장할 추가적인 메모리 공간이 필요하다
  • 배열의 경우 연속적인 공간을 할당 받으므로, 캐싱하기 쉬운데, linked list의 경우 연속적인 공간을 할당받지 않으므로 캐싱하기 쉬운 것은 아니다
  • 포인터를 바꾸려면 시간이 오래 걸린다
  • 단방향 연결 리스트 (singly linked list) 에서는 역방향으로 순회하는 것이 불가능하다
  • 탐색은 O(N) 의 시간 복잡도를 가진다
  • linked list의 마지막에 아이템을 삽입하는 것은 O(N) 의 시간 복잡도를 가진다

Linked List의 타입

  1. Simple Linked List
  • 각 노드는 단방향으로만 연결되어 있고, 각 노드의 포인터는 다음 노드만 가리킨다
  • 마지막 노드의 포인터는 NULL 이다
  1. Doubly Linked List
  • 각 노드의 순방향, 역방향으로 탐색이 가능하다
  1. Circular Linked List
  2. Doubly Cicular Linked List

Singly Linked List의 구현

  • linked list의 첫번째 노드는 head 라고 불린다
  • 각 노드는 두 부분으로 구성되어 있다
    1. Data : 값을 저장하는 부분
    2. Pointer : 다음 노드로의 참조 또는 다른 노드의 주소
class LinkedList {
    
    Node head; // linked list의 head

    class Node {
        int data;
        Node next;

        // 새로운 노드 생성자
        // 처음에는 next가 null로 초기화
        Node(int d) {
            data = d;
            next = null;
        }
    }
}

Linked List의 연산

  1. 삭제
  2. 삽입
  3. 검색

Linked List의 순회

class LinkedList {
    Node head;

    static class Node {
        int data;
        Node next;

        Node(int d) {
            data = d;
            next = null;
        }
    }

    public void printList() {
        Node n = head;

        while (n != null) {
            System.out.println(n.data + " ");
            n = n.next;
        }
    }

    public static void main(String[] args) {
        LinkedList list = new LinkedList();

        list.head = new Node(1);
        Node second = new Node(2);
        Node third = new Node(3);

        list.head.next = second;
        second.head.next = third;

        list.printList();
    }

}

Linked List의 시간 복잡도

시간복잡도 최악의 경우 평균
검색 O(N) O(N)
삽입 O(1) O(1)
삭제 O(1) O(1)

Linked List의 Random Access

  • 배열은 Random Access에 대해 O(1)의 시간 복잡도를 가진다
  • 배열과는 다르게, Linked list 에서는 O(1) 만에 요소에 접근할 수는 없다
  • 왜냐하면, 연결 리스트에서 i번째 항목에 접근하려면, 첫번째 헤드 노드부터 하나씩 순회해야 한다. 따라서, N개의 요소가 있는 연결 리스트에서는 인덱스를 통해 요소에 접근하려면, 평균적으로 O(N) 이 걸린다.

LinkedList의 삽입

insertion insertion2 insertion3

  1. 새로운 노드 cur를 주어진 값으로 초기화
  2. cur 노드의 다음 노드로 next 노드를 연결해주기 위해 curnext 필드를 next 노드로 설정한다
  3. prev 노드의 next 필드를 cur 노드로 설정한다

Linked List의 삭제

deletion deletion2

  1. 삭제될 노드의 앞 노드와 이후 노드를 찾는다
  2. 이전 노드인 prev 노드의 next 필드를 next 노드로 연결한다.

cur 노드의 이후 필드인 next 노드를 찾는 것은 어렵지 않은데, 이전 노드인 prev 노드를 찾는 것은 첫번째 head 노드부터 prev 노드를 찾아야 하므로, O(N) 의 시간복잡도가 소요된다. 따라서, 연결 리스트의 삭제 연산은 O(N) 를 가진다고 할 수있다.