for 루프 대신“기능적 작업”을 사용해야하는 이유는 무엇입니까?


39
for (Canvas canvas : list) {
}

NetBeans는 "기능적 조작"을 사용하도록 제안합니다.

list.stream().forEach((canvas) -> {
});

그러나 이것이 왜 선호 됩니까? 무엇이든 읽고 이해하기가 더 어렵습니다. 을 호출 stream()한 다음 forEach()매개 변수와 함께 람다 식 을 사용합니다 canvas. for첫 번째 스 니펫 의 루프 보다 더 좋은 방법은 없습니다 .

분명히 나는 ​​미학에 대해서만 말하고 있습니다. 어쩌면 여기에 내가 놓친 기술적 이점이 있습니다. 무엇입니까? 두 번째 방법을 대신 사용해야하는 이유는 무엇입니까?



15
특정 예에서는 바람직하지 않습니다.
Robert Harvey

1
유일한 작전이 하나의 일이라면, 나는 당신에게 동의하는 경향이 있습니다. 파이프 라인에 다른 작업을 추가하거나 출력 시퀀스를 생성하자마자 스트림 접근 방식이 선호됩니다.
JacquesB

@RobertHarvey 그렇지 않습니까? 왜 안돼?
sara

@RobertHarvey 잘 받아 들인 대답이 실제로 더 복잡한 경우에 for-version이 물에서 어떻게 날아 갔는지에 동의하지만 사소한 경우에는 "승리"가되는 이유를 알지 못합니다. 당신은 그것을 자명 한 것처럼 진술하지만 그것을 보지 못합니다. 그래서 물었습니다.
sara

답변:


45

스트림은 수집되는 데이터 또는 데이터 스트림 위에서 수행하려는 다양한 작업의 구성에 대해 훨씬 더 나은 추상화를 제공합니다. 특히 요소를 매핑하고 필터링하고 변환해야하는 경우.

당신의 예는 그리 실용적이지 않습니다. Oracle 사이트의 다음 코드를 고려 하십시오 .

List<Transaction> groceryTransactions = new Arraylist<>();
for(Transaction t: transactions){
  if(t.getType() == Transaction.GROCERY){
    groceryTransactions.add(t);
  }
}
Collections.sort(groceryTransactions, new Comparator(){
  public int compare(Transaction t1, Transaction t2){
    return t2.getValue().compareTo(t1.getValue());
  }
});
List<Integer> transactionIds = new ArrayList<>();
for(Transaction t: groceryTransactions){
  transactionsIds.add(t.getId());
}

스트림을 사용하여 작성할 수 있습니다.

List<Integer> transactionsIds = 
    transactions.stream()
                .filter(t -> t.getType() == Transaction.GROCERY)
                .sorted(comparing(Transaction::getValue).reversed())
                .map(Transaction::getId)
                .collect(toList());

두 번째 옵션은 훨씬 더 읽기 쉽습니다. 따라서 부분 처리를 수행하는 중첩 루프 또는 다양한 루프가있는 경우 Streams / Lambda API 사용에 매우 적합합니다.


5
맵 퓨전 ( stream.map(f).map(g)stream.map(f.andThen(g))) 빌드 / 리 듀스 퓨전 (한 방법으로 스트림을 빌드 한 다음이를 소비하는 다른 방법으로 전달할 때, 컴파일러는 스트림을 제거 할 수 있음) 및 스트림 퓨전 (많은 스트림 연산을 통합 할 수있는 )과 같은 최적화 기법 이 있습니다. 스트림 연산을 훨씬 효율적으로 만들 수있는 단일 명령 루프로 결합). 그것들은 GHC Haskell 컴파일러와 다른 Haskell 및 다른 기능적 언어 컴파일러에서 구현되며 스칼라에 대한 실험적 연구 구현이 있습니다.
Jörg W Mittag

Netbeans가 최적의 경로가 될 수 있다고 판단되면 컴파일러가 for 루프에서 함수 연산으로 변환 할 수 있어야하기 때문에 기능 대 루프를 고려할 때 성능은 아마도 요인이 아닙니다.
Ryan

두 번째가 더 읽기 쉽다는 것에 동의하지 않습니다. 무슨 일이 일어나고 있는지 알아내는 데 시간이 걸립니다. 다른 방법으로는 보이지 않기 때문에 두 번째 방법으로 수행하면 성능상의 이점이 있습니까?
Bok McDonagh

@BokMcDonagh, Me 경험은 새로운 추상화에 익숙해지기를 귀찮게하지 않은 개발자에게는 읽기 쉽지 않다는 것입니다. 미래이기 때문에 더 친숙해지기 위해 그러한 API를 더 많이 사용하는 것이 좋습니다. Java 세계에서만이 아닙니다.
luboskrnac

16

기능적 스트리밍 API를 사용하면 얻을 수있는 또 다른 이점은 구현 세부 정보를 숨길 수 있다는 것입니다. 방법이 아니라 수행해야 할 사항 만 설명합니다. 단일 스레드에서 병렬 코드 실행으로 변경하기 위해 수행해야 할 변경을 볼 때 이러한 이점이 분명해집니다. 그냥 변경 .stream().parallelStream().


13

무엇이든 읽고 이해하기가 더 어렵습니다.

그것은 매우 주관적입니다. 두 번째 버전을 읽고 이해하기가 훨씬 쉽습니다. 다른 언어 (예 : Ruby, Smalltalk, Clojure, Io, Ioke, Seph) 가하는 방식과 일치하며 이해해야 할 개념이 더 적습니다 (첫 번째 예는 특수 구문이지만 다른 방법과 마찬가지로 일반적인 메소드 호출 일뿐입니다).

어쨌든 그것은 친숙한 문제입니다.


6
맞아 사실이야. 그러나 이것은 나에 대한 대답보다 주석처럼 보입니다.
오메가

funtion 작업은 특수 구문도 사용합니다. "->"는 새로운 기능
Ryan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.