자바의 다양한 기능 (2)

김주언·2022년 7월 13일

JAVA

목록 보기
8/10
post-thumbnail

2. 람다식(Lambda expression)

2.1 함수형 프로그래밍과 람다식

자바는 객체 지향 프로그래밍이기 때문에 기능을 수행하긴 위해서는 객체를 만들고 그 객체 내부에 멤버 변수를 선언하고 기능을 수행하는 메서드를 구현한다.

자바 8부터 함수형 프로그래밍 방식을 지원하고 이를 람다식이라 한다. 함수의 구현과 호출만으로 프로그래밍이 수행되는 방식

함수형 프로그래밍(Functional Programming: FP)

함수형 프로그래밍은 순수함수(pure function)를 구현하고 호출함으로써 외부 자료에 부수적인 영향(side effect)를 주지 않도록 구현하는 방식이다. 순수 함수란 매개변수만을 사용하여 만드는 함수. 즉, 함수 내부에서 함수 외부에 있는 변수를 사용하지 않아 함수가 수행되더라도 외부에는 영향을 주지 않는다.

함수기반 프로그래밍이고 입력받는 자료 이외에 외부 자료를 사용하지 않아 여려 자료가 동시에 수행되는 병렬처리가 가능하다.

함수형 프로그래밍은 함수의 기능이 자료에 독립적임을 보장한다. 이는 동일한 자료에 대해 동일한 결과를 보장하고, 다양한 자료에 대해 같은 기능을 수행할 수 있다는 의미

2.2 람다식 문법

자바스크립트 화살표 함수랑 비슷하다.

두 수를 입력 받아 더하는 add() 함수

int add(int x, int y){

    return x+y;
}

위 함수를 람다식으로 표현하면 아래와 같다.

(int x, int y) -> {return x+y;}
  • 매개 변수가 하나인 경우 자료형과 괄호 생략가능
  • 실행문이 한 문장인 경우 중괄호 생략 가능

  • 실행문이 한 문장이라도 return문(반환문)은 중괄호를 생략할 수 없음

  • 실행문이 한 문장의 반환문인 경우엔 return과 중괄호를 모두 생략



3. 스트림(Stream)

자료의 대상과 관계없이 동일한 연산을 수행하며 배열, 컬렉션을 대상으로 연산을 수행 함

  • 일관성 있는 연산으로 자료의 처리를 쉽고 간단하게 함

  • 자료 처리에 대한 추상화가 구현되었다고 함

  • 한번 생성하고 사용한 스트림은 재사용 할 수 없음
    자료에 대한 스트림을 생성하여 연산을 수행하면 스트림은 소모된다. 다른 연산을 수행하기 위해서는 스트림을 다시 생성해야 함

  • 스트림 연산은 기존 자료를 변경하지 않음
    자료에 대한 스트림을 생성하면 스트림이 사용하는 메모리 공간은 별도로 생성되므로 연산이 수행되도 기존 자료에 대한 변경은 발생하지 않음

  • 스트림 연산은 중간 연산과 최종 연산으로 구분 됨
    스트림에 대해 중간 연산은 여러 개의 연산이 적용될 수 있지만 최종 연산은 마지막에 한 번만 적용된다. 최종연산이 호출되어야 중간 연산에 대한 수행이 이루어 지고 그 결과가 만들어진다. 따라서 중간 연산에 대한 결과를 연산 중에 알수 없고 이를 지연연산이라고 한다.

3.1 스트림 생성과 사용 예제

3.1.1 정수 배열에 스트림 생성

public class IntArrayTest {

	public static void main(String[] args) {

		int[] arr = {1,2,3,4,5};
		
		int sumVal = Arrays.stream(arr).sum();
		long count = Arrays.stream(arr).count();
		
		System.out.println(sumVal);
		System.out.println(count);
	}

}

중간 연산과 최종 연산

중간 연산의 예 - filter(), map(), sorted()
조건에 맞는 요소를 추출(filter)하거나 요소를 변환 함(map)

  • 문자열 리스트에서 문자열의 길이가 5 이상인 요소만 출력하기
  sList.stream().filter(s->s.length() >= 5).forEach(s->System.out.println(s));

filter()는 중간 연산이고, forEach()는 최종 연산

  • 고객 클래스 배열에서 고객 이름만 가져오기
  customerList.stream().map(c->c.getName()).forEach(s->System.out.println(s));

map()은 중간 연산이고, forEach()는 최종 연산

3.1.2 ArrayList 객체에 스트림 생성

ArrayList에 문자열 자료(이름)을 넣고 이에 대한 여러 연산을 수행해보기

public class ArrayListStreamTest {

	public static void main(String[] args) {
		List<String> sList = new ArrayList<String>();
		sList.add("Tomas");
		sList.add("Edward");
		sList.add("Jack");
		
		Stream<String> stream = sList.stream();
		stream.forEach(s->System.out.print(s + " "));
		System.out.println();
		
		sList.stream().sorted().forEach(s->System.out.print(s+ " "));
		sList.stream().map(s->s.length()).forEach(n->System.out.println(n));
		sList.stream().filter(s->s.length() >= 5).forEach(s->System.out.println(s));
		
	}

}
  • 새로운 연산을 수행하기 위해서는 기존의 스트림은 재사용할 수 없고 stream()메서드를 호출하여 스트림을 다시 생성해야 함


4. reduce()

정의된 연산이 아닌 프로그래머가 직접 구현한 연산을 적용할 수 있다.

T reduce(T identify, BinaryOperator<T> accumulator)

최종 연산으로 스트림의 요소를 소모하며 연산을 수행한다.

4.1 예제

배열의 모든 요소의 합을 구하는 reduce() 연산

Arrays.stream(arr).reduce(0, (a,b)->a+b));
  • reduce() 메서드의 두 번째 요소로 전달되는 람다식에 따라 다양한 기능을 수행 할 수 있음

  • 람다식을 직접 구현하거나 람다식이 긴 경우 BinaryOperator를 구현한 클래스를 사용 함

4.1.1 BinaryOperator 사용 예제

배열에 여러 문자열이 있을 때 길이가 가장 긴 문자열 찾기

class CompareString implements BinaryOperator<String>{

	@Override
	public String apply(String s1, String s2) {
		if (s1.getBytes().length >= s2.getBytes().length) return s1;
		else return s2;
	}
}

public class ReduceTest {

	public static void main(String[] args) {

		String[] greetings = {"안녕", "hello", "Good morning", "ㅎㅇㅎㅇ"};
		
		System.out.println(Arrays.stream(greetings).reduce("", (s1, s2)-> 
		                          {if (s1.getBytes().length >= s2.getBytes().length) 
				                                  return s1;
		                          else return s2;})); 
		
		String str = Arrays.stream(greetings).reduce(new CompareString()).get(); //BinaryOperator를 구현한 클래스 이용
		System.out.println(str);
		                          
	}
}
profile
학생 점심을 좀 차리시길 바랍니다

0개의 댓글