Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
### Java template
# Compiled class file
*.class

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

.DS_Store
.gradle
/build/
!gradle/wrapper/gradle-wrapper.jar
/out/
/target/

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
bin/

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr


build/
gradle/
test/
racingcar/src/test/
54 changes: 54 additions & 0 deletions java basics/Collection Framework.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Collection Framework
## 컬렉션 프레임워크란?

배열은 크기가 고정적이라 여러 비효율적인 문제가 생긴다.

배열의 크기는 생성할 때 결정된다.

- 배열의 크기를 벗어나면 데이터를 더 이상 저장할 수 없다.
- 데이터를 삭제하면 해당 인덱스의 데이터는 비어있어 메모리가 낭비되는 문제가 발생한다.

이런 문제점들을 해결하기 위해 객체나 데이터들을 효율적을 관리, 추가, 삭제, 검색, 저장할 수 있는 자료구조들을 만들어 두었다. 이런 라이브러리를 컬렉션 프레임워크라고 한다.

![컬렉션 프레임워크]([https://blog.kakaocdn.net/dn/bdy438/btqEjPZKIY0/e5Wm8ZJmdRNza4tKBzaK6k/img.png](https://blog.kakaocdn.net/dn/bdy438/btqEjPZKIY0/e5Wm8ZJmdRNza4tKBzaK6k/img.png))

## List 컬렉션

컬렉션 프레임워크를 상속받고 있는 List 컬렉션은 객체를 일렬로 늘어놓은 구조를 가지고 있다. List 컬렉션은 객체를 인덱스를 관리하기 때문에 객체를 저장하면 자동 인덱스가 부여되고 인덱스로 객체를 검색, 삭제할 수 있는 기능을 제공한다.(인덱스에는 데이터가 저장되어 있는 참조 값을 가지고 있다.)

![List 컬렉션]([https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxhCVv%2FbtqEg09LXoG%2Fm26SctApZoPjJtRaAEmlSk%2Fimg.png](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxhCVv%2FbtqEg09LXoG%2Fm26SctApZoPjJtRaAEmlSk%2Fimg.png))

List 컬렉션은 객체 자체를 저장하는 것이 아니라 위와 같이 객체의 번지를 참조합니다. 동일한 객체를 중복 저장할 수 있는데 이 경우 동일한 번지가 참조됩니다. null도 저장이 가능한데 이 경우에는 해당 인덱스는 객체를 참조하지 않는다. List 컬렉션을 구현하는 대표적인 클래스들은 ArrayList, LinkedList, Vector가 있으며 이 3가지 클래스는 List 인터페이스를 같이 상속하고 있으므로 공통적으로 사용할 수 있는 메소드들이 많다.

- List 클래스 주요 메소드

[제목 없음](https://www.notion.so/f9122f0852d3491b8f8c9735430501f4)

## Set 컬렉션

List 컬렉션은 선형구조를 가지고 있으므로 추가한 순서대로 저장돼어 순서를 유지했지만 Set 컬렉션은 저장 순서가 유지되지 않는다. Set 컬렉션은 순서 자체가 없어 인덱스로 객체를 검색해서 가져오는 get(index) 메소드도 존재하지 않는다. 대신 전체 객체를 대상으로 한 번씩 반복해서 가져오는 반복자(Iterator)를 제공한다. Iterator는 .iterator() 메소드를 호출하면 얻을 수 있다.

![Set 컬렉션]([https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLMuJG%2FbtqEgQzQaFv%2F18xV7JmoktO3gKPnYitGZ0%2Fimg.png](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLMuJG%2FbtqEgQzQaFv%2F18xV7JmoktO3gKPnYitGZ0%2Fimg.png))

또한 Set은 객체를 중복해서 저장할 수 없고 하나의 중복 저장이 안 되기에 null값도 하나만 저장할 수 있다. Set 컬렉션을 구현하는 대표적인 클래스들은 HashSet과 TreeSet이 있다. 이 2가지 클래스는 Set 인터페이스를 같이 상속하고 있으므로 공통적으로 사용할 수 있는 메소드들이 존재한다.

- Set 클래스 주요 메소드

[제목 없음](https://www.notion.so/8643a61c33a74b5a825cffb7fc4ab359)

## Map 컬렉션

Map 컬렉션은 키Key 와 값Value으로 구성된 객체를 저장하는 구조를 가지고 있는 자료구조이다. 키는 중복으로 저장할 수 없고 값은 중복으로 저장할 수 있으며 중복된 key값이 들어온다면 기존의 값은 없어지고 새로운 값으로 대치된다.

![Map 컬렉션]([https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDaHeK%2FbtqEjQx07Ng%2FPQSBhv0USEnMzQnzuMFw61%2Fimg.png](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDaHeK%2FbtqEjQx07Ng%2FPQSBhv0USEnMzQnzuMFw61%2Fimg.png))

Map은 Key와 Value라는 것을 한 쌍으로 갖는 자료형이다. Map은 리스트나 배열처럼 순차적으로(sequential) 해당 요소 값을 구하지 않고 key를 통해 value를 얻는다. Map 컬렉션을 구현하는 대표적인 클래스들은 HashMap, hashtable, LinkedHashMap, TreeMap 등이 있다.

- Map 클래스 주요 메소드

[제목 없음](https://www.notion.so/ca00a7b06d9c445c8fa05ad3f02ed378)

# Reference

- [자바 컬렉션 프레임워크 총정리. 티스토리]([https://coding-factory.tistory.com/550](https://coding-factory.tistory.com/550))
- [자바 ArrayList 사용법, 예제 총정리. 티스토리.[([https://coding-factory.tistory.com/551?category=758267](https://coding-factory.tistory.com/551?category=758267))
174 changes: 174 additions & 0 deletions java basics/Diff Collection.forEach, Stream.forEach.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# Diff Collection.forEach, Stream.forEach
아래처럼 Collection.forEach메소드로 반복할 때와 Stream.forEach메소드로 반볼할 때는 무슨 차이가 있을까?

```java
public void print(List<Integer> nums) {
nums.forEach(System.out::println);
nums.stream()
.forEach(System.out::println);
}
```

사실 대부분의 경우 별 차이는 없고, 작은 차이가 있다.

## Stream 객체 사용 여부

```java
public void print(List<Integer> nums) {
nums.forEach(System.out::println);
nums.stream()
.forEach(System.out::println);
}
```

Collection.forEach는 따로 객체를 생성하지 않고 forEach 메소드를 호출한다. forEach메소드는 Iterable인터페이스의 default 메소드인데, Collection 인터페이스에서 Iterable 인터페이스를 상속하고 있기에 바로 호출할 수 있는 것이다.

```java
public interface Iterable<T> {
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t); }
}
...
}

public interface Collection<E> extends Iterable<E> {
...
}

```

반면에 Stream.forEach는 Collection 인터페이스의 default 메소드 stream() 으로 Stream 객체를 생성해야만 forEach를 호출할 수 있다.

```java
public interface Collection<E> extends Iterable<E> {
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
}
```

위처럼 단순 반복이 목적이라면 Stream.forEach는 stream()으로 생성된 Stream객체가 버려지는 오버헤드가 있기에, filter, map등의 Stream 기능들과 함께 사용할 때만 Stream.forEach를 사용하고 나머지의 경우엔 Collection.forEach를 쓰는 것이 좋아 보인다.

- 참고로 Stream의 foreach와 for-loop는 다르다.

## parallelStream

stream 메소드로 생성한 Stream.forEach를 했을 땐 Collection.forEach와 별 차이가 없었지만 Collection 인터페이스의 또 다른 Stream 객체 생성 메소드 parallelStream()을 사용해서 Stream.forEach를 한다면 그 차이점이 명확하다.

```java
public void print(){
List<Integer> nums = Arays.asList(1,2,3,4,5);
System.out.println("Collection.forEach 출력 시작");
nums.forEach(System.out::println);
System.out.println("Stream.forEach 출력 시작");
nums.parallelStream().
forEach(System.out::println);
}
```

```java
Collection.forEach 출력 시작
1
2
3
4
5
Stream.forEcah 출력 시작
3
4
1
5
2
```

parallelStream() 메소드로 생성한 Stream 객체는 여러 스레드에서 스트림을 실행하기 때문에 forEach를 했을 때 실행 순서가 매번 달라지며 예측 불가능하다.

반면에 Colleciton.forEach는 내부적으로 아래와 같이 실행되기 때문에 일정한 순서를 보장한다.

```java
default void forEach(Consumer<? super T> action {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
```

## 동시성 문제

Collection.forEach의 경우엔 수정을 감지한 즉시 ConcurrentModificationException을 던지며 프로그램을 멈춘다.

ConcurrentModificationExcption이란 한 오브젝트에 대해 허가되지 않은 변경이 동시적으로 이루어질 때 발생한다. 대표적으로 Collection이 반복되는 동안 Collection을 수정할 때 발생한다.

아래의 예제는 List의 element가 짝수이면 remove하는 Consumer를 forEach로 돌린 것이다.

```java
@Test
void test() {
List<Integer> nums = new ArayList<>(Arrays.asList(1,2,3,4,5,6));
Consumer<Integer> remo eIfEven = num -> {
System.out.println(num);
if (num % 2 == 0) {
nums.remove(num);
}
};
assertThatThrownBy(() -> nums.forEach(removeIfEven))
.isInstanceOf(ConcurrentModificationException.class);
}
```

위의 예제는 List의 첫 번째 짝수 2가 지워지면 바로 ConcurrentModificationException이 발생한다.

아래는 Stream.forEach의 경우이다.

```java
@Test
void test() {
List<Integer> nums = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6));
Consumer<Integer> removeIfEven = num -> {
System.out.println(num);
if (num % 2 == 0) {
nums.remove(num);
}
};
assertThatThrownBy(() -> nums.stream().forEach(removeIfEven))
.isInstanceOf(NullPointerException.class);
}

```

Collection.forEach처럼 Collection이 수정되자마자 예외를 던지는 것이 아니라 무조건 리스트를 끝까지 돌고 예외를 던진다. 또 던지는 예외가 ConcurrentModificatonException이 아니라 NullPointerException이라는 차이점이 있다.

Collectioni.forEach는 일반적으로 해당 컬렉션의 Iterator를 사용하고 Stream.forEach는 해당 컬렉션의 spliterator를 사용한다.

Collections.java에서 보면 아래의 코드처럼 Collection.forEach에는 synchronized 키워드가 붙어있고 Stream.forEach를 위해 필요한 spliterator메소드는 안 붙어있는 것을 확인할 수 있다.

```java
@Override
public void forEach(Consumer<? super E> consumer) {
synchronized (mutex) {
c.forEach(consumer);
}
}
@Override
public Spliterator<E> spliterator() {
return c.spliterator(); // Must be manually synched by user!
}

```

즉, collection.forEach는 락이 걸려있어 멀티쓰레드에서 더 안전하다. 반면에 Stream.forEach는 반복 도중에 다른 쓰레드에 의해 수정될 수 있고, 무조건 요소의 끝까지 반복을 돌게된다. 이 과정에서 일관성 없는 동작이 발생하고 예상치 못한 에러가 발생할 확률이 높다.

## 결론

결국 반복을 위해 존재하는 Collection.forEach와 Stream.forEach의 차이는 작다.

Stream.forEach는 Stream의 컨셉에 맞게 병렬 프로그래밍에 특화된 반복을 위해 존재한다.

일반적인 반복의 경우엔 thread-safe한 Collection.forEach를 쓰는 게 좋아보인다.

# Reference

- [Velog. Collectio.forEach와 Stream.forEach는 뭐가 다를까?]([https://dundung.tistory.com/247](https://dundung.tistory.com/247))
Loading