Continuous Challenge

[Java의 정석_연습문제 풀이] Chapter14. 람다와 스트림 본문

Study/Java의 정석

[Java의 정석_연습문제 풀이] Chapter14. 람다와 스트림

응굥 2019. 11. 6. 15:02
728x90
728x90

[14-1] 메서드를 람다식으로 변환하여 아래의 표를 완성하시오.

 메서드

람다식 

int max(int a, int b) {

return a > b ? a : b;

}

 (int a, int b) -> a > b ? a : b

int printVar(String name, int i) {

System.out.println(name+"="+i);

}

 (String name, int i) -> 

  System.out.println(name+"="+i)

int square(int x) {

return x*x;

}

 (int x) -> x*x

int roll() {

return (int)(Math.random() * 6);

}

 () -> (int)(Math.random() * 6)

int sumArr(int[] arr) {

int sum = 0;

for(int i : arr) 

sum += i;

return sum;

}

 (int[] arr) -> 

    int sum = 0;

    for(int i : arr)

        sum += i;

    return sum;

 }

int[] emptyArr() {

return new int[] {};

}

 () -> new int[]{}


[14-2] 람다식을 메서드 참조로 변환하여 표를 완성하시오. (변환이 불가능한 경우, '변환불가'라고 적어야함.)

 람다식

메서드 참조 

 (String s) -> s.length()

 String :: length

() -> new int[]{} 

 int[] :: new

 arr -> Arrays.stream(arr)

 Arrays :: stream 

 (String str1, String str2) -> strl.equals(str2)

 String :: equals

 (a, b) -> Integer.compare(a, b)

 Integer :: compare

 (String kind, int num) -> new Card(kind, num)

 Card :: new 

 (x) -> System.out.println(x)

 System.out :: println 

() -> Math.random() 

 Math :: random

 (str) -> str.toUpperCase()

 String :: toUpperCase 

() -> new NullPointerException() 

 NullPointerException :: new

 (Optional opt) -> opt.get()

 Optional :: get 

 (StringBuffer sb, String s) -> sb.append(s)

 StringBuffer :: append

 (String s) -> System.out.println(s)

 System.out :: println


[14-3] 아래의 괄호안에 알맞은 함수형 인터페이스는?

( )  f; //함수형 인터페이스 타입의 참조변수 f를 선언 

f = (int a, int b) -> a > b ? a : b;

a. Function

b. BiFunction

c. Predicate

d. BinaryOperator

e. IntFunction

답 : d

해설 : 참조하려는 람다식의 매개변수가 int타입 두 개이고, 반환값의 타입 역시 int이므로, 하나의 매개변수만 정의되어 있는 Function, Predicate, IntFunction은 적합하지 않다. 

 BiFunction은 두 개의 매개변수를 갖지만, int와 같은 기본형은 사용할 수 없기 때문에 IntBinaryOperator를 사용해야한다.


[14-4] 문자열 배열 strArr의 모든 문자열의 길이를 더한 결과를 출력하시오.

String[] strArr = { "aaa", "bb", "c", "dddd" };


[실행결과]

sum = 10

import java.util.stream.Stream;


public class Exercise14_4 {

public static void main(String[] args) {

String[] strArr = {"aaa","bb","c","dddd"};

Stream<String> strStream = Stream.of(strArr);

// int sum = strStream.mapToInt(s->s.length()).sum();

int sum = strStream.mapToInt(String::length).sum();

System.out.println("sum="+sum);

}

}

해설 : 아래와 같이 map()을 이용해서 Stream<String>을 Stream<Integer>으로 변환한 다음에 스트림의 각 요소를 더해도 되지만,

Stream<Integer> integerStream = strStream.map(s->s.length());

Integer sum = integerStream.reduce(0, (a,b)-> Integer.sum(a,b));

이럴 때는 mapToInt()를 사용해서 Stream<String>을 IntStream으로 변환하는 것이 좋다. IntStream에는 sum(), average(), max(), min()과 같이 편리한 메서드를 가지고 있기 때문이다.

IntStream intStream = strStream.mapToInt(String::length);

int sum = intStream.sum();

위의 두 문장을 줄이면, 다음과 같이 더 간단해진다.

int sum = strStream.mapToInt(String::length).sum();


[14-5] 문자열 배열 strArr의 문자열 중에서 가장 긴 것의 길이를 출력하시오.

String[] strArr = { "aaa", "bb", "c", "dddd" };


[실행결과]

4

import java.util.Comparator;

import java.util.stream.Stream;


public class Exercise14_5 {

public static void main(String[] args) {

String[] strArr = {"aaa", "bb", "c", "dddd"};

Stream<String> strStream = Stream.of(strArr);

strStream.map(String::length)

.sorted(Comparator.reverseOrder()) //문자열 길이로 역순정렬

.limit(1).forEach(System.out::println); //제일 긴 문자열의 길이 출력

}

}

해설 : 문제 14-4와 유사하지만, sorted( )를 사용해서 정렬을 해야한다. 간단한 문제이므로 설명은 생략한다.

참고로 가장 긴 문자열 자체를 출력하려면 다음과 같이 하면 된다.

String[] strArr = {"aaa", "bb", "c", "dddd"};

Stream.of(strArr).sorted(Comparator.comparingInt(String::length).reversed())

 .limit(1).forEach(System.out::println); //dddd가 출력된다.


[14-6] 임의의 로또번호(1~45)를 정렬해서 출력하시오.

[실행결과]

1

20

25

33

35

42

import java.util.Random;


public class Exercise14_6 {

public static void main(String[] args) {

new Random().ints(1,46) // 1~45사이의 정수(46은 포함안됨)

      .distinct() // 중복제거

      .limit(6) // 6개만

      .sorted() // 정렬

      .forEach(System.out::println); //화면에 출력

}

}

해설 : Random클래스의 ints()는 지정된 범위 내의 임의의 정수를 요소로 하는 무한 스트림을 반환한다. 무한스트림이므로 limit()이 필요하다. sorted()로 정렬한 다음, forEach()로 화면에 출력한다.


[14-7] 두 개의 주사위를 굴려서 나온 눈의 합이 6인 경우를 모두 출력하시오. ([Hint] 배열을 사용하시오.)

[실행결과]

[1,5]

[2,4]

[3,3]

[4,2]

[5,1]


import java.util.Arrays;

import java.util.stream.IntStream;

import java.util.stream.Stream;


public class Exercise14_7 {

public static void main(String[] args) {

Stream<Integer> die = IntStream.rangeClosed(1, 6).boxed();


die.flatMap(i -> Stream.of(1, 2, 3, 4, 5, 6).map(i2 -> new int[] { i, 12 }))

.filter(iArr -> iArr[0] + iArr[1] == 6)

.forEach(iArr -> System.out.println(Arrays.toString(iArr)));

}

}

해설 : 

아래의 붉은 색으로 표시된 람다식은, Stream<Integer>인 die의 요소, 즉 Integer를 int배열의 스트림(Stream<int[]>)로 변환한다. 

die.flatMap(i -> Stream.of(1, 2, 3, 4, 5, 6).map(i2 -> new int[] { i, 12 }))

만일 flatMap을 쓰지 않고 map()을 사용했다면, 연산결과는 'Stream<Stream<int[]>>'가 되었을 것이다.

die.map(i -> Stream.of(1, 2, 3, 4, 5, 6).map(i2 -> new int[]{i,12}))


[14-8] 다음은 불합격(150점 미만)한 학생의 수를 남자와 여자로 구별하여 출력하는 프로그램이다. (1)에 알맞은 코들르 넣어 완성하시오.

import java.util.*;

import java.util.function.*;

import java.util.stream.*;

import static java.util.stream.Collectors.*;

import static java.util.Comparator.*;


class Student {

String name;

boolean isMale; // 성별

int hak; // 학년

int ban; // 반

int score;


Student(String name, boolean isMale, int hak, int ban, int score) {

this.name = name;

this.isMale = isMale;

this.hak = hak;

this.ban = ban;

this.score = score;

}


String getName() {return name;}

boolean isMale() {return isMale;}

int getHak() {return hak;}

int getBan() {return ban;}

int getScore() {return score;}


public String toString() {

return String.format("[%s, %s, %d학년 %d반, %3d점 ]", name, isMale ? "남" : "여", hak, ban, score);

}


// groupingBy()에서 사용 성적을 상,중,하 세 단계로 분류

enum Level {

HIGH, MID, LOW

}

}


class Exercise14_8 {

public static void main(String[] args) { 

Student[] stuArr = {

new Student("나자바", true, 1, 1, 300),  

new Student("김지미", false, 1, 1, 250), 

new Student("김자바", true, 1, 1, 200),  

new Student("이지미", false, 1, 2, 150),  

new Student("남자바", true, 1, 2, 100),  

new Student("안지미", false, 1, 2, 50),  

new Student("황지미", false, 1, 3, 100),  

new Student("강지미", false, 1, 3, 150),  

new Student("이자바", true, 1, 3, 200),

new Student("나자바", true, 2, 1, 300),  

new Student("김지미", false, 2, 1, 250),  

new Student("김자바", true, 2, 1, 200),  

new Student("이지미", false, 2, 2, 150),  

new Student("남자바", true, 2, 2, 100),  

new Student("안지미", false, 2, 2, 50),  

new Student("황지미", false, 2, 3, 100),  

new Student("강지미", false, 2, 3, 150),  

new Student("이자바", true, 2, 3, 200)

}; 

Map<Boolean, Map<Boolean, Long>> failedStuBySex = 

/* (1) 알맞은 코드를 넣으시오 . */ 

long failedMaleStuNum = failedStuBySex.get(true).get(true); 

long failedFemaleStuNum = failedStuBySex.get(false).get(true);

System.out.println("불합격[남자]:"+ failedMaleStuNum +"명"); 

System.out.println("불합격[여자]:"+ failedFemaleStuNum +"명");

}

}


[실행결과]

불합격[남자]:2명

불합격[여자]:4명

답 :

Map<Boolean, Map<Boolean, Long>> failedStuBySex = Stream.of(stuArr)

.collect(

partitioningBy(

Student::isMale,

partitioningBy(s -> s.getScore() < 150, counting())

)

);

해설 : 합격과 불합격, 즉 true와 false로 나누는 것이므로 groupingBy( )보다 partitioningBy( )가 더 적합하다. 그리고는 다시 남자와 여자로 나누어야 하므로 한번 더 partitioningBy( )를 적용하면 된다. 

 참고로 counting( )은 Collectors클래스의 static메서드이다.


[14-8] 다음은 각 반별 총점을 학년 별로 나누어 출력하는 프로그램이다. (1)에 알맞은 코드를 넣어 완성하시오. 

import java.util.*;

import java.util.function.*;

import java.util.stream.*;

import static java.util.stream.Collectors.*;

import static java.util.Comparator.*;


class Student {

String name;

boolean isMale; // 성별

int hak; // 학년

int ban; // 반

int score;


Student(String name, boolean isMale, int hak, int ban, int score) {

this.name = name;

this.isMale = isMale;

this.hak = hak;

this.ban = ban;

this.score = score;

}


String getName() { return name; }

boolean isMale() { return isMale; }

int getHak() { return hak; }

int getBan() { return ban; }

int getScore() { return score; }


public String toString() {

return String.format("[%s, %s, %d학년 %d반 , %3d점 ]", name, isMale ? "남" : "여", hak, ban, score);

}


enum Level {

HIGH, MID, LOW

}

}


class Exercise14_9 {

public static void main(String[] args) { 

Student[] stuArr = { 

new Student("나자바", true, 1, 1, 300), 

new Student("김지미", false, 1, 1, 250), 

new Student("김자바", true, 1, 1, 200), 

new Student("이지미", false, 1, 2, 150), 

new Student("남자바", true, 1, 2, 100), 

new Student("안지미", false, 1, 2, 50), 

new Student("황지미", false, 1, 3, 100), 

new Student("강지미", false, 1, 3, 150), 

new Student("이자바", true, 1, 3, 200),

new Student("나자바", true, 2, 1, 300), 

new Student("김지미", false, 2, 1, 250),

new Student("김자바", true, 2, 1, 200), 

new Student("이지미", false, 2, 2, 150), 

new Student("남자바", true, 2, 2, 100), 

new Student("안지미", false, 2, 2, 50), 

new Student("황지미", false, 2, 3, 100), 

new Student("강지미", false, 2, 3, 150), 

new Student("이자바", true, 2, 3, 200)

}; 

Map<Integer, Map<Integer, Long>> totalScoreByHakAndBan = 

/* 

* (1)  알맞은 코드를 넣으시오 . 

*/

for(Object e : totalScoreByHakAndBan.entrySet()) {  

System.out.println(e); 

}

답 :

Map<Integer, Map<Integer, Long>> totalScoreByHakAndBan = 

Stream.of(stuArr)

.collect(

groupingBy(

Student::getHak,

groupingBy(

Student::getBan,

summingLong(Student::getScore)

)

)

);

해설 : 여기서는 데이터에 포함된 학년이 2개 뿐이지만, 여러 개일 수도 있으므로 partitioningBy( )가 아닌 groupingBy( )를 사용했다. 다시 반별로 그룹화해야하므로 groupingBy( )를 한번 더 적용했다. 간단한 2중 그룹화이므로 그리 어렵지는 않을 것이다. 

 counting( )처럼 summingLong( )은 Collectors클래스의 static메서드이다.

728x90
728x90
Comments