Backend
home

2024. 7. 24

람다식 표현 정리

•
방법 1
public static Comparator<Book> byAuthor() { return Comparator.comparing(Book::getAuthor); }
Java
복사
•
방법 2
public static Comparator<Book> byTitle() { return Comparator.comparing(new Function<Book, String>() { @Override public String apply(Book book) { return book.getTitle(); } }); }
Java
복사
•
방법 3
// 둘 다 같은 로직 public static Comparator<Book> byYear() { return Comparator.comparingInt(book -> book.getYear()); } public static Comparator<Book> byYear() { return Comparator.comparingInt(new ToIntFunction<Book>() { @Override public int applyAsInt(Book book) { return book.getYear(); } }); }
Java
복사

람다식 - 생성자 참조

•
생성자를 참조한다는 것은 객체를 생성한다는 것을 의미한다.
•
람다식이 단순히 객체를 생성하고 반환하도록 구성된다면, 람다식을 생성자 참조로 대체할 수 있게 된다.
•
생성자가 오버로딩되어 여러 개가 있을 경우, 컴파일러는 함수형 인터페이스의 추상 메소드와 동일한 매개변수 타입과 개수를 가지고 있는 생성자를 찾아 실행한다.
// Person.java package com.lambda2; public class Person { public Member getMember1(Creatable1 c) { // 아이디를 가지고 멤버를 생성 String id = "summer"; Member m = c.create(id); return m; } public Member getMember2(Creatable2 c) { // 아이디와 이름을 가지고 멤버를 생성 및 반환 String id = "summer"; String name = "한여름"; Member m = c.create(id, name); return m; } }
Java
복사
// Member.java package com.lambda2; public class Member { private String id, name; public Member(String id) { this.id = id; System.out.println("멤버(id만으로 생성)"); } public Member(String id, String name) { this.id = id; this.name = name; System.out.println("멤버(id, name으로 생성)"); } @Override public String toString() { return "{" + "id='" + id + '\'' + ", name='" + name + '\'' + '}'; } }
Java
복사
package com.lambda2; public class ConstructorExample { public static void main(String[] args) { Person p = new Person(); p.getMember1(new Creatable1() { @Override public Member create(String id) { return new Member(id); } }); // p.getMember1(id -> new Member(id)); // p.getMember1(Member::new); // // p.getMember2((id, name) -> new Member(id, name)); p.getMember2(Member::new); } }
Java
복사
package com.lambda2; @FunctionalInterface public interface Creatable1 { public Member create(String id); } // --------------------------------------------------------------------------- package com.lambda2; @FunctionalInterface public interface Creatable2 { public Member create(String id, String name); }
Java
복사

스트림

•
stream() 메소드로 Stream 객체를 얻고, forEach() 메소드로 요소를 어떻게 처리할 지 람다식으로 제공해준다.
•
스트림의 장점
◦
내부 반복자이므로 보다 빠른 처리 속도를 가지고 있으며 병렬 처리에 효과적이다.
◦
람다식으로 다양한 요소 처리를 정의할 수 있다.
◦
중간 처리와 최종 처리를 수행하도록 파이프 라인을 형성할 수 있다.
•
for문, iterator는 컬렉션 요소를 컬렉션 바깥쪽으로 반복해서 가져와 처리하지만 스트림은 요소 처리 방법을 컬렉션 내부로 주입시켜서 요소를 반복 처리하는 내부 반복자이다.
•
for문과의 차이
package com.stream1; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; public class StreamExample { public static void main(String[] args) { List<String> languageList = new ArrayList<>(); languageList.add("Java"); languageList.add("JavaScript"); languageList.add("Python"); languageList.add("C"); // 병렬 스트림 얻기 Stream<String> parallelStream = languageList.parallelStream(); parallelStream.forEach(name -> { System.out.println(name + " : " + Thread.currentThread().getName()); }); System.out.println("==============================================="); // for 문으로 동작시키기 for (String name : languageList) { System.out.println(name + " : " + Thread.currentThread().getName()); } } } == 결과 == -- 순서는 바뀔 수 있음 -- JavaScript : ForkJoinPool.commonPool-worker-1 Java : ForkJoinPool.commonPool-worker-3 C : ForkJoinPool.commonPool-worker-2 Python : main =============================================== Java : main JavaScript : main Python : main C : main
Java
복사

스트림 - 중간 처리와 최종 처리

•
스트림은 하나 이상 연결될 수 있다.
•
아래 그림을 보면 컬렉션의 오리지널 스트림 뒤에 필터링 중간 스트림이 연결될 수 있고, 그 뒤에 매핑 중간 스트림이 연결될 수도 있다.
•
이와 같이 스트림이 연결되어 있는 것을 스트림 파이프라인(Stream Pipeline)이라고 한다.
•
중간 스트림은 최종 처리를 위해 요소를 걸러내거나(필터링), 요소를 변환시키거나(매핑), 정렬하는 작업을 수행한다.
•
최종 처리는 중간 처리에서 정제된 요소들을 반복하거나, 집계 처리(카운팅, 총합, 평균) 작업을 수행한다.
•
컬렉션에서 Student 스트림을 얻고, 중간 처리를 통해 score 스트림으로 변환한 후 최종 집계 처리로 score 평균을 구하는 과정이다.
package com.stream2; import java.util.Arrays; import java.util.List; import java.util.OptionalDouble; import java.util.stream.IntStream; import java.util.stream.Stream; public class StreamExample { public static void main(String[] args) { List<Student> sList = Arrays.asList( new Student("Alice", 90), new Student("Bob", 80), new Student("Jonathan", 85), new Student("David", 95) ); // Stream<Student> originalStream = sList.stream(); // IntStream intStream = originalStream.mapToInt(Student::getScore); // OptionalDouble optAverage = intStream.average(); // double avg = optAverage.getAsDouble(); // 위의 주석처리된 코드를 다음과 같이 표현할 수 있다. double avg = sList.stream() .mapToInt(Student::getScore) .average() .getAsDouble(); System.out.println(avg); } }
Java
복사

스트림 - 리소스로부터 스트림 얻기

•
java.util.stream 패키지에는 스트림과 관련한 인터페이스들이 있다.
•
BaseStream 인터페이스를 부모로 한 자식 인터페이스는 상속 관계를 이룬다.
•
BaseStream에는 모든 스트림에서 사용할 수 있는 공통 메소드들이 정의되어 있으며,
◦
Stream은 객체 요소를 처리하는 스트림이다.
◦
IntStream, LongStream, DoubleStream은 각각 기본 타입인 int, long, double 요소를 처리하는 스트림이다.
•
스트림 인터페이스들은 주로 컬렉션과 배열에서 얻는다.
•
java.util.Collection 인터페이스는 stream() 메소드와 parallelStream() 메소드를 가지고 있기 때문에 자식 인터페이스인 List와 Set 인터페이스를 구현한 모든 컬렉션에서 객체 스트림을 얻을 수 있다.
package com.stream2; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; public class ProductExample { public static void main(String[] args) { List<Product> pList = new ArrayList<>(); for (int i = 1; i <= 5; i++) { Product p = new Product(i, (int) (10000 * Math.random()), "상품" + i, "회사명"); pList.add(p); } Stream<Product> stream = pList.stream(); stream.forEach(System.out::println); } }
Java
복사

스트림 - 리소스로부터 스트림 얻기 (배열)

•
java.util.Arrays 클래스를 이용하면 다양한 종류의 배열로부터 스트림을 얻을 수 있다.
package com.stream4; import java.util.Arrays; import java.util.stream.IntStream; import java.util.stream.Stream; public class StreamExample { public static void main(String[] args) { String[] strArr = {"맥북", "이어폰", "에어팟"}; // 배열로부터 스트림 얻는 두 가지 방법 Stream<String> strStream1 = Stream.of(strArr); Stream<String> strStream2 = Arrays.stream(strArr); strStream1.forEach(i -> System.out.print(i + ", ")); System.out.println(); strStream2.forEach(i -> System.out.print(i + ", ")); System.out.println("\n"); int[] intArr = { 3, 1, 4, 1, 5, 9, 2 }; // 배열로부터 스트림 얻는 두 가지 방법 IntStream intStream1 = IntStream.of(intArr); IntStream intStream2 = Arrays.stream(intArr); intStream1.forEach(i -> System.out.print(i + ", ")); System.out.println(); intStream2.forEach(i -> System.out.print(i + ", ")); } }
Java
복사

스트림 - 리소스로부터 스트림 얻기 (숫자 범위)

•
IntStream 또는 LongStream의 정적 메소드인 range( ) 와 rangeClosed( ) 메소드를 이용하면, 특정 범위의 정수 스트림을 얻을 수 있다.
•
range( ) 와 rangeClosed( ) 메소드의 첫 번째 매개값은 시작 수이고, 두 번째 매개값은 끝 수이다.
•
range( ) 메소드는 끝 수를 포함하지 않고, rangeClosed( ) 메소드는 끝 수를 포함한다.
package com.stream5; import java.util.stream.IntStream; public class StreamExample { public static void main(String[] args) { // 특정 범위의 정수 스트림을 만들기 int sum1 = IntStream.range(1, 10).sum(); System.out.println(sum1 + "\n"); int sum2 = IntStream.rangeClosed(1, 10).sum(); System.out.println(sum2); } }
Java
복사

스트림 - 리소스로부터 스트림 얻기 (파일)

•
java.nio.file.Files의 lines( ) 메소드를 이용하면 텍스트 파일의 행 단위 스트림을 얻을 수 있어서, 텍스트 파일에서 한 행씩 읽고 처리할 때 유용하게 사용할 수 있다. (복사경로: “/Users/haminsung/Desktop/test/java”)
// data.txt - raw data {"pno": 1, "name": "아이폰", "company": "apple", "price": 2000000} {"pno": 2, "name": "맥북 프로", "company": "apple", "price": 3000000} {"pno": 3, "name": "갤럭시 워치", "company": "samsung", "price": 400000} {"pno": 4, "name": "PS5", "company": "sony", "price": 500000} {"pno": 5, "name": "아이패드", "company": "apple", "price": 800000}
Java
복사
// data.txt - 가공 1 아이폰 apple 2000000 2 맥북프로 apple 3000000 3 갤럭시워치 samsung: 400000 4 PS5 sony 500000 5 아이패드 apple 800000
Java
복사
package com.stream6; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.stream.Stream; public class StreamExample { public static void main(String[] args) throws IOException { String absolutePath = "/Users/haminsung/Desktop/test/java/data.txt"; Path path = Paths.get(absolutePath); Stream<String> stream = Files.lines(path, Charset.defaultCharset()); // stream.forEach(line -> System.out.println(line)); stream.forEach(l -> { String[] productInfo = l.split(" "); int pno = Integer.parseInt(productInfo[0]); String pName = productInfo[1]; String pCompany = productInfo[2]; int price = Integer.parseInt(productInfo[3]); Product p = new Product(pno, pName, pCompany, price); System.out.println(p); }); stream.close(); } } /// Product.java /// package com.stream6; import lombok.AllArgsConstructor; import lombok.Data; @AllArgsConstructor @Data public class Product { private int pno; private String pname; private String pCompany; private int price; }
Java
복사

스트림 - 요소 걸러내기 (필터링)

•
필터링은 요소를 걸러내는 중간 처리 기능으로, distinct()와 filter() 메소드가 필터링에 쓰인다.
•
Predicate 인터페이스는 함수형 인터페이스로 객체를 조사하는 인터페이스다.
◦
Predicate: 객체 요소 조사 인터페이스
◦
IntPredicate, LongPredicate, DoublePredicate는 각각 기본 타입인 int, long, double 요소를 처리하는 스트림이다.
•
Predicate 인터페이스에는 매개값을 조사한 후, boolean을 반환하는 test() 메소드가 있다.
package com.stream7; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class StreamFilterExample { public static void main(String[] args) { List<String> nameList = Arrays.asList("우상혁", "황선우", "김우민", "서채현", "신유빈", "우상혁"); // distinct로 중복이 제거되었다! nameList.stream() .distinct() .forEach(n -> System.out.print(n + ", ")); System.out.println(); // filter로 "우"가 들어간 이름만 나온다. nameList.stream() .filter(n -> n.contains("우")) .forEach(n -> System.out.print(n + ", ")); System.out.println(); System.out.println("======================================="); // distinct로 중복된 요소가 제거하고, filter로 "우"가 들어간 이름만 나오게 하기 // nameList.stream() // .distinct() // .filter(n -> n.contains("우")) // .forEach(n -> System.out.print(n + ", ")); // 리스트 형태로 출력됨 List<String> filteredList = nameList .stream() .distinct() .filter(n -> n.contains("우")) .collect(Collectors.toList()); // 리스트 형태로 변환 System.out.println(filteredList); } }
Java
복사

스트림 - 요소 변환 (매핑)

•
스트림의 요소를 다른 요소로 변환하는 중간 처리 기능
•
mapXxx( ) 메소드의 종류는 상당히 다양하며, 해당 요소를 변환할 때 사용된다.
◦
map( ), mapToInt( ), mapToLong( ), mapToDouble( ), mapToObj( )…
•
mapXxx() 메소드의 매개변수에는 Function 인터페이스 타입이 들어오는데, Function은 함수형 인터페이스이다.
•
모든 Function 인터페이스에는 매개값을 반환값으로 매핑(변환) 하는 applyXxx() 메소드가 있다.
•
asDoubleStream( ), asLongStream( ) 메소드는 기본 타입 간의 변환에 사용되며,
•
boxed( ) 메소드는 기본 타입 요소를 래퍼 객체 요소로 변환할 때 사용된다.

스트림 - 요소 변환 (매핑)

•
flatMapXxx() 메소드는 하나의 요소를 복수 개의 요소들로 변환해 새로운 스트림을 반환한다.
•
flatMapXxx() 메소드의 종류는 상당히 다양하며, 해당 요소를 변환할 때 사용된다.
◦
flatMap(), flatMapToInt(), flatMapToLong(), flatMapToDouble() …
•
flatMapXxx() 메소드의 매개변수에도 MapXxx()와 동일하게 Function 인터페이스 타입이 들어온다.

스트림 - 요소 정렬

•
스트림의 중간 처리 기능으로 정렬이 있고, 이는 sorted() 메소드로 사용 가능하다.
•
sorted() 메소드는 요소가 Comparable을 구현하고 있어야만 사용 가능하다.
•
만약 Comparable을 구현하지 않은 객체로 구성된 스트림인 경우에는 ClassCastException이 발생한다.
package com.stream12; import java.util.ArrayList; import java.util.Comparator; import java.util.List; public class SortExample { public static void main(String[] args) { List<Student> sList = new ArrayList<>(); sList.add(new Student("박명수", 60)); sList.add(new Student("유재석", 100)); sList.add(new Student("정준하", 40)); sList.stream() .sorted((s1, s2) -> s1.getScore() - s2.getScore()) .forEach(s -> System.out.println(s)); System.out.println(); sList.stream() .sorted((s1, s2) -> s2.getScore() - s1.getScore()) .forEach(s -> System.out.println(s)); } }
Java
복사