함수형 프로그래밍
함수의 구현과 호출만으로 프로그래밍이 수행되는 방식
순수함수(pure function)를 구현하고 호출함으로써, 외부 자료에 side effect를 주지 않도록 구현
순수함수 : 매개변수만을 사용하여 만드는 함수
→ 따라서 함수 외부에 있는 변수를 사용하지 않아 외부에 영향을 주지 않음
함수를 처리할 때 외부 자료를 사용하지 않아서 외부에 영향을 미치지 않기 때문에 여러 자료가 동시에 수행되는 병렬처리가 가능하다.
1. 람다식
- 익명 함수
- 매개변수와 매개변수를 이용한 실행문을 이루어진다.
- (매개변수) -> { 실행문 };
람다식의 장점
- 코드가 간결해짐
- 코드 가독성이 높아짐
- 생산성이 높아짐
람다식의 단점
- 코드 재사용 불가
- 디버깅이 어려움
람다식 표현 규칙
메서드 이용
int add(int x, int y){
return x+y;
}
람다식으로 변환
(int x, int y) -> {return x+y;}
1. 매개변수가 하나인 경우
자료형과 괄호 생략 가능
str -> {System.out.println(str);}
매개변수가 두개 이상인 경우 괄호를 생략할 수 없음
(x,y) -> {System.out.println(x+y);}
x,y -> {System.out.println(x+y);} // 오류
2. 실행문이 한 문장인 경우
중괄호 생략 가능
str -> System.out.println(str);
실행문이 한 문장이라도 반환문은 중괄호 생략 불가능
str -> return str.length(); // 오류
실행문이 한문장인 반환문의 경우 return과 중괄호를 모두 생략
str -> str.length();
2. 함수형 인터페이스
- 람다식을 선언하기 위해 만드는 인터페이스
- 인터페이스 내에 메서드는 하나만 선언해야 함
- 이를 방지하기 위해 @FunctionalInterface 애노테이션 사용
@FunctionalInterface
public interface Calc {
public int calc(int x, int y);
}
해당 인터페이스의 메서드를 오버라이딩 하여 사용하거나, 람다식으로 선언한 뒤 사용할 수 있다.
@override
Calc c1 = new Calc() {
@Override
public int calc(int x, int y) {
return x+y;
}
};
System.out.println(c1.calc(1,2));
람다식으로 선언
Calc multiple = (x,y) -> x*y;
System.out.println(multiple.calc(1,2));
Calc getMax = (x,y) -> (x>y)?x:y;
System.out.println(getMax.calc(1,2));
3. 스트림(stream)
- Stream은 배열, 컬렉션 등을 대상으로 데이터를 하나씩 참조하여 연산을 수행한다.
- for문의 사용을 줄여 코드를 간결하게 한다.
배열 스트림
Arrays.stream(arr)
String[] arr = new String[]{"hello", "world", "!!"};
Stream stream = Arrays.stream(arr);
컬렉션 스트림
list.stream()
List<Integer> list = new ArrayList<>(Arrays.asList(1,2,3));
Stream stream = list.stream();
중간연산 / 최종연산
스트림은 중간연산과 최종연산을 구분된다.
- 중간연산 : filter(), map(), sorted() 등 조건에 따라 요소를 추출하거나 변환한다.
- 최종연산 : 결과를 반환한다.
중간연산
forEach()
요소들을 반복 처리한다.
int[] arr = {1,2,3,4,5};
List<Integer> list = new LinkedList<>();
Arrays.stream(arr).forEach(n -> list.add(n*10));
System.out.println(list.toString()); //[10, 20, 30, 40, 50]
filter()
조건에 맞는 요소들을 추출한다.
IntStream intStream = IntStream.range(1, 10).filter(n -> n % 2 == 0 && n>3);
예제
List<Integer> list = new LinkedList<>(Arrays.asList(1,2,3,4,5));
List<Integer> copyList = new LinkedList<>();
list.stream().filter(n -> n>3)
.forEach(n-> copyList.add(n));
System.out.println(copyList.toString()); // [4, 5]
map()
각각의 요소들을 변환한다.
IntStream intStream = IntStream.range(1, 10).map(n -> n+1);
예제
List<Member> list = new LinkedList<>(
Arrays.asList(new Member("m1",10), new Member("m2", 15),
new Member("m3", 25), new Member("m4", 30)));
List<Integer> ageList = new LinkedList<>();
list.stream().map(member -> member.name).forEach(name -> System.out.print(name+" "));
// m1 m2 m3 m4
list.stream().map(member -> member.age).filter(age -> age<100).forEach(age -> ageList.add(age));
// ageList : [ 10,15,25,30 ]
class Member{
String name;
int age;
public Member(String name, int age) {
this.name = name;
this.age = age;
}
}
sorted()
요소들을 정렬한다.
List<Integer> list = new LinkedList<>(Arrays.asList(5,2,3,4,1));
list.stream().sorted().forEach(n -> System.out.print(n+" "));
//1 2 3 4 5
최종연산
sum(), average()
List<Integer> list = new LinkedList<>(Arrays.asList(1,2,3,4,5));
int sum = list.stream().mapToInt(Integer::intValue).sum(); //15
double avg = list.stream().mapToInt(Integer::intValue).average().getAsDouble();//3.0
int sum2 = IntStream.range(1, 5).sum(); //10, 1부터 4까지
double avg2 = IntStream.range(1, 5).average().getAsDouble(); //2.5, 1부터 4까지
max(), min()
List<Integer> list = new LinkedList<>(Arrays.asList(1,2,3,4,5));
int max = list.stream().mapToInt(Integer::intValue).max().getAsInt(); //5
int min = list.stream().mapToInt(Integer::intValue).min().getAsInt(); //1
IntStream.range(1,6).max().getAsInt(); // 5
IntStream.range(1,6).min().getAsInt(); // 1
reduce()
앞서 계산한 결과에 이어서 계산한다.
(reduce는 Optional로 반환함)
List<Integer> listRD = new LinkedList<>(Arrays.asList(1,2,3,4,5));
System.out.println(listRD.stream().reduce((x,y) -> x*y).get());
//120
스트림 직접 사용
builder()
Stream builder = Stream.builder().add(1).add(2).add(3).build();
builder.forEach(n-> System.out.println(n));
1
2
3
generate()
Stream gen = Stream.generate(()->"hello").limit(3); //limit : 반복 횟수
gen.forEach(System.out::println);
hello
hello
hello
iterate()
iterate(초기값, 람다식) 으로 구성
초기값부터 시작하여 람다식에 따라 연산한다.
Stream iter = Stream.iterate(10, n->n*2).limit(5); //limit : 반복 횟수
iter.forEach(System.out::println);
10
20
40
80
160
반응형
'JAVA & Spring' 카테고리의 다른 글
[JAVA] Math 클래스 (0) | 2023.06.02 |
---|---|
[JAVA] format() 메서드 : 문자열 포맷 지정 / printf() (0) | 2023.06.01 |
[JAVA] 파일 입출력 (2) | 2023.05.19 |
[Java] StringTokenizer (0) | 2023.05.04 |
[Java] Optional<T> 클래스 (0) | 2023.05.03 |