개발 메모장

[Java] Stream API(2) - 스트림 데이터 가공 본문

Java

[Java] Stream API(2) - 스트림 데이터 가공

yyyyMMdd 2023. 12. 13. 16:02
728x90

#. 생성한 스트림으로 처리가 가능하다면 사용하지 않겠지만 데이터를 가공해 처리해야 하는 경우 사용하는 메서드들에 대해 알아보려 합니다.

 

#. 아래 작성한 내용 외에도 많은 메서드가 존재하니 일부 메서드에 대한 내용만 간단히 알아보도록 하겠습니다.

 


  • 처리 방법에 따른 메서드 설정

    - 병렬 처리 시 parallel()을 추가해 주면 되며, 이를 확인하는 방법은 Boolean형의 isParallel()로 확인 가능합니다.

List<String> mmyList = myList.stream().parallel().toList();
Stream<String> tream = myList.parallelStream();

 

    - 순차 처리 시 sequential()을 추가해 주면 됩니다.

Boolean parallelChk = myList.stream().parallel().isParallel();  // true
Stream<String> tream = myList.parallelStream().sequential();
// isParallel() -> false

 



    - 병렬 처리 시 유의 사항

    1. 스레드 안전성 고려
        - 데이터를 추가하거나 삭제 등 불일치로 이어질 수 있는 상태 작업이나 변경 가능한 고유 데이터 작업은 피하는 것이 좋습니다.

    2. 성능 확인
        - 병렬 처리는 계산 처리에는 유용할 수 있지만 항상 성능이 좋진 않으므로 유의해야 합니다.

        - 데이터 세트가 작거나 계산 집약도가 낮은 작업은 병렬화 오버헤드가 발생할 수 있습니다.

    3. 순서 유지 확인
        - forEachOrdered(), forEach() 메서드와 같이 순차처리하는 작업에선 동시 실행으로 인한 데이터 순서가 유지되지 못하는 경우가 있을 수 있습니다.

 


  • 아래 메서드들의 사용을 위한 리스트 스트림 객체 생성
List<String> dataList = Stream.of("water","123", "earth", "air", "fire", "fry", "").toList();

 


  • map

    - 값들을 map에서 요청한 방법에 맞게 값을 변환해 주는 기능입니다.

    - 인자는 람다가 들어가며 아래는 String 클래스의 메서드를 사용하여 각각의 값들을 변환시켜줍니다.
List<String> mappedDataList = dataList.stream().map(String::toUpperCase).toList();
// [WATER, 123, EARTH, AIR, FIRE, FRY, ]

 

    - 직접 생성한 클래스와 메서드도 사용 가능합니다.

class StringTransformer {
    public String transformString(String str) {
        return str.toUpperCase();
    }
}

public void testStream() {
    StringTransformer transformer = new StringTransformer();
    List<String> stringStream = Stream.of("a", "b", "c");
    Stream<String> transformedStream = stringStream.map(transformer::transformString);
}

 


  • flatMap

    - 플랫맵을 사용해 여러 구조를 하나로 만들 듯 평면화한다고 생각하면 좋을 것 같습니다.

    - 이중 리스트에서 리스트로 각각의 값을 꺼내어 만드는 것보다 더 쉽게 처리가 가능하게 됩니다.
// 객체 생성
List<List<String>> listOflists = Arrays.asList(
						Arrays.asList("apple", "banana"),
                				Arrays.asList("orange", "grape"),
                				Arrays.asList("watermelon", "pineapple")
        					);
// [[apple, banana], [orange, grape], [watermelon, pineapple]]

// flatMap 처리
List<String> flattenedStream = listOflists.stream()
				.flatMap(List::stream).toList();
// [apple, banana, orange, grape, watermelon, pineapple]

 


  • filter

    - 불필요한 값이나 필요한 값을 찾기 위해 필터링해주는 메서드입니다.
List<String> filteredDataList = dataList.stream()
				.filter(a -> a.endsWith("e"))
				.toList();
// [fire]

// 이 밖에도 사용가능한 기능이 많음
// contains("e") -> e가 포함된 데이터만 호출
// contentEquals("earth") -> earth와 같은 데이터만 호출(equals()도 같은 역할이나 객체 대상) 
// isBlank() -> 빈 문자열 또는 공백 문자열을 필터링
// isEmpty() -> 빈 문자열을 필터링
// matches("\\d+") -> 정규식을 넣어 정규식에 맞는 데이터만 필터링

List<String> test = dataList.stream()
	    	    .filter(a -> a.regionMatches(0, "fire", 0, 4))
            	    .toList(); 
// [fire]		
// regionMatches(1, 2, 3, 4)
// 1. 람다식 a의 인덱스
// 2. 비교문자열 
// 3. 2의 시작 인덱스 
// 4. 1과2에 적용할 길이

 


  • sorted

    - 정렬 처리를 위한 메서드이며 기본 값은 ASC입니다.

    - DESC의 경우 Comparator.reverseOrder()를 이용해 처리해야 합니다.
List<String> sortedDataList = dataList.stream()
			      .sorted()
			      .toList();
// [, 123, air, earth, fire, fry, water]

List<String> sortedDataListDesc = dataList.stream()
			          .sorted(Comparator.reverseOrder())
			          .toList();
// [water, fry, fire, earth, air, 123, ]

 


  • distinct

    - 스트림 객체 또는 리스트 등 컬렉션에서 중복된 값이 있을 경우 이를 제거하고 나머지 값을 리턴합니다.

    - 기존 스트림 객체와 새 스트림 객체를 concat으로 합친 데이터로 테스트하였습니다.
List<String> addDataList = Stream.of("cloud", "rain", "electric", "water", "earth", "fire").toList();

List<String> concatDataList = Stream.concat(dataList.stream(), addDataList.stream()).toList();
// [water, 123, earth, air, fire, fry, , cloud, rain, electric, water, earth, fire]

List<String> distinctConcatDataList = Stream.concat(dataList.stream(), addDataList.stream()).distinct().toList();
// [water, 123, earth, air, fire, fry, , cloud, rain, electric]

 


  • skip

    - 인자 값으로 넣은 숫자 이전은 스킵하고 그 숫자 자리부터 데이터를 리턴합니다.
List<String> skipDataList = dataList.stream().skip(5).toList();
// [fry, ]

 


  • peek

    - 기존 스트림 객체의 값을 로그에 찍어보거나 새로운 객체에 복사할 때 반복처리 됩니다.

    - 결과에서 볼 수 있듯 peek -> map 순으로 처리되는 것이 아닌 peek이 전부 처리된 후 map이 처리되게 됩니다.
List<Integer> testList = new ArrayList<>();

List<Integer> intList = Stream.of(1,3,4,5,6).toList();
List<Integer> peekDataList = intList.stream().peek(testList::add).map(n -> n+2).toList();

System.out.println(testList);
// [1, 3, 4, 5, 6]
System.out.println(peekDataList);
// [3, 5, 6, 7, 8]

 


#. 결과를 도출하기 전 데이터를 가공하여 원하는 값을 도출하기까지 전처리하는 과정으로 손에 익으면 비교적 쉽게 처리가 가능할 것 같습니다.

 

 

===========================================================
틀린 내용이 있거나 이견 있으시면 언제든 가감 없이 말씀 부탁드립니다!

===========================================================

728x90