Java List 인터페이스 중 CopyOnWriteArrayList 소개

JAVA/JAVA 관련|2018. 6. 3. 16:36

자바에는 크게 4개의 List 인터페이스를 구현한 클래스가 있다.

- Vector, ArrayList, LinkedList, CopyOnWriteArrayList


그 중 가장 생소한 이름이 있는데 CopyOnWriteArrayList이다. CopyOnWriteArrayList는 그냥 ArrayList랑 다르길래 화려한 이름을 가지고 있는걸까?


ArrayList vs CopyOnWriteArrayList

일반 ArrayList의 경우 스레드에 안전하게 설게되어 있지 않기때문에 만약 스레드 처리가 필요한 List의 경우에 Vector를 사용하거나 ArrayList에 synchroized를 사용하여 처리하였다. 하지만 자바 1.5부터 있던 CopyOnWriteArrayList를 쉽게 이문제를 해결할 수 있다.


CopyOnWriteArrayList의 경우 ArrayList와 모든 부분이 동일하나 어디에 컨텐츠를 전달할 때 컨텐츠를 복사해서 전달한다. 그렇기 때문에 전달 후 해당 List의 내용이 변경될 것을 우려하지 않아도 된다.


CopyOnWriteArrayList는 그냥 ArrayList보다 물론 부담이 있을수 있다. 하지만 잘못설계된 동기화코드보다 더 안전하고 비용이 절감될 수도 있다.


그리고 CopyOnWriteArrayList는 객체를 매번 복사하는 것이 아니라 전달시 해당 상태를 스냅샷으로 가지고 있는 방식으로 진행한다.

자세한 내용은 아래 사이트를 참조하면 도움이 될 것이다.


https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CopyOnWriteArrayList.html

댓글()

제네릭 - 규칙 25 배열 대신 리스트를 써라

JAVA/Effective Java|2018. 5. 29. 22:50

배열과 제네릭 자료형(List)의 차이


배열은 convariant  제네릭 자료형은 invariant 자료형이다.

차이 1.

covariant 
- Sub[] 이 Super 의 하위자료형이라면 Sub[] 은 Super[]의 서브 타입이다. 

invariant
Type1 Type2 List<Type1> List<Type2> 의 서브타입도 슈퍼타입도 아니다.
그렇기 때문에 List<Type1>은 List<Type2>의 서브타입 또는 슈퍼타입도 될 수 없다.

이런 이유로 제네릭 쪽이 배열보다 취약한 것 같지만 다음과 같은 문제를 보면 
오히려 문제가 발생하는 부분은 배열이다.



1
2
3
4
5
6
7
8
// 실행 중에 문제를 일으킴 (배열)
Object[] objectArray = new Long[1];
objectArray[0] = "babo ya";
=> 컴파일시 문제는 없지만 런타임 시 ArrayStoreException 예외가 발생한다.
 
// 컴파일시 문제가 발생하는 코드 (제네릭)
List<Object> ol = new ArrayList<Long>(); 
ol.add("babo ya");
cs



배열과 제네릭 모두 Long 객체 컨테이너안에 String 객체를 넣을 수는 없다.
그러나 배열은 위와 같이 실수를 유발할 수 있기 때문에 제네릭 타입이 더 좋다.


차이 2.
배열은 실체화(reification) 되는 자료형이다.
-> 배열의 각 원소의 자료형은 런타임시에 결정된다.
-> 위의 예를 통해 보면 알 수 있지만, String객체를 Long 배열에 넣는것이 컴파일시에는 문제가 발생하지 않는다는것이다.

리스트는 컴파일 시점에만 적용되고, 자료형 정보는 실행될 때는 삭제된다.


이런 차이들로 인해 배열과 제네릭(List)는 섞어서 사용하기 어렵다.
그렇기 때문에 new List<E>[], new List<String>[], new E[]는 전부 컴파일 되지 않는 오류이다.

이로인해 제네릭 배열을 만들 수 없다는 것이 불편함을 야기할 수 있다.
그렇기에 List<E>를 써서 배열을 대신할 수 있다. 대신 불편하지만 형안전성을 보장받을 수 있다.

그리고

배열은 컴파일시 리스트는 런타임시 체크를 하기 때문에
다음과 같이 list의 toArray에 캐스팅을 하게되면 런타임시 E가 무슨 자료형이 될지 알 수 없기 때문에 아래와 같은 경고 메시지가 출력된다.



<E> E reduce(List<E> list) { E[] snapshot = (E[]) list.toArray(); // uncheck cast 경고 메시지 }




이는 제네릭(List)는 불변 자료형이며, 실행시간에 형인자의 정보는 삭제가 되기 때문에 배열로 변경될 시 형 안정성을 보장하지 못하기 때문에 오류가 발생하는 것이다. 

그래서 위와 같은 경우에는 배열이 아닌 ArrayList를 사용해야 한다.



1
2
3
<E> E reduce(List<E> list) {
  List<E> snapshot = new ArrayList<>(list);
}
cs



결론으로
배열과 제네릭은 쉽게 혼용될 수 없다. 
서로 혼용해서 사용하다가 컴파일 오류나 경고 메시지를 만나게 되면, 배열을 리스트로 바꿔서 사용해야 한다는 것을 기억해야 한다.

출처 : 조슈아 블로크, 『 Effective Java 2/E』, 이병준 옮김, 인사이트(2014.9.1), 규칙25 인용.

댓글()

더블링크드 리스트 구현하기

JAVA/알고리즘|2018. 5. 28. 22:43
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package java8;
 
 
 
public class Node {
 
 
 
 public Node next;
 
 public Node prev;
 
 private String value;
 
 
 
 public Node(String value) {
 
  this.value = value;
 
 }
 
 
 
 public Node() {
 
  
 
 }
 
 
 
 public String getValue() {
 
  return value;
 
 }
 
 
 
 public void setValue(String value) {
 
  this.value = value;
 
 }
 
}
 
 
 
 
 
 
 
 
package java8;
 
 
 
import java.util.Scanner;
 
 
 
public class MainClass {
 
 public static void main(String args[]) {
 
  System.out.print("값을 입력하세요. : ");
 
  Scanner in = new Scanner(System.in);
 
  Node parentNode = new Node(in.next());
 
  Node curr = parentNode;
 
  StringBuilder str = new StringBuilder();
 
 
 
  while (true) {
 
   System.out.print("값을 입력하세요. : ");
 
   String input = in.next();
 
   if (input.equals("-1")) {
 
    break;
 
   }
 
   Node node = new Node(input);
 
   curr.next = node;
 
   node.prev = curr;
 
   curr = node;
 
  }
 
 
 
  curr = parentNode;
 
 
 
  while (true) {
 
   System.out.print(curr.getValue() + " ");
 
   if (curr.next == null)
 
    break;
 
   curr = curr.next;
 
  }
 
  System.out.println();
 
 
 
  while (curr != null) {
 
   System.out.print(curr.getValue() + " ");
 
   if (curr.prev == null)
 
    break;
 
   curr = curr.prev;
 
  }
 
 }
 
}
cs


'JAVA > 알고리즘' 카테고리의 다른 글

정렬알고리즘 - 선택정렬  (0) 2018.05.28
10진수 2진수 변환  (0) 2018.05.28
더블링크드 리스트 구현하기  (0) 2018.05.28
백준 1924 - 요일 맞추기  (0) 2018.05.28
백준 2839 - 설탕 배달  (0) 2018.05.28
선택정렬, 버블정렬, 삽입정렬 예제  (0) 2016.12.22

댓글()