[TIL] 2월 22일
스트림이란?
- 다양한 데이터소스(배열, 컬렉션(List, Set, Map)를 표준화된 방법으로 다루기 위한것
- 스트림을 사용한 코드가 간결하고 재사용성도 높다.
스트림의 특징
-
스트림은 데이터 소스를 읽기만 하고, 변경하지 않는다. (원본 변경 X, read only)
-
스트림은 Iterator처럼 일회용이다.
- 한번 사용하면 닫혀서 다시 사용할 수 없다. 필요하다면 스트림을 다시 생성해야한다.
-
최종 연산이 수행되기 전까지는 중간 연산이 수행되지 않는다. → 지연된 연산
- 중간 연산을 호출하는 것은 단지 수행되어야할 작업을 지정해주는 것
- 최종연산이 수행되어야 비로소 스트림의 요소들이 중간 연산을 거친다.
-
스트림은 작업을 내부 반복으로 처리한다.
- forEach() 메서드가 해당
- forEach()는 메서드 안으로 for문을 넣은 것이다.
- 코드가 간결해진다.
-
스트림의 작업을 병렬로 처리
- 병렬스트림을 지원한다.
-
기본형 스트림 제공 - IntStream, LongStream, DoubleStream 등등
-
Stream 대신에 IntStream을 사용하면 된다.
→ 오토박싱과 언박싱의 비효율이 줄어든다.
-
숫자와 관련된 유용한 메서드를 IntStream이 Stream보다 더 많이 제공한다.
-
루카스 함수형 프로그래밍 실습문제
-
CarTest클래스에 구현된 test코드 안에 있는 익명클래스를 람다로 바꿔본다.
public interface MoveStrategy { boolean isMovable(); }
public class Car { ... public Car move(MoveStrategy moveStrategy) { if (moveStrategy.isMovable()) { return new Car(name, position + 1); } return this; } ... }
public class CarTest { @Test void 이동() { Car car = new Car("pobi", 0); Car actual = car.move(() -> true); assertEquals(new Car("pobi", 1), actual); } @Test void 정지() { Car car = new Car("pobi", 0); Car actual = car.move(() -> false); assertEquals(new Car("pobi", 0), actual); } }
이렇게 하는게 맞나..?
-
Lambda 클래스에 구현된 sumAll(), sumAllEven(), sumAllOverThree()의 중복을 제거한다.
sum()이라는 메소드를 생성해서 매개변수로 List와 함수형 인터페이스 Conditional의 참조변수를 지정했다.
public class Lambda {
...
public int sum(List<Integer> numbers, Conditional c){
int total = 0;
for(int num: numbers){
if(c.test(num)){
total += num;
}
}
return total;
}
...
}
@FunctionalInterface
public interface Conditional {
boolean test(Integer number);
}
public class LambdaTest {
@Test
void check_sum() {
Lambda lambda = new Lambda();
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
int test = lambda.sum(numbers, x -> true);
int expected = lambda.sumAll(numbers);
assertEquals(test, expected);
test = lambda.sum(numbers, x -> x % 2 == 0);
expected = lambda.sumAllEven(numbers);
assertEquals(test, expected);
test = lambda.sum(numbers, x -> x > 3);
expected = lambda.sumAllOverThree(numbers);
assertEquals(test, expected);
}
}
테스트 코드를 신경써서 짜는게 좋겠지만 람다식 실습에 의의를 두기로..
실습에서 원하는게 이게 맞나?
자바의 정석 14장 람다와 스트림
스트림의 중간연산(823p)
스트림 자르기 - skip(), limit()
- Stream skip(long n) : 앞에서부터 n개 건너뛰기
- Stream limit(long maxSize) : maxSize 이후의 요소 걸러내기
- 예제
IntStream intStream = IntStream.rangeClosed(1, 10);
intStream.skip(3).limit(4).forEach(System.out::print);
</> 실행 결과
4567
스트림 요소 걸러내기 -filter(), distinct()
- Stream filter(Predicate<? super T> predicate) : 조건식에 맞지 않는 것 제거
- boolean값을 반환하는 람다식을 매개변수로 줘도 된다.
- Stream distinct() : 중복 제거
- 예제
// distinct
IntStream intStream = IntStream.of(1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5);
intStream.distinct().forEach(System.out::print);
System.out.println();
// filter
intStream = IntStream.rangeClosed(1, 10);
intStream.filter(i -> i % 2 == 0).forEach(System.out::print);
System.out.println();
intStream = IntStream.rangeClosed(1, 10);
intStream.filter(i -> i % 2 != 0).filter(i -> i % 3 != 0).forEach(System.out::print);
System.out.println();
</> 실행 결과
12345
246810
157
정렬 - sorted() (824p)
정렬을 할때는 정렬대상과 정렬 기준이 필요하다.
sorted()는 지정된 Comparator(정렬기준)로 스트림을 정렬한다.
→ sorted()는 Comparator 인터페이스에 구현된 메서드를 이용해서 정렬 기준을 제공할 수 있다.
-
Stream sorted()
-
스트림 요소의 기본 정렬 기준(Comparable)으로 정렬한다.
→ 스트림의 요소가 Comparable을 구현한 클래스여야 한다.
-
-
Stream sorted(Comparator<? super T> comparator)
- int값을 반환하는 람다식을 사용할 수 있다.
-
Comparator 인터페이스에 구현된 메서드들
-
Comparator 인터페이스에 있는 메서드들을 이용하면 정렬이 쉽다.
-
메서드들은 모두 Comparator를 반환한다.
-
가장 기본적인 메서드는 comparing()이다.
Comparator comparing(Function<T, U> keyExtractor)
Comparator comparing(Function<T, U> keyExtractor, Comparator keyComparator)
-
정렬기준이 여러개인 경우
- thenComparing()을 붙여준다.
-
-
예제
public class StreamEx1 {
public static void main(String[] args) {
Stream<Student> studentStream = Stream.of(
new Student("이자바", 3, 300),
new Student("김자바", 1, 200),
new Student("안자바", 2, 100),
new Student("박자바", 2, 150),
new Student("소자바", 1, 200),
new Student("나자바", 3, 290),
new Student("감자바", 3, 180)
);
studentStream.sorted(Comparator.comparing(Student::getBan) // 반별정렬
.thenComparing(Comparator.naturalOrder())) // 기본정렬
.forEach(System.out::println);
}
}
class Student implements Comparable<Student> {
String name;
int ban;
int totalScore;
Student(String name, int ban, int totalScore) {
this.name = name;
this.ban = ban;
this.totalScore = totalScore;
}
public String toString() {
return name + ", " + ban + ", " + totalScore;
}
public String getName() {
return name;
}
public int getBan() {
return ban;
}
public int getTotalScore() {
return totalScore;
}
// 총점 내림차순을 기본정렬로한다. Comparable 구현
public int compareTo(Student s) {
return s.totalScore - this.totalScore;
}
}
</> 실행 결과
김자바, 1, 200
소자바, 1, 200
박자바, 2, 150
안자바, 2, 100
이자바, 3, 300
나자바, 3, 290
감자바, 3, 180
-
studentStream.sorted(Comparator.comparing(Student::getBan)
.thenComparing(Comparator.naturalOrder()))
.forEach(System.out::println);-
Comparator.comparing(Student::getBan) 으로 반별로 정렬해주고
thenComparing(Comparator.naturalOrder())로 기본 정렬을 해준다.
-
-
Student는 Comparable 인터페이스를 구현했다.
- compareTo()를 오버라이딩 했다.
- 총점 내림차순을 기본정렬로 한다.
-
여기서 만약 studentStream.sorted()로 했으면
studentStream.sorted()
.forEach(System.out::println);
</> 실행 결과
이자바, 3, 300
나자바, 3, 290
김자바, 1, 200
소자바, 1, 200
감자바, 3, 180
박자바, 2, 150
안자바, 2, 100
- Comparable을 구현한 Student 클래스의 compareTo()가 기본 정렬 기준이된다.
- 총점 내림차순 기준으로 정렬된다.
이전에 Comparable, Comparator 학습할때 무슨소린지 몰라서 넘어갔는데 좀 알거 같다. 다시 공부해봐야겠다.
변환 - map() (827p)
스트림의 요소들을 원하는 것들만 뽑아내거나, 특정 형태로 변환할 때 map()을 쓴다.
-
File 객체에서 파일의 이름(String)만 뽑아내기
Stream<File> fileStream = Stream.of(new File("Ex1.java"), new File("Ex1), new File("Ex1.bak"), new File("Ex2.java"), new File("Ex1.txt")); // map()으로 Stream<File>을 Stream<String>으로 변환 Stream<String> filenameStream = fileStream.map(File::getName);
- map으로 파일(Stream)에서 파일 이름(Strema)만 추출했다.
조회 - peek()
스트림의 요소를 소비하지 않고 엿보기
forEach()와 비슷한데 peek()은 중간연산이라서 스트림의 요소를 소모하지 않는다.
forEach()는 최종연산임
Optional와 OptionalInt (835p)
T타입 객체의 래퍼클래스
null을 간접적으로 다루기 위한 것
- Optional
- null을 직접 다루면 NullPointerException이 발생할 수 있어서 Optional을 통해 간접적으로 null을 다룬다.
- null체크를 하게되면 if문이 꼭 필요하고 그럼 코드가 복잡해진다. Optional을 통해 해결할 수 있다.
- Optional 사용의 장점
- NullPointerException이 발생하지 않는다
- 널체크를 위한 if문이 없어서 간결하고 안전한 코드작성 가능
Object result = getResult();
이때 result에 담기는 반환값은 (1) null 이거나 (2) 객체 일것이다.
→ 만약 result에 null이 담겼다면,
result.toString(); 이 경우에 NullPointerException 이 발생한다.
여기서 Optional 객체에 반환값을 담는다면,
→ 이 경우에 0x100 이라는 주소값이 있으니 result는 null이 될 수 없다.
public final class Optional<T> {
private final T value; // T타입의 참조변수
...
}
- T타입의 참조변수 value에는 null을 포함한 모든 타입의 객체를 저장할 수 있다.
Optional객체 생성하기 - of(), ofNullable(), empty()
-
Optional 객체 생성하는 법 - of()의 사용
String str = "abc"; Optional<String> optVal = Optional.of(str); Optional<String> optVal = Optional.of("abc");
-
참조변수 값이 null일 가능성이 있으면 ofNullable()을 사용한다.
Optional<String> optVal = Optional.of(null); // NullPointerException 발생!!! Optional<String> optVal = Optional.ofNullable(null); // OK
-
Optional타입의 참조변수를 기본값으로 초기화하는 경우 - empty() 사용
Optional<String> optVal = null; // null로 초기화, 바람직하지 않음 Optional<String> optVal = Optional.empty(); // 빈객체로 초기화, 권장하는 방법
- 보통 String의 값을 초기화할 때 String str = null; 로 안하고 String str = "";로 하듯이 Optional타입의 참조변수를 초기화 할때도 empty()를 사용하는 것이 좋다.
Optional 객체의 값 가져오기 - get(), orElse(), orElseGEt(), orElseThrow()
-
get()과 orElse()의 사용
Optional<String> optVal = Optional.of("abc"); String str1 = optVal.get(); // optVal에 저장된 값을 반환, null이면 예외 발생 String str2 = optVal.orElse(""); // optVal에 저장된 값이 null일때 ""반환
-
get() : optVal에 저장된 값을 반환한다.
-
null이면 예외가 발생한다.
→ 이러한 이유로 주로 get()보다는 orElse()를 쓴다.
-
-
orElse("") : optVal에 저장된 값이 null이면 인자를 넣어서 대체할 값을 지정할 수 있다.
-
-
T orElseGet(Supplier<? extends T> other)
- orElse()의 변형으로 null 대신 대체할 값을 람다식으로 지정할 수 있다.
-
T orElseThrow(Supplier<? extends X> exceptionSupplier)
- null일때 지정된 예외를 발생시킨다.
-
orElseGet()과 orElseThrow()의 사용
Optional<String> optVal = Optional.of("abc"); String str1 = optVal.orElseGet(String::new); String str2 = optVal.orElseThrow(NullPointerEception::new); // null이면 예외발생
Optional객체의 값 가져오기 - isPresent()와 ifPresent() (837p)
-
isPresent() : Optional 객체의 값이 null이면 false, 아니면 true 반환
if(str != null) { System.out.println(str); }
-
아래의 코드는 위의 코드를 isPresent()를 활용해서 작성한 것이다.
if(Optional.ofNullable(str).isPresent()) { System.out.println(str); }
-
-
ifPresent() : Optional의 value가 null이 아닐때 수행할 작업을 람다식으로 지정해준다.
Optional.ofNullable(str).ifPresent(System.out::println);
- 메소드 참조 System.out::println 을 람다식으로 바꾸면
- v → System.out.println(v);
- 메소드 참조 System.out::println 을 람다식으로 바꾸면
OptionalInt, OptionalLong, OptionalDouble(838p
기본형값을 감싸는 래퍼클래스
Optional를 써도 되지만 성능때문에 이와 같은 것들을 사용한다.
11장
Comparator와 Comparable(628p)
객체 정렬에 필요한 메서드를 정의한 인터페이스
정렬 기준을 제공하는 것이 목적
-
Comparable : 기본 정렬 기준을 구현하는데 사용
public interface Comparable { public int compareTo (Object o); }
- compareTo(Object o) : 주어진 객체(o)를 자신(this)와 비교
-
Comparator : 기본 정렬 기준이외에 다른 기준으로 정렬할 때 사용
public interface Comparator { int compare(Object o1, Object o2); boolean equals(Object obj); }
- compare(Object o1, Object o2) : o1, o2 두 객체를 비교해서 어느쪽이 더 큰지 int값으로 반환
- 결과값이 0이면 같음, 양수면 왼쪽이 큼, 음수면 오른쪽이 큼
- compare(Object o1, Object o2) : o1, o2 두 객체를 비교해서 어느쪽이 더 큰지 int값으로 반환
오늘 한일
- 학습을 하면 항상 개념서에 있는 내용과 강의에서 받아적은 내용을 거의 그대로 정리해서 더 빨리 잊혀지는 것 같다. 나만의 언어로 정리해야할거 같은데 개념을 이해하기에 급급한듯 하다.
- 루카스 함수형프로그래밍에 나온 람다 실습을 해봤는데 맞게 했는지 모르겠다 ㅎㅎㅎ
- 자바의 정석 스트림 중간연산
- 스트림을 자르는 skip()과 limit(), 요소 걸러내는 filter()와 distinct(), 정렬하는 sorted()는 Comparator 인터페이스에 구현된 메서드를 이용해서 정렬 기준을 제공할 수 있다, 정렬기준을 제공하는 인터페이스 Comparator와 Comparable(기본 정렬 기준), 스트림의 요소를 변환하는 map(), forEach()와 비슷하지만 중간연산이여서 스트림의 요소를 소모하지 않고 System.out.print와 같은걸 이용해서 연산이 잘되었는지 확인하는 peek()
- null을 간접적으로 다루기 위한 래퍼클래스 Optional, of()또는 ofNullable()을 이용해서 Optional 객체 생성하기, Optional.empty()를 이용해서 빈 Optional 객체 생성하기, orElse()와 orElseGet()을 이용하여 Optional 객체에 저장된 값 가져오기
Todo
(내일)
- 스트림 최종연산 학습
- 루카스 함수형 프로그래밍 실습 마저하기
- 함수형 프로그래밍이란? 특징 나만의 언어로 정리하기
- 미션5 도전..?
Author And Source
이 문제에 관하여([TIL] 2월 22일), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@yeon/TIL-2월-22일저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)