자바에서는 다양한 데이터 소스를 표준화된 방법으로 다루기위해 이전의 데이터소스들(List,Set,Map, 배열 등.)은
각각 성격이 달라 데이터를 다루는 방법이 모두 달랐다.
JDK 1.8버전 이후부터 추가된 Stream은 데이터를 다루는 방법을 표준화 한 기능이다
구현방식은 위의 그림과 같다 데이터소스는 스트림으로 변환되고 중간 연산은( n번가능) 최종 연산(한번)을 거쳐 결과가 만들어진다 .
스트림의 특징
-스트림은 데이터소스 (원본) 데이터를 읽기만할 뿐 (Read Only) 변경지 않는다.
List<Integer> list = Arrays.asList(3,1,5,1,4,2);
List<Integer> sortedList = list.stream().sorted().collect(Collectors.toList());
//[3, 1, 5, 1, 4, 2]
System.out.println(list);
//[1, 1, 2, 3, 4, 5]
//원본을 변경하지 않고 새로운 리스트를 만들어낸다.
System.out.println(sortedList);
-스트림은 Iterator 처럼 일회용이다. (필요하면 다시 스트림을 생성해야 한다.)
//스트림은 Iterator 처럼 일회용이다. (필요하면 다시 스트림으 생성해야 함)
//최종 연산 단계에서 스트림의 요소를 소모해버리기 때문에
//최종 연산이 끝나면 스트림은 닫혀버림
Stream<Integer> s = list.stream();
s.sorted().forEach(i-> System.out.println(i));
//에러가 난다
s.forEach(System.out::println);
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
at StreamExam.main(StreamExam.java:26)
-스트림은 최종 연산 전까지 중간연산이 수행되지 않는다.- 지연된 연산
//스트림은 최종 연산 전까지 중간연산이 수행되지 않는다.- 지연된 연산
IntStream intStream = new Random().ints(1,46);
intStream.distinct().limit(6).sorted()
.forEach(i-> System.out.println(i+","));
//메서드가 호출되었을떄 스트림 처리를 바로하는게 아니라 표시만 해두었다가
//나중에 수행한다. (지연된 연산)
-스트림은 내부 반복으로 처리된다. forEach 메서드 안에 for 문이 감춰져 있다 . 성능은 비효율 적이지만 코드가 간결 하다
-스트림의 작업을 병렬로 처리 - 병렬 스트림 지원 (parell stream)
Stream<String> stringStream = Stream.of("dd","aa","CC","ff","h");
int sum = stringStream.parallel().mapToInt(str -> str.length()).sum();
System.out.println("---------------");
System.out.println(sum);
System.out.println("---------------");
-기본형 스트림 제공 IntStream LongStream DoubleStream
//기본형 스트림 제공 - IntStream LongStream DoubleStream
//오토박싱&언박싱의 비효율이 제거됨 (Stream<Integer> 대신 IntStream 사용)
//기본형 데이터 타입에 최적화된 유용한 메서드를 제공한다.
int [] intArr = {1,2,3,4,5,};
IntStream stream = Arrays.stream(intArr);
위와 같이 기본형 배열인 intArr의 경우 Stream<Integer>를 사용하게 되면 기본형 데이터타입은 int가 Interger로 변환되면서 성능상에 문제가 일어날 수 있기 때문에 기본형 스트림을 제공한다.
스트림 작업 순서
1.스트림 만들기
- Collection 인터페이스에 stream()으로 컬렉션을 스트림으로 만들기
List<Integer> list = Arrays.asList(1,2,3,4,5);
Stream<Integer> intStream = list.stream();
int Stream.forEach(System.out::print);
//최종 연산이 끝나 스트림이 이미 닫혔기 때문에 에러가난다.
int Stream.forEach(System.out::print);
-객체 배열로부터 스트림 생성하기
//인자를 넣어서 Stream 생성am 생성
Stream<T> Stream.of(T...values) // 가변 인자
//배열을 넣어서 Stream 생성
Stream<T> Stream.of(T [])
//배열을 생성하면서 Stream 함께 생성
Stream<T> Arrays.stream(T [])
//배열을 넣고 원하는 인덱스를 시작과 끝점을 지정해서 그 인덱스의 요소만으로 Stream을 생성
Stream<T> Arrays.stream(T [] array, int startInclusive , int endExclusive)
무한 난수 스트림
public class IntStreamExam {
public static void main(String[] args) {
//난수를 무한으로 생성하는 스트림 (무한 스트림)
IntStream intStream = new Random().ints();
intStream.forEach(System.out::println);
//무한스트림은 limit()으로 잘라 주는게 좋다.
//갯수와 범위를 직접 줄 수도 있다
// 5개 나오게 하고 10 아래의 숫자
IntStream intStream2 = new Random().ints(5,10);
//limit으로 자를 수도 있다.5개만 나온다
intStream2 .limit(5);
}
}
특정 범위의 정수를 요소로 갖는 스트림 생성 (intStream, LongStream)
//1,2,3,4
IntStream intStream1 = IntStream.range(1,5);
//1,2,3,4,5
IntStream intStream4 = IntStream.rangeClosed(1,5);
2. 스트림의 중간연산
스트림 자르기
- 무한 스트림의 경우 값이 무한으로 생성되기 때문에 자르기가 필요하다.
skip(long n) - 앞에서부터 n개 건너뜀
//스트림 자르기
//skip(long n) - 앞에서부터 n개 건너뜀
IntStream intStream = IntStream.rangeClosed(1,10);
intStream.skip(3).limit(5).forEach(System.out::print);
limit(long maxSize) - maxSize 이후의 요소는 잘라냄
//limit(int maxSize) - maxSize 이후의 요소는 잘라냄
IntStream intStream1 = IntStream.rangeClosed(1, 10);
intStream1.limit(5).forEach(System.out::print);
스트림의 요소 걸러내기
filter() - 조건에 맞지 않는 요소 제거
//filter() - 조건에 맞지 않는 요소 제거
IntStream intStream3 = IntStream.rangeClosed(1,10);
//결과값 246810
intStream3.filter(i->i%2==0).forEach(System.out::print);
//중간연산이기 때문에 여러번 사용가능
IntStream intStream4 = IntStream.rangeClosed(1,10);
//결과값 2,4
intStream4.filter(i->i%2 == 0 ).filter(i->i<6).forEach(System.out::print);
distict()- 중복되는 요소를 제거
//distict()- 중복되는 요소를 제거
IntStream intStream2 = IntStream.of(1,2,2,3,3,3,4,5,5,6);
//결과값 123456
intStream2.distinct().forEach(System.out::print);
스트림 정렬
//스트림 정렬
Stream<String> strStream = Stream.of("a","b","c","C","d","e");
//결과값 Cabcde <-sorted()에 Comparator를 따로 지정하지 않으면 기본정렬 됨
//대문자가 우선으로 나온다
strStream.sorted().forEach(System.out::print);
//(s1,s2)->s1.compareTo(s2) <- 아래의 메서드 참조의 람다식
//첫번째 파라미터를 기준으로 compareTo의 파라미터를 비교해서 정렬
strStream.sorted(String::compareTo).forEach(System.out::print);
//기본 정렬에서 역순으로 정렬
//결과값 edcbaC
strStream.sorted(Comparator.reverseOrder()).forEach(System.out::print);
//대소문자 구분없이 정렬
//String.CASE_INSENSITIVE_ORDER 스트링 클래스에 정의되 있는 Comparator다
//결과값 abcCde
strStream.sorted(String.CASE_INSENSITIVE_ORDER).forEach(System.out::print);
//comparing의 파라미터인 Function을 기준으로 정렬
Stream<String> strStream = Stream.of("a","bb","ccc","C","dddd","eeeee");
//결과값 aCbbcccddddeeeee
strStream.sorted(Comparator.comparing(String::length)).forEach(System.out::print);
comparing() - comparing의 파라미터 기준으로 정렬
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class ComparingExam{
public static void main(String[] args) {
//학생 리스트 생성
List<Student> students = new ArrayList<>();
//학생 인스턴스 생성수 학생리스트에 추가
for(int i = 1; i<11; i++){
Student student = new Student("student"+i, i);
if(i%2==0){
student = new Student("student"+(i), i-1);
}
students.add(student);
}
//반 번호를 기준으로 정렬 후 출력
students.stream().sorted(Comparator.comparing(Student::getClassNum))
.forEach(student -> System.out.println(
"classNum="
+student.getClassNum()
+",studentName="
+student.getName())
);
//정렬 기준이 여러개일 때 thenComparing();
students.stream().sorted(
Comparator.comparing(Student::getClassNum)
.thenComparing(Student::getName))
.forEach(student -> System.out.println(
"classNum="
+student.getClassNum()
+",studentName="
+student.getName()));
}
}
class Student{
private String name;
private int classNum;
public String getName() {
return name;
}
public int getClassNum() {
return classNum;
}
public Student(String name, int classNum) {
this.name = name;
this.classNum = classNum;
}
}
결과값
classNum=1,studentName=student1
classNum=1,studentName=student2
classNum=3,studentName=student3
classNum=3,studentName=student4
classNum=5,studentName=student5
classNum=5,studentName=student6
classNum=7,studentName=student7
classNum=7,studentName=student8
classNum=9,studentName=student9
classNum=9,studentName=student10
Process finished with exit code 0
스트림의 요소 변환
map() - 스트림의 요소 변환
peek() - 스트림의 요소를 소비하지 않고 엿볼 수 있다.
import java.util.ArrayList;
import java.util.List;
public class StreamMapExam {
public static void main(String[] args) {
List<File> fileList = new ArrayList<>();
fileList.add(new File("a.exe"));
fileList.add(new File("b.exe"));
fileList.add(new File("c.jpg"));
fileList.add(new File("d.jpg"));
fileList.add(new File("e.png"));
fileList.add(new File("f.png"));
fileList.add(new File("g"));
fileList.add(new File("h"));
//fileList에서 이름을 꺼내고
fileList.stream().map(File::getFileName)
//확장자가 있는지 확인 없으면 거른다
.filter(s->s.contains("."))
//중간 확인
.peek(s -> System.out.println(s))
//.다음까지만 자르고
.map(s -> s.substring(s.indexOf('.')+1))
//대문자로 바꾸고
.map(String::toUpperCase)
//중복제거해서
.distinct()
//출력
.forEach(System.out::println);
}
}
class File{
private String fileName;
public String getFileName() {
return fileName;
}
public File(String fileName) {
this.fileName = fileName;
}
}
flatMap() - 스트림의 스트림을 스트림으로 변환
public class FlatMapExam {
public static void main(String[] args) {
String [] arr1= {"a","b","c"};
String [] arr2 = {"d","e","f"};
//위 두개의 배열로 스트림을 만들면 아래처럼 String [] 의 스트림이 된다.
//스트림에서 사용하는 데이터는 {"a","b","c"} ,{"d","e","f"} 이런 형태일 것이다.
Stream<String[]> arrStream = Stream.of(arr1, arr2);
//flatMap은 위와같은 배열의 타입 스트림을 flat 하게 만들어준다.
//(arr)->Arrays.stream(arr);
//flatMap의 데이터는 "a","b","c","d","e","f"의 형태이다.
//아래처럼 String stream 하나로 합쳐졌다.
Stream<String> stringStream = arrStream.flatMap(Arrays::stream);
}
}
flatMap() 활용 -단어별로 문자열 출력
import java.util.Arrays;
import java.util.stream.Stream;
public class FlatMapExam2 {
public static void main(String[] args) {
String [] arr = {"The Spring Framework is an application framework and " +
" inversion of control container for the Java platform",
" The framework's core features can be used by any Java application"};
//String [] arr 의 2개의 라인으로 Stream이 생성
Stream<String> lineArr = Arrays.stream(arr);
//1.line.split(" +")으로 공백이 하나 이상인 것마다 나눠지고 문자열 배열로 리턴된다.
//2.1번의 문자열 배열들을 flatMap으로 하나로 만들고
Stream<String> stringStream = lineArr.flatMap(line -> Stream.of(line.split(" +")));
//출력
stringStream.forEach(System.out::println);
/* 결과 - 단어 단위로 잘려서 출력되었다.
The
Spring
Framework
is
an
application
framework
and
inversion
of
control
container
for
the
Java
platform
The
framework's
core
features
can
be
used
by
any
Java
application
*/
}
}
'JAVA' 카테고리의 다른 글
JAVA 스터디 23 - 메서드 참조(method reference) (0) | 2021.03.27 |
---|---|
JAVA 스터디 22 - 컬렉션 프레임웍과 함수형 인터페이스 (0) | 2021.03.27 |
JAVA 스터디 21 - Predicate의 결합 (0) | 2021.03.27 |
JAVA 스터디 20 - 람다식 (Lamda Expression) (0) | 2021.03.27 |
JAVA 스터디 18 - 애노테이션 (0) | 2021.03.26 |