Lamda의 우아함과 Stream의 주의사항

박근수·2024년 11월 27일
0

01. 익명 클래스보다는 람다를 사용하라

익명 함수를 람다로 변환

//익명 함수
List<String> words = List.of("사과", "배");
Collections.sort(words, new Comparator<>(){
	public int compare(String o1, String o2){
    	return Integer.compare(o1.legth(), o2.legth());
    }
});

//Lamda
Collections.sort(words, Comparator.compareingInt(s -> s.length()));
Collections.sort(words, Comparator.compareingInt(String::length));
word.sort(Comparator.comparingInt(String::length));

람다를 사용할 수 있는 자바 버전이면, 타입 추론 또한 가능하기 때문에 매개변수 타입을 가능하면 생략한다.
익명 함수는 이제 (함수형 인터페이스가 아닌) 타입의 인스턴스를 만들 때만 사용

02. 람다보다는 메서드 참조를 사용

항상 메서드 참조가 정답은 아님
//ide 권장
service.excute(GoshThisClassNameIsHumongous::action);

//람다 (훨씬 간결하다)
service.excute(() -> action);

03. 표준 함수형 인터페이스를 사용하라

@FuntaionalInterface

* @Override와 비슷
* 람다형으로 선언되었음을 문서로 알림
* 해당 인터페이스가 추상 메서드를 하나만 가지고 있어야 컴파일 가능
* 다른 의도로 사용되는 것을 막음

대표적인 기본 함수형 인터페이스

04. 스트림을 주의해서 사용하라

스트림을 사용해야 하지 말아야할 때

  • return (메서드 빠져나가기), break & continue (반복문 종료 or 특정 한 번 건너 뛰기) 같은 행위가 필요할 때
  • 스트림 내부에서 밖의 지역 변수를 수정해야할 때 (금지)
int a = 1;

List.of("사과", "배").stream().filter(str -> {
	if(str.equals("배")) a = 2;
    
	return true;
});    

스트림을 사용해야할 때

//원소의 시퀀스 일괄 변화
List<ItemInfo> -> List<String>
//시퀀스 필터링
.filter();
//시퀀스를 하나의 연산을 통해 결합할 때 (더하기, 연결, 최솟값 등)
mapToInt().sum();
//컬렉션 모으기
.collect(Collectors.toList());
//시퀀스에서 특정 조건을 만족하는 원소 찾기
.findFirst();

구현 차이

//반론 방식
public List<Integer> minFice(List<Integer> integerList){
	List<Integer> returnList = new ArrayList<>();
    List<Integer> copiedList = new ArrayList<>(integerList);
    Collections.sort(copiedList);
    int cnt = 0;
    
    for(int i = 0; i<copiedList.size(); i++){
    	if(i==5) break;
        
        returnList.add(copiedList.get(i));
    }
    return returnList;
}

//스트림 방식
public List<Integer> minFice(List<Integer> integerList){
	return integerList.stream().sorted().limit(5)
    .collect(Collectors.toList());
}

05. 스트림에서는 부작용 없는 함수를 사용하라

Strema은 순수 함수여야 한다.

순수함수란?

오직 입력만이 결과에 영향을 준다.
다른 가변 상태를 참조하지 않으며, 함수는 다른 상태를 변경시키지 않는다.
//Stream 코드를 사용했을 뿐, 함수형 코드도 아니고, 단순 반복문에 불과하다.
public static List<integer> integerSort(List<Integer> integerList){
	List<Integer> returnList = new ArrayList<>();
    integerList.stream().sorted().forEach(num -> {
    	returnList.add(num);
	});
    
    return returnList;
}

//올바른 코드
public static List<integer> integerSort(List<Integer> integerList){
	
    return integerList.stream().sorted().collect(Collector.toList());

toMap 예제

@Data
@AllArgsConstructor
class User{
	private Long id;
    private String name;
    private int age;
    private float height;
}

...
public static Map<Long, String> getHeightGroup(List<User> userList){
	return userList.stream().collect(Collectors.toMap(
    	User::getId, User::getName));
}

06. 반환 타입으로는 스트림과 컬렉션이 낫다.

Strema과 Iterable 사이의 어댑터

public static <E> Iterable<E> iterableOf(Stream<E> stream){
	return stream::iteratore;
}

public static <E> Stream<E> streamOf(Iterable<E> iterable){
	return StreamSupport.stream(iterable.spliterator(), flase);
}

//Stream pipeline에서만 사용한다 : response Stream
//반복문에서만 쓰일 걸 안다 : response Iterable
profile
개발블로그

0개의 댓글