
지난주에 이어 JAVA 후반부의 내용을 배웠다. 중요한 내용들인 만큼 하루하루 소화해 내는데 바빴던 한 주였던 것 같다. 배운 내용은 아래와 같다.
chap14. 컬렉션(>클릭<)
chap15. Enum
chap16. 람다(클릭)
chap17. 스트림(클릭)
chap18. 스레드(클릭)
chap19.Juni(클릭)
자료구조 & 알고리즘(클릭)
내용 하나하나 하루만에 다 습득하기란 물론 불가능한 내용들이지만, 이번 회고도 새롭게 배우고 기억해야 하는 내용들 위주로 작성해 보겠다.
로또 번호 생성하는 람다 식을 세우시오
람다에 대한 이해도가 부족하여 이 문제를 풀 때 동기분의 도움을 받아 풀었다. 이를 통해 어느 정도 람다에 대한 이해가 생겼지만, 그 전까지 감을 잘 못잡았다.
Set() 을 사용하고, 로또 번호 생성을 위해 random()을 사용해야겠다는 생각만 가지고 문제를 풀었지만, 아래와 같이 한 줄만 쓰고 아무것도 못했다.
Function<Set<Integer>> set = () -> (int)((Math.random()*45)+6);
람다 Comsumer, Supplier, Function 중 하나를 써야겠다는 생각은 들었지만 내부 람다 사용과 로직에 대해 아무런 아이디어도 떠오르지 않고, 수업자료들을 참고해보아도 어떤 식으로 풀어야 할지 감이 오지 않았다.
그렇게 동기분에게 어떤 식으로 풀어야 할지 질문헀는데, 동기분이 이해가 아예 안될 때는 답을 확인하고 시작하는 것도 이해에 도움이 될 때가 많다며 답을 부여 준 것이 많은 도움이 되었다.
내가 했던 실수는 아래와 같다.
Supplier<Set<Integer>> lotto = () -> {
Random random = new Random();
Set<Integer> lottoSet = new HashSet<>();
while (lottoSet.size() < 6) {
lottoSet.add(random.nextInt(100));
}
System.out.println(Arrays.toString(lottoSet.toArray()));
return lottoSet;
};
lotto.get();
Supplier<Set<Integer>> lotto =
해당 로또 문제는 입력 X 반환 X 출력 O 이므로, Comsumer 나 Supplier를 사용하면 된다. 로또는 중복제외&정수값이니 반환 자료형은 Set<Integer> 이다.
이 문제는 User 클래스에서 나이가 20세 이상인 객체를 이름만 리스트로 출력하는 문제이다.
내가 작성한 코드는 아래와 같다. 하지만 계속해서 경고가 떠서 한 동안 막혀있었다.
Stream<User> streamUsers = users.stream();
streamUsers
.filter(user -> user.getAge() >= 20)
.map(User::getName)
.collect(Collectors.toList());
System.out.println(names);
내가 했던 실수는 Stream<User> 타입으로 .collect() 를 통해 리스트로 받아오려고 했던 것이다. 타입에러의 일종인 것이다.
List<String> names = users.stream() // Stream 생성
.filter(user -> user.getAge() >= 20) // 조건: 나이 20 이상
.map(User::getName) // 이름만 추출
.collect(Collectors.toList()); // List로 수집
System.out.println(names);
.collect() 로 모아서 리스트로 만들기 위해서는 names를 List 타입으로 선언하여, .stream()으로 스트림으로 변경해야 했다. 이를 통해 문제를 해결했다.
stream()은 1회용이라 생성한 스트림을 streamUsers 변수로 따로 저장할 필요 없이 바로 사용하면 된다. 선언 후 곧바로 이어쓰는 것을 의미한다..collect() 를 쓰려면 List<T> 타입으로 받아와야 한다. 이번 문제는 null이 아닌 문자열 중 "java"를 포함하는 텍스트만 소문자로 출력하고, Objects::nonNull를 이용해서 null 체크하는 문제이다.
내가 작성한 코드는 아래와 같다.
List<String> result = texts.stream()
.map(Objects::nonNull)
.filter(str->str.toString().contains("java"))
.map(text -> text.toString().toLowerCase())
.forEach(System.out::println);
이렇게 작성했는데, .forEach()에서 호환되지 않는 타입이라는 컴파일 오류가 계속 떴다. 위 코드에 어떤 오류가 있는지 하나씩 알아가 보자.
.map(Objects::nonNull)
map은 변환용 메서드이다. 스트림내 요소들에 대해 함수가 적용된 결과의 새로운 요소로 매핑해준다. 위 상황에서는 .map이 아니라, filter() 가 적합하다. 게다가 null 이 들어와 boolean 오류가 생길 것이다.
.filter(str->str.toString().contains("java"));
이 또한 null 때문에 널포인트에러가 뜰 것이다.
.map(text -> text.toString().toLowerCase())
이 전에 null 제거가 되지 않았기 때문에 연쇄적으로 nullPointerError가 발생할 것이다.
.forEach(System.out::println);
forEach() 는 void를 반환하기에 List<String> 타입과 알맞지 않다.
List<String> result = texts.stream()
.filter(Objects::nonNull) // null 제거
.filter(str -> str.toLowerCase().contains("java")) // "java" 포함 여부
.map(String::toLowerCase) // 소문자로 변환
.collect(Collectors.toList()); // 리스트로 수집
result.forEach(System.out::println); // 출력
filter(Objects::nonNull)filter(str -> str.toLowerCase().contains("java"))map(String::toLowerCase)toLowerCase는 String 메소드이므로 메소드 레퍼렌스::로 간략하게 작성해보았다.collect(Collectors.toList())collect() 로 결과를 리스트로 모아준다. List<String> 타입으로 가져왔으니, 동일한 타입으로 반환해주자.i love java
java is fun
filter(Objects::nonNull) : map 아니다. map은 스트림 각 요소 어케 바꿀지 정해줌map은 변환용, filter는 조건검사용임forEach는 결과 출력용이며, collect는 리스트 반환이라 같이 못쓴다.문제: 길이가 3 이상인 문자열만 골라 쉼표로 연결하여 출력하세요.
List<String> words = List.of("hi", "hello", "to", "world", "java");
Stream<String> wordsStream = words.stream()
.filter(word -> word.length() >= 3)
.collect(Collectors.joining(","));
// 출력값 // hello, world, java
안풀렸다. 조금씩 바꿀 때마다 다른 오류가 발생하고 계속해서 생기는 오류를 잡기가 안되어 오류를 잡는데 정말 오래 걸렸다. 길이가 3이상인 문자열을 연결해주면 되는것 아닌가 해서, .filter()와 collect()의 joinging() 만 적절히 사용하면 금방 끝날 줄 알았다.
이번에 잘못한 것도 collect() 사용 시 반환 타입에 있어서 발생했다.
collect(Collectors.joining(","))는 Stream<String>이 아니라 String을 반환 한다는 것이다.
그래서 wordsStream의 타입을 Stream<String>이 아니라 String으로 받아한다.
List<String> words = List.of("hi", "hello", "to", "world", "java");
String result = words.stream() // String 타입을 스트림 화
.filter(word -> word.length() >= 3) // 길이가 3 이상
.collect(Collectors.joining(", ")); // 연결
System.out.println(result);
hello, world, java
Collectors.joining(delimiter) → 스트림의 문자열 요소를 구분자 기준으로 하나의 문자열로 합침. 결국 반환하는 것이 String 타입이기 때문에, 얘를 받아줄 얘도 String 타입이어야 한다..collect(Collectors.joining(",")) 결과 타입은 String → 받아줄 타입을 Stirng result로 스트링 타입으로 객체를 생성해서 스트림화시키고, 조건을 맞춘 후 collect()에 담아 다시 String으로 반환한다.