1. (잡담) 출력하는 무한 반복문을 실행할 경우 컴퓨터가 멈추거나 엄청나게 느려지는 이유
- CPU의 기본 처리 단위는 클럭인데 1클럭당 반복문을 1회 실행한다. CPU의 성능이 2.4gHz라면 1초에 2.4g번 화면에 출력하므로 엄청난 자원을 소모하게 된다.
- CPU는 컴퓨터에 한 개이기 때문에, 멀티 스레드를 사용하더라도 우선 순위가 높다면 자원을 많이 잡아 먹기도 한다.
제네릭을 사용하는 이유
ArrayList al = new ArrayList();
ArrayList<String> al = new ArrayList<String>();
- 둘다 사용 가능하다. 제네릭 <String>을 사용하는 이유는, 오브젝트를 인자로 받아서 컬렉션의 객체나 요소를 받아올 경우 다운캐스팅, 업캐스팅을 해야되고, 명확한 자료형 Type을 구분해내기 힘들다.
- 따라서 코드를 명확히 하기 위해 제네릭<>을 사용하는 것이 좋은 코딩 방법이다.
2. 제네릭 HashSet<E> 클래스
중복된 자료를 받지 않는다.
자동으로 오름차순 정렬을 해 값을 저장한다.(저장한 순서를 보장받지 못한다.)
Hashset(collection)
collection type을 매개변수로 받는다.
2.1 HashSet<String> 예제
3. 제네릭 TreeSet<> 클래스
HashSet<>과 출력 순서가 다르다.
중복된 요소를 허용하지 않는다.
3. HashSet<>과 TreeSet<>의 차이점
HashSet
- HashSet은 해시 테이블(정확히는 HashMap의 인스턴스)을 사용하여 요소를 저장합니다.
- 요소의 순서는 보장되지 않으멀로, 추가된 순서대로 요소를 반복할 수 없습니다.
- 요소의 추가, 삭제, 검색 작업의 시간 복잡도는 일반적으로 O(1)이지만, 최악의 경우(해시 충돌이 많을 때) O(n)이 될 수 있습니다.
- null 값을 요소로 저장할 수 있습니다.
TreeSet
- TreeSet은 레드-블랙 트리(Red-Black tree) 구조를 사용하여 요소를 저장합니다.
- 모든 요소는 정렬된 순서(자연 순서 또는 생성자에 제공된 Comparator에 의한 순서)로 유지됩니다. 따라서 요소를 반복할 때 정렬 순서대로 접근할 수 있습니다.
- 요소의 추가, 삭제, 검색 작업의 시간 복잡도는 O(log n)입니다.
- null 값을 요소로 저장할 수 없습니다(만약 null을 저장하려고 하면 NullPointerException을 발생시킵니다).
요약
- 정렬: HashSet은 요소의 순서를 보장하지 않지만, TreeSet은 요소를 정렬된 순서로 저장합니다.
- 성능: HashSet은 요소의 추가, 삭제, 검색 작업에서 상수 시간 성능을 제공할 수 있지만, TreeSet은 이러한 작업에 대해 로그 시간 성능을 제공합니다.
- 내부 구현: HashSet은 해시 테이블을, TreeSet은 레드-블랙 트리를 사용합니다.
- null 값: HashSet은 null 값을 요소로 허용하지만, TreeSet은 허용하지 않습니다.
사용 사례에 따라 두 클래스 중 하나를 선택할 수 있습니다. 예를 들어, 정렬된 순서로 데이터를 유지해야 한다면 TreeSet을 사용하는 것이 좋고, 성능이 중요한 상황에서는 대부분의 경우 HashSet이 더 나은 선택일 수 있습니다.
4. 해쉬코드란 ?
해시코드(hash code)는 객체를 식별할 수 있는 정수 값입니다. Java에서는 Object 클래스에 정의된 hashCode() 메소드를 통해 이 값을 얻을 수 있으며, 모든 Java 객체는 이 메소드를 상속받습니다. 해시코드의 주된 용도는 해시 기반 컬렉션(예: HashSet, HashMap, HashTable 등)에서 객체를 효율적으로 저장하고 검색하는 데 있습니다.
해시코드의 특징
- 일관성: 동일한 객체에 대해 hashCode() 메소드를 여러 번 호출하면, 프로그램 실행 동안 동일한 정수 값을 반환해야 합니다. 객체가 수정되지 않는 한, 해시코드 값은 일관되어야 합니다.
- 동등성: 두 객체가 equals() 메소드에 의해 동등하다고 판단될 경우, 이 두 객체는 반드시 동일한 해시코드 값을 가져야 합니다. 그러나 반대는 반드시 참이 아닙니다. 즉, 두 객체의 해시코드가 같다고 해서 두 객체가 반드시 동등한 것은 아닙니다(해시 충돌).
- 해시 충돌: 서로 다른 객체가 동일한 해시코드 값을 가질 수 있습니다. 이를 해시 충돌이라고 하며, 해시 기반 컬렉션은 이러한 충돌을 처리할 수 있는 메커니즘을 가지고 있습니다.
해시코드 사용
해시코드는 객체의 메모리 주소를 기반으로 계산될 수도 있지만, 이는 Java 사양에 의해 보장되는 것은 아닙니다. 실제 해시코드 생성 알고리즘은 개발자가 정의할 수 있으며, Object 클래스의 hashCode() 메소드를 오버라이드하여 객체의 상태를 반영하는 방식으로 구현할 수 있습니다. 이렇게 해시코드를 잘 구현하는 것은 해시 기반 컬렉션의 성능을 최적화하는 데 중요합니다.
예를 들어, Person 클래스에 대한 hashCode() 메소드를 구현할 때, 객체의 고유한 필드(예: 이름, 아이디 등)를 사용하여 해시코드를 계산하게 할 수 있습니다. 이렇게 하면 동일한 Person 객체(내용이 같은)는 항상 동일한 해시코드 값을 가지게 되어, 해시 기반 컬렉션에서 효율적으로 사용될 수 있습니다.
해시코드의 중요성
해시 기반 컬렉션에서는 객체의 저장 위치를 결정하기 위해 해시코드를 사용합니다. 따라서 해시코드의 분포가 균일할수록, 즉 충돌이 적을수록 데이터에 접근하는 속도가 빨라집니다. 해시코드 구현이 좋지 않아 충돌이 자주 발생한다면, 해시 테이블의 성능은 크게 저하될 수 있습니다.
5. Set.of();
불변 세트(immutable) 값을 수정하거나 추가할 수 없다. 고정된 컬렉션이다. 자바 8 버전부터 사용할 수 있다.
- 8줄: Set은 인터페이스지만 이런 방식으로 사용할 수 있다.
- 10줄 : 불변이기 때문에 add()는 안된다.
- 13줄 : Set 인터페이스를 사용하기 위해선 HashSet이나 TreeSet으로 동적바인딩한다.
- 18줄: HashSet<String>()생성자 매개변수로 Set.of() 메서드를 사용하면 불변세트를 초기값으로 설정해줄 수 있다.
- 출력 결과에서 대괄호가 나오는 것은 파이썬 처럼 자료형을 나타내는 것이 아니라 Object객체의 toString에 설정된 return값이다.
Set<String> set3 = new HashSet<String>("aaa","bbb","ccc");
이런식으론 못쓴다.
6. List<> 인터페이스
6.1 ArrayList<> 제네릭 클래스
- 11줄: null 값도 요소로 추가할 수 있다.
- 14줄, 18줄: ArrayList<> 요소 출력하는 방법
- 중간에 값을 추가하거나 삭제하는 것은 가능하다.
- ArrayList는 값을 추가하거나 삭제할 때 Qeue 방식을 써서 처리하기 때문에 로직상 성능이 안좋은 편이다.
6.2 LinkedList
- 중간에 값을 add()로 넣으면 값이 뒤로 밀린다.
7. Map<K, V>
HashMap과 HashTable은 Java의 Collection Framework에 포함된 두 가지 해시 테이블 구현입니다. 둘 다 키-값 쌍을 저장하는 데 사용되지만, 몇 가지 중요한 차이점이 있습니다.
1. 동기화(Synchronization)
- **HashTable**은 동기화됩니다. 이는 HashTable의 메소드들이 여러 스레드에 의해 동시에 실행되는 것을 안전하게 만들어 줍니다. 따라서 멀티 스레드 환경에서 안전하게 사용할 수 있지만, 이로 인한 오버헤드가 있습니다.
- **HashMap**은 동기화되지 않습니다. 이는 HashMap이 HashTable보다 더 빠르게 작동한다는 것을 의미하지만, 멀티 스레드 환경에서는 여러 스레드가 동시에 HashMap을 수정하려고 할 때 데이터의 무결성을 보장할 수 없습니다. 멀티 스레드 환경에서 HashMap을 사용할 때는 외부에서 동기화를 처리해야 합니다 (예: Collections.synchronizedMap을 사용하여 HashMap을 동기화된 맵으로 만들 수 있습니다).
2. 널(Null) 허용
- **HashMap**은 하나의 널 키와 여러 널 값을 가질 수 있습니다.
- **HashTable**은 키 또는 값으로 널을 허용하지 않습니다. 널을 사용하려고 하면 NullPointerException을 발생시킵니다.
3. 이터레이터(Iterator)와 열거형(Enumeration)
- **HashMap**의 이터레이터(Iterator)는 fail-fast 속성을 가집니다. 이는 맵이 이터레이터 생성 후에 수정될 경우 ConcurrentModificationException을 던집니다.
- **HashTable**은 열거형(Enumeration)과 이터레이터(Iterator) 둘 다를 제공하지만, 열거형은 fail-fast 속성이 없습니다.
4. 상속
- **HashMap**은 AbstractMap 클래스를 상속하고 Map 인터페이스를 구현합니다.
- **HashTable**은 Dictionary 클래스를 상속하며 Map 인터페이스를 구현합니다. Dictionary 클래스는 오래된 클래스이며, 새로운 코드에서는 Map 인터페이스를 구현한 클래스를 사용하는 것이 좋습니다.
요약
- HashMap은 멀티 스레드 환경에서 동기화를 지원하지 않으며, 널 키와 값들을 허용합니다. 더 빠르고 가볍습니다.
- HashTable은 멀티 스레드 환경에서의 사용을 위해 동기화되어 있지만, 널 키와 값을 허용하지 않습니다. 이는 오버헤드가 있고 성능이 더 느릴 수 있습니다.
대부분의 현대적인 Java 애플리케이션에서는 HashMap을 선호하며, 필요한 경우 명시적으로 동기화를 처리합니다. HashTable은 구식이며 대체로 사용되지 않습니다.
7.1 HashMap<>
- .containKey() 인자에 넣은 키 요소가 있으면 true를 반환한다.
- .get() 인자에 키 요소를 넣으면 value값을 반환한다.
- .put(인자1, 인자2) 키 값에 인자1을 넣고 밸류값에 인자2를 넣는다.
- HashMap<key type, valuse type> 제네렉 클래스 타입
- 키 값과 대응되는 밸류값에 대한 메서드가 여럿 존재한다.
댓글