[Java] Stream 이란?

Stream 이란?

StreamJava8 부터 지원하며, 컬렉션과 배열 등에 저장 된 요소들을 하나씩 참조하며 반복적인 처리를 가능하게 하는 기능이다.

-> Stream 을 사용하면 우리가 for 문을 이용해 처리하던 걸 간단하고 클린하게 작성 할 수 있게 된다.


JAVA8 에서는 아래와 같이 요악하고 있다.

  • 선언형
    • 간결하고 가독성이 좋아진다
  • 조립할 수 있다.
    • 유연성이 좋아진다.
  • 병렬화
    • 성능이 좋아진다.


사용방법

1
2
3
4
5
6
7
8
LinkedList<Integer> dataList = new LinkedList<Integer>() {
	{
		Random random = new Random();
		for(int i=0; i<100; i++) {
			add(random.nextInt(100));
		}
	}
};

기존코드

위와 같은 데이터가 있을 때 우리가 기존에 사용하던 방법은 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// dataList 의 값을 정렬하고
Collections.sort(dataList, new Comparator<Integer>() {
  @Override    
  public int compare(Integer arg0, Integer arg1) {
    return arg1.compareTo(arg0);    
  }
}); 

// 반복을 돌면서 가장 큰 7의 배수 4개를 출력한다.
for (int i=0, count=0, size=dataList.size(); i<size && count<4; ++i) {
  if (dataList.get(i) % 7 == 0) {
    System.out.println(dataList.get(i));        
    ++count;
  }
};

Stream 사용

1
2
3
4
5
dataList.stream()
  .filter((a) -> a % 7 == 0)  // 중간연산 - 7의 배수인 요소만 필터링 
  .sorted((a, b) -> b.compareTo(a)) // 중간연산 - 정렬
  .limit(4) // 중간연산 - 4개만 찾기
  .forEach(System.out::println); // 최종연산 - 출력

보기 쉽게 줄바꿈을 했지만, 라인수가 무척이나 줄어들게 된다. 만족스럽다.

이와 같이 Stream 을 이용하면 단순히 비지니스 처리를 선언으로 하기 때문에 쉽게 구현할 수 있으며, 각각의 블럭 (sorted나 filter) 등이 chain 형식을 지원하기 때문에 복잡한 로직에 대해 가독성과 명확성을 확실시 해줄 수 있을 것같다. (즉 파이프라인 구조이다.)

또한 내부적으로는 데이터 처리과정을 병렬화할 수 있기 때문에 성능을 더 좋게 만들 수 있다. (물론 병렬을 위한 thread 처리에 대해서는 신경쓰지 않아도 된다.)


샘플코드

List 에서 데이터 꺼내기

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
// List<String> 에서 String 타입 데이터 꺼내기
public static void main(String[] args) {
	ArrayList<String> list = new ArrayList<>(Arrays.asList("Apple","Banana","Melon","Grape","Strawberry"));
	
	System.out.println(list.stream().filter(t->t.length()>5).collect(Collectors.joining(" "))); //Banana Strawberry

	System.out.println(list.stream().filter(t->t.length()>5).collect(Collectors.toList())); //[Banana, Strawberry]
}


// List<Map<String, Object>> 에서 첫번째 Map 꺼내기
Optional<Map<String, Object>> tempMap = StreamSupport.stream(tempList.spliterator(), false)
	.map(map -> map)
	.filter(map -> !map.isEmpty())
	.filter(map -> map.get("tempKey") != null)
	.findFirst();


// List<Map<String, Object>> 에서 특정 value와 일치하는 map 만 꺼내기
private Map<String, Object> getMap(List<Map<String, Object>> tempList, String tempValue) {
	return StreamSupport.stream(tempList.spliterator(), false)
		.map(tempMap -> tempMap)
		.filter(tempMap -> !tempMap.isEmpty())
		.filter(tempMap -> !tempMap.get("temp_key").toString().isBlank())
		.filter(tempMap -> tempMap.get("temp_key").toString().equals(tempValue))
		.findFirst()
		.orElseThrow(() -> new NullPointerException("NPE"));
}	


// List<Map<String, Object>> 에서 특정 Key 의 Value 를 모두 꺼내기
private String getValueToString(List<Map<String, Object>> tempList, String key) {
	return StreamSupport.stream(tempList.spliterator(), false)
		.map(map -> map)
		.filter(map -> !map.isEmpty())
		.filter(map -> !map.get(key).toString().isBlank())
		.map(map -> map.get(key).toString())
		.collect(Collectors.joining("연결문자"));
}

String arrayValue = getKeyToString(tempList, key);
List<String> seatsNumber = Arrays.asList(arrayValue.split("자를문자"));


Map 에서 데이터 꺼내기

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
// key 를 이용한 value 꺼내기
public static void main(String[] args) {
	Map<String, Integer> books = new HashMap<>();
	books.put("JSP 프로그래밍", 50);
	books.put("자바 프로그래밍", 100);
	books.put("Javascript 프로그래밍", 0);
	
	// value 값을 이용 한 key 추출
	// value 가 0 인 데이터 추출
	Optional<String> optBooks = books.entrySet().stream()
			.filter(e -> e.getValue() == 0)
			.map(Map.Entry::getKey)
			.findFirst();

	// assertEquals("Javascript 프로그래밍", optBooks.get());
	System.out.println("optBooks.get() : "+optBooks.get());
	
	// key 값을 이용한 value 추출
	Optional<Integer> stock = books.entrySet().stream()
			.filter(e -> e.getKey().startsWith("JSP"))
			.map(Map.Entry::getValue)
			.findFirst();
	System.out.println("stock.get() : "+stock.get());
	// assertEquals(50, stock.get());
}



// Map 에서 String 꺼내기
private String getEntryValueToString(Map<String, Object> entry, String key) {
	return entry.entrySet().stream()
		.filter(e -> !entry.isEmpty())
		.filter(e -> entry.get(key) != null)
		.filter(e -> e.getKey().equals(key))
		.map(e -> e.getValue().toString())
		.findFirst()
		.orElseThrow(() -> new NullPointerException("NPE"));
}


// 특정 value 만 변환하기
private Map<String, Object> conversionValue(Map<String, Object> tempMap) {
	return tempMap.entrySet().stream()
		.filter(e -> !tempMap.isEmpty())
		.filter(e -> tempMap.get("tempKey") != null)
		.map(e -> {
			if(e.getKey().equalsIgnoreCase("tempKey")) { 
				e.setValue("변경할 값");
			}
			return e;
		})
		.collect(HashMap::new, (map, entry) -> map.put(entry.getKey(), entry.getValue()), HashMap::putAll);
}


List<Map<String,Object» 정렬하기

1
2
3
4
5
6
// createDate 의 값을 비교하여 정렬 
List<Map<String, Object>> list =  tempList.stream()
	.sorted((currentMap, nextMap) -> 
		(currentMap.get("createDate"))
		.compareTo(nextMap.get("createDate"))
	).collect(Collectors.toList());


List 중복 데이터 제거하기

1
2
3
4
List<String> beforeList = new ArrayList<>(Arrays.asList("Apple","Banana","Melon","Grape", "Apple", "Strawberry"));
System.out.println("before : "+beforeList);
List<String> afterList = beforeList.stream().distinct().collect(Collectors.toList());
System.out.println("after : "+afterList);


Leave a comment