함수형 프로그래밍은 대입문을 사용하지 않는 프로그래밍이며, 작은 문제를 해결하기 위한 함수를 작성합니다.
Stream API는 데이터를 추상화하고, 처리하는데 자주 사용되는 함수들을 정의해두었다. 여기서 데이터를 추상화하였다는 것은 데이터의 종류에 상관 없이 같은 방식으로 데이터를 처리할 수 있다는 것을 의미하며, 그에 따라 재사용성을 높일 수 있다.
예를 들어 배열 요소의 특정 기준에 따라 정렬(sorting)하거나,요소 중 특정 값은 제외하고 출력하는(filter)기능이 있습니다. 이렇게 여러 자료의 처리에 대한 기능을 구현해 놓은 클래스가 스트림(stream)입니다.
스트림을 활용하면 배열,컬렉션 등의 자료를 일관성 있게 처리할 수 있습니다.
자료에 따라 기능을 각각 새로 구현하는 것이 아니라 처리해야 하는 자료가 무엇인지와 상관없이 같은 방식으로 메서드를 호출할 수 있기 때문입니다.(다른말로는 '자료를 추상화했다'라고 표현합니다.)
배열을 예로 들어보겠습니다.
(정수 5개의 요소로 가진 배열이고, 이를 모두 출력하는 출력문입니다.)
int[] arr = {1,2,3,4,5};
for(int i =0; i<arr.length; i++){
System.out.println(arr[i]);
}
int[]arr ={1,2,3,4,5};
Arrays.stream(arr).foreach(n->System.out.println(n));
스트림 연산의 종류에는 크게 중간 연산과 최종 연산 두가지가 있습니다.
- 중간연산 : 자료를 거르거나 변경하여 또 다른 자료를 내부적으로 생산합니다.
- 중간연산 : 생성된 내부 자료를 소모해 가면서 연산을 수행합니다.
(따라서 최종 연산은 마지막에 한 번만 호출됩니다.)- 그리고 최종연산이 호출되어야 중간 연산의 결과가 만들어집니다.
문자열 배열이 있을 때 문자열의 길이가 5이상인 경우만 출력하는 코드는 다음과 같습니다.
sList.stream().filter(s -> s.length() >= 5).foreach(s -> System.out.println(s));
예를 들어 고객 클래스가 있다면 고객 이름만 가져와서 출력할 수 있습니다.map()은 요소들을 순회하여 다른 형식으로 변하기도 합니다.
customerList.stream().map(c -> c.getName()).forEach(s -> System.out.println(s));
-filter(),map() 둘 다 함수를 수행하면서 해당 조건이나 함수에 맞는 결과를 추출해 내는 중간 역할을 합니다.
최종 연산은 스트림의 자료를 소모하면서 연산을 수행하기 때문에 최종 연산이 수행되고 나면 해당 스트림은 더 이상 사용할 수 없습니다.
정수 배열에 스트림 생성하고 사용하기
import java.util.Arrays;
public class IntArrayTest {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
int sumVal = Arrays.stream(arr).sum();
int count = (int)Arrays.stream(arr).count();
//count()메서드의 반환 값이 Long이므로 int형으로 변환시켜주어야 합니다.
double average = Arrays.stream(arr).average().getAsDouble();
//average()의 리턴값 : OptionalDouble
//getAsDouble()은 OptionalDouble타입을 Double타입으로 바꿔준다.
int max = Arrays.stream(arr).max().getAsInt();
//max()의 리턴값 : OptionalInt
int min = Arrays.stream(arr).min().getAsInt();
System.out.println("총합: "+sumVal);
System.out.println("수 :"+count);
System.out.println("평균 : "+average);
System.out.println("최고값"+max);
System.out.println("최소값"+min);
}
}
Collection 인터페이스를 구현한 클래스 중 가장 많이 사용하는 ArrayList에 스크림을 생성하고 활용해 보겠습니다.
List<String>sList = new ArrayList<>();
sList.add("Tomas");
sList.add("Edward");
sList.add("Jack");
메서드 | 설명 |
---|---|
Stream<E> stream() | 스크림 클래스를 반환합니다. |
Stream<String> stream = sList.stream();
Stream<String>stream = sList.stream();
stream.forEach(s -> System.out.println(s));
이렇게 forEach()메서드는 내부적으로 반복문이 수행됩니다. 그럼 forEach()괄호 안에 구현되는 람다식의 의미를 알아봅시다.
forEach() 메서드가 수행되면 요소가 하나씩 차례로 변수 s에 대입되고 이를 매개변수로 받아 출력문이 호출됩니다.
이번에는 ArrayList에 저장된 이름을 정렬하여 그 결과를 출력해 봅니다.
앞에서 stream변수에 스트림을 생성했지만 forEach()메서드가 수행되면서 자료가 소모되었습니다. 따라서 스트림을 새로 생성해야 합니다.
Stream<String> stream2 = sList.stream()
stream2.sorted().forEach(s -> System.out.println(s));
지금까지의 코드를 정리한 예제입니다.
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class ArrayListStreamTest {
public static void main(String[] args) {
List<String> sList = new ArrayList<>();
sList.add("Tomas");
sList.add("Edward");
sList.add("Jack");
Stream<String> stream = sList.stream();
stream.forEach(s -> System.out.println(s+" "));
System.out.println();
sList.stream().sorted().forEach(s -> System.out.println(s));
}
}
스트림을 활용하여 여행객의 여행 비용 계산하기 예제
package 스트림And람다식;
public class TravelCustomer {
private String name; //고객이름
private int age; //나이
private int price; //가격
public TravelCustomer(String name, int age, int price) {
this.name = name;
this.age = age;
this.price = price;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public int getPrice() {
return price;
}
@Override
public String toString() {
return "name: "+name+"age: "+age+"price: "+price;
}
}
import java.util.ArrayList;
import java.util.List;
public class TravelTest {
public static void main(String[] args) {
//고객생성
TravelCustomer customerLee = new TravelCustomer("이순신",40,100);
TravelCustomer customerKim = new TravelCustomer("김유신",20,100);
TravelCustomer customerHong = new TravelCustomer("홍길동",13,50);
List<TravelCustomer> customerList = new ArrayList<>();
//ArrayList에 고객 추가
customerList.add(customerLee);
customerList.add(customerKim);
customerList.add(customerHong);
System.out.println("==고객 명단 추가된 순서대로 출력==");
customerList.stream().map(c ->c.getName()).forEach(s -> System.out.println(s));
int total = customerList.stream().mapToInt(c ->c.getPrice()).sum();
System.out.println("총 여행 비용은: "+total+" 입니다.");
//여기서 mapToInt메서서드는 각 고객이 지불한 비용을 가져와서 정수로 변환후 sum()으로 합을 구합니다.
System.out.println("==20세 이상 고객 명단 정렬하여 출력 ==");
customerList.stream().filter(c ->c.getAge()>=20)
.map(c ->c.getName()).sorted().forEach(s -> System.out.println(s));
}
}
map은 요소들을 특정조건에 해당하는 값으로 변환해 줍니다.
-요소들을 대,소문자 변형 등 의 작업을 하고 싶을떄 사용 가능 합니다.
filter는 요소들을 조건에 따라 걸러내는 작업을 해줍니다.
-길이의 제한, 특정문자포함 등 의 작업을 하고 싶을때 사용 가능합니다.
sorted는 요소들을 정렬해주는 작업을 해줍니다.