728x90

입출력

입출력 : I/O Input과 Output의 약자이다. 컴퓨터 내부 또는 외부의 장치와 프로그램간의 데이터를 주고받는 것을 의미한다. 키보드로부터 입력을 받고 모니터 화면에 출력을 하는 행위도 I/O행위이다.

 

스트림(Stream)

자바에서의 입출력 즉 데이터를 한쪽에서 다른쪽으로 전달하려면 두 개의 대상을 연결하고 데이터를 전송할 수 있는 것이 필요하다. 이것을 스트림(Stream)이라한다.

이전에 작성한 스트림과는 다른 개념이다.

 

입출력에서 사용되는 스트림은 데이터를 운반하는 연결통로를 의미한다.

 

스트림은 한쪽방향 즉 단방향 통신만 가능하다. 그렇기 때문에 입력과 출력을 수행하기 위해서는 입력스트림(input Stream), 출력스트림(output stream) 2개의 스트림이 각각 필요하다.

 

스트림은 먼저보낸 데이터를 먼저 받는 FIFO 구조이다. 중간에 건너뜀 없이 연속적으로 데이터를 주고 받는다.

 

바이트기반 스트림 InputStream, OutputStream

스트림은 바이트단위로 데이터를 전송하며 입출력 대상에 따라 다른 스트림을 사용한다.

파일

입력스트림 - FileInputStream

출력스트림 - FileOutputStream

메모리(byte 배열)

입력스트림 - ByteArrayInputStream

출력스트림 - ByteArrayOutputStream

프로세스(프로세스간 통신)

입력스트림 - PipedInputStream

출력스트림 - PipedOutputStream

오디오 장치

입력스트림 - AudioInputStream

출력스트림 - AudioOutputStream

 

입출력 대상에따라 입출력스트림 클래스가 정의되어있으며 각각 읽고 쓰는데 필요한 추상메서드들을 입출력대상에 맞게 구현해 놓았다.

 

읽기 쓰기 메서드

입력을 받는 InputStream에는 데이터를 읽는 read()메서드가 정의되어있다.

abstract int read()

int read(byte[] b)

int read(byte[] b, int off, int len)

 

int read(byte[] b) 메서드는 내부적으로 int read(byte[] b, int off, int len) 를 호출하고  int read(byte[] b, int off, int len) 는 내부적으로 read()를 호출한다. 추상메서드 read() 는 입력스트림으로 부터 1byte를 읽어서 반환하는 메서드로 구현되어있다.

 

출력 스트림 OutputStream에는 데이터를 출력하는 write()메서드가 정의되어있다.

abstract void write(int b)

void write(byte [] b)

void write(byte[] b, int off, int len)

 

write()메서드도 마찬가지로 아래 두개의 메서드가 내부적으로 write(int b)를 사용한다.

write(int b)는 b를 출력하는 함수이다. 각각 데이터를 출력스트림으로 출력한다.

 

보조스트림

스트림의 기능을 보완하기 위한 보조스트림이 제공된다. 보조스트림은 데이터를 입출력하는것이아닌 스트림의 기능을 향상시키거나 새로운 기능을 추가할 수 있는 역할을 한다. 보조스트림을 사용하기위해서는 입출력 스트림을 먼저 생성한 후 이것을 이용하여 보조스트림을 생성해야한다.

 

FileInputStream fis = new FileInputStream("test.txt");

BufferedInputStream bis = new BufferedInputStream(fis);

bis.read();

 

test파일을 읽는 코드이다.

파일 입력 스트림인 FileInputStream에 성능 향상을 위한 보조스트림 BufferedInputStream을 생성해준 경우이다. 보조스트림역시 InputStream과 OutputStream의 자손이므로 입출력방법이 동일하다.

 

보조스트림의 종류

FilterInputStream, FilterOutputStream : 필터를 이용한 입출력 처리

BufferedInputStream. BufferedOutputStream : 버퍼를 이용한 입출력 처리

DataInputStream, DataOutputStream : int, float와 같은 기본형 단위(primitive type)로 데이터를 처리하는 기능

SequenceInputStream : 두개의 데이터를 하나로 연결

LineNumberInputStream : 읽어 온 데이터의 라인 번호를 카운트

ObjectInputStream, ObjectOutputStream : 데이터를 객체단위로 읽고 쓰는데 사용. 주로 파일을 이용하며 객체 직렬화

PrintStream : 버퍼를 이용하며, 추가적인 print관련 기능

PushbackInputStream : 버퍼를 이ㅛㅇ해서 읽어온 데이터를 다시 되돌리는 기능(unread, push back to buffer)

 

문자기반 스트림 Reader, Writer

자바에서는 문자형인 char형이 2byte이기 때문에 바이트 기반의 스트림으로 2byte인 문자를 처리하는데 어려움이 있다.

2byte문자를 처리하기 위한 스트림이 문자기반 스트림 Reader와 Writer이다.

문자데이터를 입출력 할때는 바이트기반 스트림 대신 문자기반 스트림을 사용하는것이 좋다.

 

문자열 파일

입력스트림 - FileReader

출력스트림 - FileWriter

문자열 메모리(char 배열)

입력스트림 - CharArrayReader

출력스트림 - CharArrayWriter

프로세스(프로세스간 문자열 통신)

입력스트림 - PipedReader

출력스트림 - PipedWriter

문자열 버퍼 스트림

입력스트림 - StringReader

출력스트림 - StringWriter

 

Reader메서드

int read()

int read(char[] cbuf)

abstract int read(char[] cbuf, int off, int len)

 

InputStream의 read와 비슷하지만 Reader의 read()는 char배열을 인자로받아 처리한다.

 

Writer메서드

void write(int c)

void write(char[] cbuf)

abstract void write(char[] cbuf, int off, int len)

void write(String str)

void write(String str, int off, int len)

 

Writer의 write메서드의 인자로 char[]과 String을 받아서 처리한다.

 

문자열 기반 보조스트림

BufferedReader, BufferedWriter : 문자열기반 버퍼를 사용한다.

FilterReader, FilterWriter : 문자열기반 필터를 사용한다.

LineNumberReader : 읽어온 문자 데이터 라인 번호를 카운트한다.

PrintWriter : 버퍼를 이용하여, 추가적인 출력기능

pushbackReader : 버퍼를 이용해서 읽어온 읽어온 데이터를 다시 되돌린다.

 

728x90

'Programming > JAVA' 카테고리의 다른 글

바이트기반 보조스트림  (0) 2021.09.11
바이트 기반 스트림  (0) 2021.09.10
그룹화와 분할 - groupingBy(), partitioningBy()  (0) 2021.09.02
collect()  (0) 2021.09.02
스트림의 최종연산  (0) 2021.09.01
728x90

그룹화와 분할

그룹화 : 스트림의 요소를 특정 기준으로 그룹화 하는것

분할 : 스트림의 요소를 두가지, 지정한 조건에 일치하는 그룹과 일치하지 않는 그룹으로 분할하는것

 

그룹화 메서드 groupingBy()

Collector groupingBy(Function classifier)

Collector groupingBy(Function classifier, Collecctor downstream)

Collector grooupingBy(Function classifier, Supplier mapFactory, Collector downstream)

 

분할 메서드 partitioningBy()

Collector partitioningBy(Predicate predicate)

Collector partitioningBy(Predicate predicate, Collecctor downstream)

 

그룹화인 groupingBy()의 경우 Function을 매개변수로 받고 분할인 partitioningBy()의 경우 Predicate를 매개변수로 받는다. 스트림을 두개의 그룹으로 나눠야하는 경우 partitioningBy()가 더 빠르다.

그룹화와 분할의 결과는 Map에 담겨 반환된다.

 

partitioningBy()에 의한 분류

위처럼 코드가 있을경우 partitioningBy() 사용예시이다.

Map<Boolean, List<Student>> stuBySex = stuStream.collect(partitioningBy(Student::isMale));

List<Student> maleStudent = stuBySex.get(true);

List<Student> femaleStudent = stuBySex.get(flase);

각각 남자학생일경우, 여학생일 경우 List이다. Student객체에 boolean으로 초기화 되어있는 값을 확인하여 partitioningBy에서 분할한다.

 

성별 학생수 구하기

Map<Boolean, Long> stuNumBySex = stuStream.collect(partitioningBy(Student::isMale, counting());

System.out.println("남학생 수 : "+ stuNmBySex.get(true));

System.out.println("여학생 수 : "+ stuNmBySex.get(false));

 

성별 1등학생 구하기

Map<Boolean, Optional<Stuudent>> topScoreBySex = stuStream

                                       .collect(partitioningBy(Student::isMale,maxBy(comparingInt(Student::getScore)));

System.out.println("남학생 수 : "+ topScoreBySex.get(true));

System.out.println("여학생 수 : "+ topScoreBySex.get(false));

 

150점 아래 학생들만 출력

Map<Boolean, Map<Boolean, List<Student>>> failedStuBySex = stuStream

                      .collect(partitioningBy(Student::isMale, partitioningBy(s -> s.getScore() < 150)));

List<Student> failedMaleStu = failedStuBySex.get(true).get(true);

List<Student> failedFemaleStu = failedStuBySex.get(false).get(true);

 

groupingBy()에 의한 분류

학생들을 반별로 그룹짓기

Map<Integer, List<Student>> stuByBan = stuStream.collect(groupingBy(Student::getBan));

 

groupingBy()로 그룹화를 하면 기본적으로 List<T>에 값을 담는다.

toList()를 매개변수로 넣어주어도되고 필요에 따라 toSet()이나 toCollection(HashSet::new) 등을 사용할 수 있다.

ex)

Map<Integer, List<Student>> stuuByBan = stuStream.collect(groupingBy(Student::getBan, toList()));

Map<Integer, HashSet<Student>> stuByHak = stuStream

                                            .collect(groupingBy(Studuent::getHak,toCollection(HashSet::new)));

분할과 그룹화는 많이써봐야할것같다. 이해는 되지만 사용하지않은 상황에서 응용이 어렵게 느껴진다.

728x90

'Programming > JAVA' 카테고리의 다른 글

바이트 기반 스트림  (0) 2021.09.10
자바의 입출력  (0) 2021.09.10
collect()  (0) 2021.09.02
스트림의 최종연산  (0) 2021.09.01
Optional  (0) 2021.08.25
728x90

collect() 

collect()는 스트림의 요소를 수집하는 최종연산이다.

collect에는 어떻게 수집할 것인가에 대한 방법이 필요하는데 이러한 방법을 정의한것이 컬렉터(collector)이다.

 

컬렉터는 Collector 인터페이스를 구현한 것으로, 직접 구현하거나 미리 작성된 것을 사용할 수 있다. 

Collectors클래스는 미리 작성된 다양한 컬렉터를 반환하는 static메서드를 가지고 있다.

 

스트림의 요소를 수집하는 collect()의 매개변수로는 수집하는 방법인 컬렉터가 사용된다.

해당 컬렉터는 Collector 인터페이스를 통해 구현하거나, Collectors클래스의 static메서드를 통해 얻을 수 있다.

 

collect() : 스트림의 최종연산, 매개변수로 컬렉터가 필요하다.

Collector : 인터페이스로 컬렉터를 구현할 때 사용한다.

Collectors : 클래스이다. 미리작성된 static메서드로 컬렉터를 제공한다.

 

collect() 선언

Object collect(Collector collector) //Collector인터페이스를 구현한 객체를 매개변수로 사용한다.

Object collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner)

 

스트림을 컬렉션과 배열로 변환 - toList(), toSet(), toMap(), toCollection(), toArray()

스트림의 모든 요소들을  컬렉션에 수집하기 위한 메서드들이다.

List<String> names = stuStream.map(Student::getName).collect(Collectors.toList());

Student스트림의 모든 요소들의 이름을 List로 수집한 코드이다.

 

ArrayList<String> list = names.stream().collect(Collectors.toCollection(ArrayList::new));

List<String>을 ArrayList로 할당한 코드이다.

List나 Set은 toList(), toSet()이 있지만 다른 컬렉션을 사용하고 싶은 경우 Collectors의 toCollection()에 해당 컬렉션 생성자를 넣어주면 가능하다.

 

toMap()

Map<String, Person> map = personStream.collect(Collectors.toMap(p->p.getRegId(), p->p));

toMap의 경우 key와 value를 각각 지정해 주어야한다.

 

스트림에 지정된 요소들을 배열로 저장하는 메서드 toArray()

toArray()에 매개변수로 해당 타입의 생성자 참조를 넣어주어야한다. 매개변수를 작성하지 않을시 반환되는 배열의 타입은 Object[] 이다.

Student[] stuNames = studentStream.toArray(Student[]::new);

Object[] stuNames = studentStream.toArray();

 

통계 - counting(), summingInt(), averagingInt(), maxBy(), minBy()

개수, 합, 평균, 최대값, 최솟값등을 반환하는 메서드이다.

 

ex)

long count = stuStream.count();

long count = stuStream.collect(Collectors.counting());

 

long totalScore = stuStream.mapToInt(Student::getTotalScore).sum();

long totalScore = stuStream.collect(summingInt(Student::getTotalScore));

 

OptionalInt topScore = studentStream.mapToInt(Student::getTotalScore).max();

Optional<Student> topStudent = stuStream.max(Comparator.comparingInt(Student::getTotalScore));

Optional<Student> topStudent = stuStream.collect(maxBy(Comparator.comparingInt(Student::getTotalScore));

 

IntSummaryStatics stat = stuStream.mapToInt(Student::getTotalScore).summaryStatistics();

IntSummaryStatics stat = stuStream.collect(summarizingInt(Student::getTotalScore));

 

위 예시는 collect를 사용한경우와 사용하지않은 경우를 비교한 것이다. collect사용법으로 알아두면 좋을것 같다.

 

리듀싱 - reducing()

리듀싱 선언

Collector reducing(BinaryOperator<T> op)

Collector reducing(T identity, BinaryOperator<T> op)

Collector reducing(U identity, Function<T, U> mapper, BinaryOperator<U> op)

 

사용예시

IntStream intStream = new Random().ints(1,46).distinct().limit(6);

 

OptionalInt max = intStream.reduce(Integer::max);

Optional<Integer> max = intStream.boxed().collect(reducing(Intger::max));

//intStream을 boxed()를 통해 Stream<Integer>로 변경한다.그래야 Stream에 선언된 collect를 사용할 수 있다.

 

long sum = intStream.reduce(0, (a,b) -> a+b);

long sum = intStream.boxed().collect(reducing(0,(a,b)->a+b));

 

int grandTotal = stuStream.map(Student::getTotalScore).reduce(0, Integer::sum);

int grandTotal = stuStream.collect(reducing(0, Student::getTotalScore, Integer::sum));

 

문자열 결합 - joining()

스트림의 모든요소를 하나의 문자열로 결합하여 반환한다. 구분자 지정및 접두사, 접미사 지정도 가능하다.

스트림의 요소가 문자열이 아닌경우에는 map()을 통해 문자열로 변환해주어야한다.

 

ex))

String studentNames = stuStream.map(Student::getName).collect(joining());

String studentNames = stuStream.map(Student::getName).collect(joining(","));

String studentNames = stuStream.map(Studednt::getName).collect(joining(",","[","]");

 

728x90

'Programming > JAVA' 카테고리의 다른 글

자바의 입출력  (0) 2021.09.10
그룹화와 분할 - groupingBy(), partitioningBy()  (0) 2021.09.02
스트림의 최종연산  (0) 2021.09.01
Optional  (0) 2021.08.25
스트림(Stream)의 중간연산  (0) 2021.08.25
728x90

스트림의 최종연산

스트림을 다루는데 중간연산과 최종연산이 사용된다.

최종 연산의 경우 스트림의 요소를 소모해서 결과를 만들어내기 때문에 최종연산 이후 스트림은 없어진다.

 

forEach()

void forEach(Consumer<? super T> action)

반환타입이 void이며 스트림의 요소들을 출력하는 용도로 많이 사용되는 최종연산이다.

 

allMatch(), anyMatch(), noneMatch(), findFirst(), findAny() - 조건검사

지정한 조건에 일치하는 스트림 요소가 있는지, 조건에 모두 일치하는지, 조건에 모두 일치하지 않는지 등을 확인하는데 사용되는 최종연산이다.

 

boolean allMatch(Predicate <? super T> predicate) : 조건식에 모든 요소가 일치하면 true반환

boolean anyMatch(Predicate <? super T> predicate) : 어떤 요소하나라도 조건에 일치하면 true반환

boolean noneMatch(Predicate<? super T> predicate) : 조건식에 모든 요소들이 일치하지 않는다면 true반환

 

filter()연산과 함께 사용되는 findFirst(), findAny()가 있다.

filter()를 적용하여 조건식에 맞는 요소가 있는지 확인할 때 사용한다.

findAny와 findFirst()는 반환 타입이 Optional<T>이며, 스트림의 요소가 없을 때는 비어있는 Optional 객체를 반환한다.

 

count(), sum(), average(), max(), min() - 통계

스트림의 요소들의 통계에 대한 정보를 얻을 수 있는 연산이다. 요소의 개수, 요소들의 합, 평균, 가장큰값, 가장 작은 값 등을 얻을 수 있다.

long count()

Optional<T> max(Comparator<? super T> comparator)

Optional<T> min(Comparator<? super T> comparator)

 

리듀싱 - reduce()

스트림의 요소를 하나씩 줄여나가면서 연산을 수행하고 모든 요소들을 소모하여 나온 결과를 반환한다.

Optional<T> reduce(BinaryOperator<T> accumulator)

두가지 요소를 연산하여 결과를 반환한다.

 

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

-> 초기값(identity)를 가지고 연산(accumulator)를 수행한다.

U reduce(U identity, accumulator, BinaryOperator<U> combiner)

->combiner는 병렬 스트림에 의해 처리된 결과를 합할 때 사용한다.

 

reduce 사용예시

int count = intStream.reduce(0, (a,b)-> a+1);

int sum = intStream.reduce(0,(a,b)->a+b);

int max = intStream.reduce(Integer.MIN_VALUE, (a,b) -> a>b ? a: b);

int min = intStream.reduce(Integer.MAX_VALUE, (a,b) -> a < b? a:b);

 

a에 초기값이 대입되고 스트림요소들이 차례대로 b에 대입된다. 이후 연산결과가 a에 대입되며 b에 새로운요소가 들어오게된다.

최종연산 count()와 sum()의 경우 내부적으로 reduce()를 사용한다.

 

reduce의 반환타입은 Optional이다. IntStream의 reduce의 경우 반환 타입이 OptionalInt이다. 

OptionalInt에 저장된 값을 꺼낼때는 getAsInt()메서드를 사용하면된다.

ex)

OptionalInt max = intStream.reduce(Integer::max);

int maxValue = max.getAsInt();

 

 

728x90

'Programming > JAVA' 카테고리의 다른 글

그룹화와 분할 - groupingBy(), partitioningBy()  (0) 2021.09.02
collect()  (0) 2021.09.02
Optional  (0) 2021.08.25
스트림(Stream)의 중간연산  (0) 2021.08.25
스트림(Stream)  (0) 2021.08.24
728x90

Stream의 최종연산 결과가 종종 Optional타입인 경우가 있다.

Optional은 Stream의 최종연산결과를 다룰때 사용된다.

Optional<T>는 T타입의 객체 를 감싸는 래퍼 클래스이다. Optional타입의 객체에는 모든 타입의 참조변수를 담을 수 있다.

 

public final class Optional<T>

{

     private final T value;

      ...

}

 

Optional타입의 객체로 최종연산의 결과를 반환하는경우 Optional객체에 결과를 담아서 반환하는 것이다.

Optional객체의 경우 null을 결과로 받더라도 NullPointerException을 발생시키지 않아 유용하다.

 

Optional객체 생성

Optional객체를 생성할 때는 of() 또는 ofNullalble()을 사용한다.

ex)

String str = "abc";

Optional<String> optVal = Optional.of(str);

Optional<String> optVal = Optional.of("abc");

Optional<String> optVal = Optional.of(new String("abc"));

 

참조변수가 null일 가능성이있다면 of()대신 ofNullable()을 사용해야한다.

of()의경우 NullPointerException을 발생시키기 때문이다.

 

Optional객체 값 가져오기

get() : Optional 객체에 저장된 값을 가져올 때 사용하는 메서드이다.

orElse() : 값이 null일 경우 NoSuchElementException이 발생하는데 이것을 방지하기 위한 메서드

T orElseGet(Supplier<? extends T> other) : null을 대체할 값을 반환하는 람다식을 지정할 수 있다.

T orElseThrow(Supplier<? extends X> exceptionSupplier) : null일 때 지정된 예외를 발생시킬 수 있다.

 

Optional객체도 filter(), map(), flatMap()을 사용할 수 있다. 단 Optional의 map, flatMap의 반환 타입은 Stream이 아닌 Optional이다. 

 

isPresent()는 Optional객체의 값이 null이면 false를, 아니면 true를 반환한다.

 

Optional<T>를 반환하는 메서드 목록

Optional<T> findAny()

Optional<T> findFirst()

Optional<T> max(Comparator<? super T> comparator)

Optional<T> min(Comparator<? super T> comparator)

Optional<T> reduce(BinaryOperator<T> accumulator)

 

Optional 기본형

OptionalInt, OptionalLong, OptionalDouble로 각 기본형을 다루는 Optional들이 있다.

 

IntStream 에 정의된 메서드 목록

OptionalInt findAny()

OptionalInt findFirst()

OptionalInt reduce(IntBinaryOperator op)

OptionalInt max()

OptionalInt min()

OptionalDouble average()

 

 

Optional을 다루는 예제이다.

728x90

'Programming > JAVA' 카테고리의 다른 글

collect()  (0) 2021.09.02
스트림의 최종연산  (0) 2021.09.01
스트림(Stream)의 중간연산  (0) 2021.08.25
스트림(Stream)  (0) 2021.08.24
다양한 함수형 인터페이스 & 메서드 참조  (0) 2021.08.22
728x90

스트림의 중간연산

스트림 자르기 - skip(), limit()

skip()메서드와 limit()메서드는 스트림을 자르는데 사용한다.

Stream<T> skip(long n) : 스트림의 요소 처음부터 n개만큼 삭제한다.

Stream<T> limit(long maxSize) : 스트림의 요소중 maxSize만 남기고 제거한다.

 

스트림의 요소 걸러내기 - filter(), distinct()

Stream<T> filter(Predicate<? super T> predicate) : 주어진 조건(Predicate)에 해당하지않는 요소들을 제거한다.

Stream<T> distinct() : 중복되는 요소들을 제거하여 하나만 남긴다.

 

정렬 - sorted()

스트림을 정렬할 때 사용하는 sorted()메서드 이다.

Stream<T> sorted()

Stream<T> sorted(Comparator<? super T> comparator)

 

sorted()메서드는 매개변수로 주어지는 Comparator로 스트림을 정렬한다. Comparator를 지정하지않으면 스트림 요소의 기본 정렬 기준으로 정렬된다.

 

주어진 Student스트림을 정렬하는 예제이다. Ban을 기준으로 정렬하고 naturalOrder()를 통해 클래스에 정의된 compareTo 기준(totalScore)으로 정렬한다. 이후 각각 값들을 하나씩 출력한다.

변환 - map()

map() : 스트림에 저장된 값중 원하는 필드만 뽑아내거나 특정 형태로 변환해야 할 때 사용하는 메소드이다.

Stream<R> map(Function<? super T, ? extends R> mapper)

map()은 중간연산이기 때문에 filenameStream을 보면 최종연산으로 forEach를 사용한다.

fileStream을 보면 map(File::getName)을 통해 Stream<String>으로 변경하고, filter를 통해 확장자가 있는지 확인한다.

이후 확장자만 잘라내기위해 .이후의 값들을 확인. ->모든 확장자 글자들을 대문자로 변경 -> 중복제거 -> 출력 순으로 진행한다.

조회 - peek()

중간연산으로 연산중 결과를 확인할 수 있는 메서드이다.

연산사이에 삽입하여 연산의 결과가 올바르게 나왔는지 확인 가능하다.

중간연산 사이에 filename과 extension을 출력하여 결과를 확인한다.

Stream의 연산의경우 최종연산이 호출되면 중간연산들을 수행하기때문에 하나씩 출력된다.

 

mapToInt(), mapToLong(), mapToDouble()

map()은 연산의 결과로 Stream<T> 타입의 스트림을 반환하는데, 스트림의 요소를 숫자로 변환하는 경우 IntStream과 같은 기본형 스트림으로 변환하는것이 더 유용하다.

기본형 스트림으로 변환하기 위한 메서드들이다.

IntStream mapToInt(ToIntFunction<? super T> mapper);

DoubleStream mapToDouble(ToDoubleFunction <? super T> mapper);

LongStream mapToLong(ToLongFunction<? super T> mapper)

 

IntStream과 같은 기본형 스트림의 메서드

int sum() : 스트림의 모든요소의 합

OptionalDouble average() : sum() / (double)count()

OptionalInt max() : 스트림 요소중 최대값

OptionalInt min() : 스트림 요소중 최소값

 

sum()이나 average()같은 메소드들은 최종연산이기 때문에 한번 호출할경우 스트림이 닫힌다.

연속적으로 해당 메서드들을 호출해야할 경우 summaryStatistics()메서드를 통해서 IntSummaryStatistics형 자료를 따로 생성해주는것이 좋다.

IntStream을 통해 SummaryStatistics()를 호출할 경우 위처럼 getSum(), getAverage()등을 연속적으로 호출이 가능하다.

flatMap() - Stream<T[]>를 Stream<T>로 변환

스트림의 요소가 배열이거나 map()의 연산결과가 배열인 경우 스트림의 타입이 Stream<T[ ]>으로 생성된다.

이러한 경우 Stream<T>로 다루는것이 더 편한데, 이러한 경우 map()이 아닌 flatMap()을 사용해주면 된다.

flatMap을 통해 하나의 스트림으로 정리된것을 알 수 있다.

728x90

'Programming > JAVA' 카테고리의 다른 글

스트림의 최종연산  (0) 2021.09.01
Optional  (0) 2021.08.25
스트림(Stream)  (0) 2021.08.24
다양한 함수형 인터페이스 & 메서드 참조  (0) 2021.08.22
람다식(Lambda expression)  (0) 2021.08.21
728x90

스트림

스트림 : 다양한 데이터소스를 표준화된 방법으로 다루기 위한 것.

스트림은 데이터를 추상화하고, 데이터를 다루는데 자주 사용되는 메서드들을 정의해 놓았다.

 

스트림은 스트림 생성 -> 중간연산(0~n번) ->최종연산(1번) 으로 사용된다.

스트림의 특징

1.스트림은 데이터 소스를 변경하지 않는다.

스트림은 데이터를 읽기만 할 뿐, 데이터 소스를 변경하지 않는다. 데이터를 정렬하거나 중복제거 등은 가능하지만 데이터 값을 변경하는것은 불가능하다.(Read only)

 

2.스트림은 1회용이다.

스트림의 경우 최종연산을 하게되면 해당 스트림이 닫힌다. 닫힌 스트림은 사용할 수 없으며 사용시 에러가 발생한다.

 

3. 스트림의 작업은 내부 반복으로 처리한다.

내부 반복 : 반복문을 메서드의 내부에 숨길 수 있다.

forEach()의 경우 스트림에 정의된 메서드로 매개변수에 대입된 람다식을 데이터 소스의 모든 요소에 적용시켜주는 메서드이다.

 

4.스트림의 연산

1.중간연산 : 연산 결과가 스트림을 반환하는 연산. 스트림에 연속적으로 연산가능

2.최종연산 : 연산 결과가 스트림이 아닌 연산. 스트림의 요소를 소모하기 때문에 스트림당 한번만 수행이 가능

최종연산 이후에는 스트림이 닫히기 때문에 최종연산은 한번만 가능하다.

 

연산목록

중간연산

Stream<T> distinct() : 중복 제거

Stream<T> filter(Predicate<T> predicate) : 조건에 안맞는 요소 제외

Stream<T> limit(long maxSize) : 스트림의 개수 지정

Stream<T> skip(long n) : 스트림의 일부를 건너 뛴다.

Stream<T> peek(Consumer<T> action) : 스트림의 요소에 작업 수행

Stream<T> sorted()

Stream<T> sorted(Comparator<T> comparator)

:스트림의 요소 정렬

 

Stream<R> map(Function<T,R> mapper) : 스트림의 요소 변환

 

최종연산

void forEach(Consumer<? super T> action) : 각 요소에 지정된 작업 수행

long count() : 스트림 요소 개수 반환

Optional<T> max(Comparator<? super T> comparator) : 스트림의 최대값 반환

Optional<T> min(Comparator<? super T> comparator) : 스트림의 최소값을 반환

Optional<T> findAny(), Optional<T> findFirst() : 스트림의 요소 하나를 반환

boolean allMatch(Predicate<T> p) : 모든 요소가 조건을 만족하는지 확인

boolean anyMatch(Predicate<T> p) : 하나의 요소라도 해당 조건을 만족하는지 확인

boolean noneMatch(Predicate<> p) : 모든 요소가 조건을 만족하지 않으면 true

Object[] toArray(), A[] toArray(IntFunction<A[ ]> generator) : 스트림의 모든 요소를 배열로 변환

Optional<T> reduce(BinaryOperator<T> accumulator)

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

U reduce(U identity, BiFunction<U, T, U> accumulator, BinaryOperator<U> combiner)

 : 스트림의 요소를 하나씩 줄여 가면서 계산한다.

R collect(Collector<T,A,R> collector)

R collect(Supplier<R> supplier, BiConsumer<R, T> accumulator, BiConsumer<R, R> combiner)

: 스트림의 요소를 수집한다. 주로 요소를 그룹화 하거나 분할한 결과를 컬렉션에 담아 반환하는데 사용된다.

 

스트림 연산에서 중요한점은 최종연산이 수행되기 전까지 중간 연산은 수행되지않는다.

중간연산을 호출하더라도 즉각적인 연산이 수행되는것이 아니다.

최종 연산이 수행되어야 스트림의 요소들이 중간연산을 거쳐 최종 연산에서 소모된다.

 

5.기본형 스트림

스트림은 기본적으로 Stream<T> 형태이지만, 오토박싱&언박싱으로 인한 비효율을 줄이기 위해 데이터 소스의 요소를 기본형으로 다루는 스트림, IntStream, LongStream, DoubleStream이 제공된다. 

일반적으로 Stream<Integer> 보다 IntStream을 사용하는것이 더 성능이 좋다. 또한 IntStream과 같이 기본형이 적용된 스트림의 경우 기본형에 대한 작업에 유용한 메서드들이 더 존재한다.

 

6.병렬 스트림

스트림으로 데이터를 처리할 때 장점이 병렬처리가 쉽다는 것이다. 

병렬스트림으로 전환하는 경우 parallel()이라는 메서드를 사용하는데 해당 메서드를 호출하면 스트림이 병렬 스트림으로 전환된다. 병렬로 처리되지 않게 하려면 sequential()을 호출하면 된다.

 

 

스트림 만들기

스트림의 소스가 될 수 있는 대상은 배열, 컬렉션, 임의의 수 등이 있다. 이러한 소스들을 이용하여 스트림을 만들 수 있다.

 

컬렉션

Collection에 stream()메서드라 정의되어 있어 해당 메소드를 호출하면 스트림을 생성할 수 있다.

stream()메서드는 해당 컬렉션을 소스로 하는 스트림을 반환한다.

Stream<T> Collection.stream()

ex)

List<Integer> list = Arrays.asList(1,2,3,4,5);

Stream<Integer> intStream = list.stream();

 

배열

배열을 소스로하는 스트림을 생성할 때는 Stream과 Arrays에 정의되어있는 static메서드로 생성할 수 있다.

Stream<T> Stream.of(T ... values)

Stream<T> Stream.of(T[])

Stream<T> Arrays.stream(T[])

Stream<T> Arrays.stream(T[ ] array, int startInclusive, int endExclusive)

 

특정 범위의 정수

IntStream과 LongStream은 지정한 범위의 연속된 정수를 스트림으로 생성해서 반환할 수 있다.

IntStream IntStream.range(int begin, int end)

IntStream IntStream.rangeClosed(int begin, int end)

range()는 begin ~ end-1까지의 수를 스트림으로 생성하고 rangeClosed()는 begin ~ end 까지의 수를 스트림으로 생성한다.

 

forEach()는 매개변수로 들어온 람다식의 작업을 스트림의 모든요소에 대해 수행한다.

intStream.forEach(System.out::println);

foroEach()가 스트림의 요소를 소모하면서 작업을 수행하므로 같은 스트림에 forEach()를 두 번 호출할 수 없다.

스트림의 요소를 한번 더 출력하려면 스트림을 새로 생성해야 한다.

 

임의의 수

난수를 생성하는데 사용하는 Random클래스에 난수들로 이루어진 스트림을 반환하는 메서드가 있다.

IntStream ints()

LongStream longs()

DoubleStream doubles()

 

위 메서드들이 반환하는 스트림은 크기가 정해지지 않은 '무한 스트림(infinite stream)' 이다.

스트림의 개수를 지정해주는 limit()을 함께 사용하여 무한스트림을 유한 스트림으로 만들어 주어야한다.

ex)

IntStream intStream = new Random().ints();

intStream.limit(5).forEach(System.out::println);

위처럼 무한스트림을 유한스트림으로 변경하여 5개의 정수형 난수를 출력 할수 있다.

 

IntStream ints(long streamSize)

LongStream longs(long streamSize)

DoubleStream doubles(long streamSize)

 

위 메서드들은 지정된 크기에 해당하는 '유한 스트림'을 생성하여 반환한다.

 

람다식 - iterate(), generate()

iterate()와 generate()는 람다식을 매개변수로 받아 람다식에 해당하는 결과를 요소로하는 무한 스트림을 생성하여 반환한다.

static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)

: seed값부터 시작해서 람다식 f에 의해 계산된결과를 스트림에 요소에 포함시키며 해당 결과를 다시 seed값으로 넣어 계산을 반복한다.

static <T> Stream<T> generate(Supplier<T> s)

: seed값 없이 s의 결과로만 무한 스트림을 생성한다.

 

728x90

'Programming > JAVA' 카테고리의 다른 글

Optional  (0) 2021.08.25
스트림(Stream)의 중간연산  (0) 2021.08.25
다양한 함수형 인터페이스 & 메서드 참조  (0) 2021.08.22
람다식(Lambda expression)  (0) 2021.08.21
fork & join  (0) 2021.08.20
728x90

다양한 함수형 인터페이스

java.util.function 패키지에 일반적으로 자주 쓰이는 형식의 메서드를 함수형 인터페이스로 미리 정의해 두었다. 일반적으로 많이쓰이는 이패키지의 인터페이스를 활용하면 재사용성이나 유지보수 측면에서도 좋을것이다.

 

목록

Supplier<T> : 메서드 T get() - 매개변수는 없고, 반환값만 있다. (공급자)

Consumer<T> : 메서드 void accept(T t) - Supplier와 반대로 매개변수만 있고, 반환값이 없다. (소비자)

Function<T, R> : 메서드 R apply(T t) - 일반적인 하나의 매개변수와 반환값이 있는 함수. 입력타입 T 반환타입 R (입출력 함수)

Predicate<T> : 메서드 boolean test(T t) - 조건식을 표현하는데 사용됨. 매개변수 하나, 반환타입은 boolean(조건식)

 

매개변수가 두개인 함수형 인터페이스

매개변수가 두개인 함수형 인터페이스의 이름에는 'Bi'가 붙는다.

 

BiConsumer<T, U> : 메서드 void accept(T t, U u) - 두개의 매개변수만 있고 반환값은 없는 소비자

BiPredicate<T, U> : 메서드 boolean test(T t, U u) - 조건식을 표현하고 수행하는데 사용된다. 매개변수는 두개이고 반환값은 boolean이다.

BiFunction<T,U,R> : 메서드 R apply(T t, U u) - 두개의 매개변수 t, u를 받아서 결과를 반환한다. 반환 타입은 R

 

3개이상의 매개변수를 사용하는 함수형 인터페이스를 사용하고싶다면 직접 만들어서 사용해야한다.

ex)

@FunctionalInterface

interface TriFunction<T, U, V, R>{

     R apply(T t, U u, V v);

}

 

켤렉션 프레임웍과 함수형 인터페이스

컬렉션 프레임워크의 인터페이스에 함수형인터페이스가 존재한다.

 

UnaryOperator<T> : T apply(T t) - Function의 자손, Function과 달리 매개변수와 결과의 타입이 같다.

BinaryOperator<T> : T apply(T t, T t) - BiFunction의 자손, BiFunction과 달리 매개변수와 결과의 타입이 같다.

 

메서드 목록

Collection

boolean removeIf(Predicate<E> filter) : Predicate 조건식에 일치하는 값들을 삭제

List

void replaceAll(UnaryOperator<E> operator)  : 모든 요소 UnaryOperator의 함수의 반환값으로 변환하여 대체

Iterator 

void forEach(Consumer<T> action) : 모든 요소에 작업 action을 수행

Map

V comput(K key, BiFunction<K,V> f) : 지정된 키의 값에 작업 f를 수행

V computeIfAbsent(K key, Function<K,V> f) : 키가 없으면, 작업 f를 수행 후 추가

V computeIfPresent(K key, BiFunction<K,V,V> f ) : key가 있을때, 작업 f를 수행

V merge(K key, V value, BiFunction<V,V,V> f) : 모든 요소에 병합작업 f를 수행

void forEach(BiConsumer<K, V> action) : 모든 요소에 작업 action을 수행

void replaceAll(BiFunction<K,V,V> f) : 모든요소에 치환작업 f를 수행

 

list.forEach를 사용하고 매개변수로 람다식을 주었다. list의 모든 요소를 출력한다.

2번째줄 출력은 list.removeIf()를 수행한 이후이다. x가 2의배수거나 3의배수라면 모두 삭제하는 연산이다.

3번째줄 출력은 replaceAll을 통해 모든 요소들을 10배하여 요소값을 바꾸는 것이다.

4번째줄은 forEach를 통해 맵의 모든 요소들을 출력한 것이다.

 

Function의 합성과 Predicate의 결합

Function과 Predicate에 정의된 메서드

 

Function

default <V>Function<T,V> andThen(Function<? super R, ? extends V> after)

default <V>Function<V,R> compose(Function<? super V, ? extends T> before)

static <T> Function<T,T> identity()

 

andThen의 사용예시 Function f, g가 있을 때, f.andThen(g)는 함수 f를 적용한 후 g함수를 적용한다는 의미이다.

compose는 반대로 f.compose(g)의 경우 g함수를 먼저 적용한후 f함수를 적용한다.

identity()는 항등함수로 x->x 를 나타낸다. 잘 사용하지않는다.

 

Predicate

default Predicate<T> and(Predicate<? super T> other)

default Predicate<T> or(Prerdicate<? super T> other)

default Predicate<T> negate()

static <T> Predicate<T> isEqual(Object targetRef)

 

and, or 메서드는 내부에 또다른 조건식을 필요로한다. and(조건식)의 경우 두 조건식의 and결합(&&)이고 or(조건식)의 경우 두 조건식의 or결합(||)이다.

negate()는 부정연산자(!)가 결합되는것이다.

isEqual()은 매개변수로 대상하나를 지정하고 또다른 비교대상은 test()의 매개변수로 지정하는 방식으로 사용하는데 각각의 대상이 같은지 비교한다.

 

메서드참조

람다식을 더욱 간결하게 표현하는 방법으로 람다식이 하나의 메서드만 호출하는 경우 '메서드 참조(method reference)'라는 방법으로 람다식을 간략히 할 수 있다.

 

Function<String, Integer> f = (String s) -> Integer.parseInt(s);

위같은 함수형인터페이스가 정의되었다고 가정할 때, 위식을 아래와 같이 간단하게 나타낼 수 있다.

Function<String, Integer> f = Integer::parseInt;

람다식의 일부가 생략되었다. 생략된정보들은 좌변의 함수형인터페이스 형식으로부터 얻거나 우변의 parseInt메서드호출로 알 수 있다. Function<String, Integer>는 String을 매개변수로 받고 반환값은 Integer로 반환한다는 의미이다.

해당 Function의 메서드로는 Integer클래스의 parseInt를 사용하는것을 알 수 있다.

 

3가지 경우의 메서드 참조

static메서드 참조

(x) -> ClassName.method(x) == ClassName::method

인스턴스메서드 참조

(obj, x) -> obj.method(x) == ClassName::method

특정 객체 인스턴스 메서드 참조

(x) -> obj.method(x) == obj::method

 

생성자 매서드 참조

생성자를 호출하는 람다식도 메서드 참조로 변환할 수 있는데 위와같이 하면된다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

728x90

'Programming > JAVA' 카테고리의 다른 글

스트림(Stream)의 중간연산  (0) 2021.08.25
스트림(Stream)  (0) 2021.08.24
람다식(Lambda expression)  (0) 2021.08.21
fork & join  (0) 2021.08.20
쓰레드의 동기화  (0) 2021.08.20

+ Recent posts