hashCode(), equals() 구현해야 한다.ArrayList, LinkedListnew ArrayList<>(1000000);ensureCapacity() 메서드로 용량을 늘려 크기 조정 작업을 건너뛸 수 있다.add() 작업은 항상 O(1)이다.ArrayList는 다른 원소를 모두 한 칸씩 밀어야한다.LinkedList는 특정 인덱스에 추가해도 레퍼런스만 변경하면 된다.ArrayList는 랜덤 엑세스 하는 경우 모든 원소를 O(1)만에 가져올 수 있다.LinkedList는 인덱스 카운트만큼 원소를 방문해야 한다.LinkedList 고유 기능이 필요한 경우가 아니고 랜덤 엑세스가 필요한 알고리즘에선 ArrayList를 권장
java.util.Map<K, V> 인터페이스를 제공하며 키/값 모두 참조형이어야 한다.HashMap 축소 버전 핵심 메서드
public Object get(Object key) {
// 편의상 널 키는 지원하지 않음
if (key == null) return null;
int hash = key.hashCode();
int i = indexFor(hash, table.length);
for (Entry e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
private int indexFor(int h, int length) {
return h & (length - 1);
}
static class Node implements Map.Entry {
final int hash;
final Object key;
Object value;
Node next;
Node(int h, Object k, Object v, Entry n) {
hash = h;
key = k;
value = v;
next = n;
}
}
HashMap.Node 클래스는 java.util 패키지에서만 접근할 수 있다. (정적 클래스 사용하는 전형적인 유스케이스)HashMap 배치
equals() 메서드로 리스트에서 키를 찾는다. (중복 허용 x)indexFor() 메서드를 키 객체의 hashCode() 메서드를 사용하는 코드로 교체
HashMap 생성자에 전달하는 두 매개변수는 성능에 큰 영향을 미친다.
initialCapacity: 현재 생성된 버킷 개 수(디폴트 16)loadFactor: 버킷 용량 자동 증가시키는 한계치 (디폴트 2배)HashMap의 get(), put()은 일정 시간이 소요되지만 순회를 하면 비용이 증가할 수 있다.LinkedList로 구현하면 리스트를 훑어보는 작업이 커질수록 비용이 더 든다.HashMap은 한 버킷에 TREEIFY_THRESHOLD 값만큼 키/값 쌍이 모이면 버킷을 TreeNode로 비꾸어 마치 TreeMap처럼 동작한다.TreeNode는 리스트 노드에 비해 2배 더 공간을 차지하기에 처음부터 쓰지는 않는다.LinkedHashMap
HashMap의 서브클래스로 이중 연결 리스트를 사용해 원소의 삽입 순서를 관리한다.TreeMap처럼 비용이 많이 들지 않는다.TreeMap은 레드-블랙 트래를 구현한 Map
레드-블랙 트리: 기본 이진 트리 구조에 메타데이터를 부가(노드 컬러링)해서 트리 균형이 치우치는 현상을 방지한 트리
TreeMap은 다양한 키가 필요할 때 아주 유용하다.
TreeMap이 제공하는 get(), put(), containsKey(), remove() 메서드는 log(n) 작업 성능을 보장Map 일부를 처리해야할 때 TreeMap을 쓰는편이 바람직하다.MultiMap(하나의 키에 여러 값을 묶은 맵) 구현체를 제공하지 않는다.Map<K, List<V>> 형태로도 충분하다.Set이 존재하며 고려해야할 사항은 Map과 비슷하다.
HashSet은 HashMap으로 구현되어 있다.Set은 중복을 허용하지 않기에 Map의 키 원소와 같다.
HashSet의 add() 메서드가 내부적으로는 HashMap의 키에 대응되고, 값은 PRESENT라는 더미 객체로 구성HashSet의 삽입/삭제, contains 작업의 복잡도는 O(1)
initialCapacity, loadFactor에 따라 달라진다.TreeSet 역시 TreeMap을 활용
Order, OrderItem, DeliverySchedule 등Order에는 여러 OrderItem 인스턴스가 매핑됨jamp -histo 명령이나 VisualVM 같은 GUI 툴을 통해 자바 힙 상태를 살펴볼 수 있는데 도메인 객체 메모리 누수 현상을 효과적으로 진단할 수 있다.누수를 일으키는 도메인 객체는 종종 GC 마킹 시간을 증가시키는 주범이다. 단명 객체가 긴 전체 객체 체인에 걸쳐 살아남기 때문
finalize() 메서드는 자동으로 리소스를 관리하려고 만든 장치close() 호출은 정말 깜빡 잊고 놓치기 쉽다.
Object의 Finalize() 메서드는 자바 태동기부터 존재했다.
어떤 객체가 더 이상 자신을 참조하지 않는다고 가비지 수집기가 판단하면 그 객체에 있는 finalize() 메서드를 호출한다. 서브 클래스는 finalize() 메서드를 오버라이드해서 시스템 리소스를 처분하는 등 기타 정리 작업을 수행한다.
Object 생성자 바디에서 성공 반환되는 시점에 해당 객체를 등록하는 식으로 JVM에 구현돼 있다.
Finalize() 실행Finalize() 문제점
Finalize()를 실행해야 한다. (스레드 생성 오버헤드 감수)Object.Finalize()는 디프리케이트 됐다.자바 7 이전까진 개발자가 책임지고 리소스를 닫아야 했다.
public void readFirstLineOld(File file) throws IOException {
BufferReader reader = null;
try {
reader = new BufferedReader(new FileReader(file));
// ...
} finally {
if (reader != null) {
reader.close();
}
}
}
자바 7부터 try-with-resources 생성자를 통해 AutoCloseable 인터페이스를 구현한 객체를 자동으로 닫을 수 있다.
public void readFirstLineNew(File file) throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
String firstLine = reader.readLine();
// ...
}
}
런타임 GC에 의존하는 종료화와 달리 try-with-resources는 순수한 컴파일 타임 기능이다.
invokedynamic 명령어 덕분에 호출부에서 실행할 메서드를 유연하게 결정할 수 있게 되었다.
invokedynamic 호출부에 의해 호출되는 메서드를 나타낸 객체자바 7부터 일부 클래스, 패키지(MethodHandle)가 추가되어 실행 가능한 메서드의 레퍼런스를 직접 반영할 수 있게 됐다.
MethodType mt = MethodType.methodType(int.class);
MethodHandles.Lookup l = MethodHandles.lookup();
MethodHandle mh = l.FindVirtual(String.class, "hashCode", mt);
String reveiver = "b";
int ret = (int) mh.invoke(receiver);
System.out.println(ret);
MethodHandles.lookup() 메서드로 컨텍스트 객체를 생성setAccessible()말고는 방법이 없어 자바의 안전한 접근 제어 체계에 허점을 노출시킨다.대부분 개발자에게 메서드 핸들이란 코어 리플렉션과 기능은 비슷하나 최대한 정적 타입을 안전하게 지키는 요즘 방식의 리플렉션이라고 생각하면 된다.