자바 프로그래밍 시 , 데이터는 배열이나 컬렉션 클래스에 저장해서 사용
-> 빠른 작업을 위해 표준화된 방법으로 데이터를 가져온 후 연산
Iterator 와 Stream을 사용하여 출력하는 과정비교public class StreamTest {
public static void main(String[] args) {
List<String> fruits = Arrays.asList("apple", "orange", "banana");
for (String result : fruits) {
System.out.println(result);
}
//stream 을 사용할 경우
List<String> fruits2 = Arrays.asList("apple", "orange", "banana");
Stream<String> fruits2stream = fruits2.stream();
fruits2stream.forEach(System.out::println);
}
}
람다식을 사용해서 간결하게 요소들을 다룰 수 있다.| 리턴 타입 | 데이터 소스 |
|---|---|
| Stream\ | 컬렉션 |
| Stream\ | 배열 |
| IntStream | int 범위 |
| LongStream | long 범위 |
| Stream\ | 디렉토리 |
| Stream\ | 텍스트 파일 |
| IntStream | 랜덤 수 |
public class CollectionToStreamTest{
public static void main(String[] args){
List<Student> studentList = new ArrayList<>();
studentList.add(new Student("홍길동", 2));
studentList.add(new Student("이순신", 1));
studentList.add(new Student("박찬호", 2));
studentList.add(new Student("손흥민", 3));
studentList.add(new Student("동길동", 4));
Stream<Student> stream = studentList.stream();
//-> ArrayList를 Stream으로 리턴한다.
stream.forEach(i->System.out.println(i.getName()));
}
}
public class ArrayToStreamTest{
public static void main(String[] args){
Student[] stdArr = {
new Student("홍길동", 2),
new Student("이순신", 1),
new Student("박찬호", 2),
new Student("손흥민", 3),
new Student("동길동", 4)
}
Stream<Student> stream = Arrays.stream(stdArr);
//-> 배열을 스트림 객체로 리턴한다.
stream.forEach(i->System.out.println(i.getName()));
int[] intArr = {1,2,3,4,5};
IntStream intStream = Arrays.stream(intArr);
intStream.forEach(i->System.out.println(i+ ", "));
//-> 정수 타입 배열은 IntStream 으로 빠르게 처리할 수 있다.
}
}
IntStream intStream1 = IntStream.range(0, 100) // -> 100이 포함된다.
IntStream intStream2 = IntStream.rangeClosed(0, 100) // -> 100이 포함된다.
📌 0 ~ 100 범위의 스트림을 얻을 수 있다.
IntStream intStream1 = new Random().ints(5);
//-> 5개 임의 정수로 스트림을 얻는다.
IntStream intStream2 = new Random().ints(1, 50).limit(10);
//-> 1~50 사이의 정수를 얻은 후, 10개 출력한다.
DoubleStream doubleStream1 = new Random().doubles(5);
//-> 5개 임의의 실수로 스트림을 얻는다.
📌 결과
-132217446
-270009177
158943372
2003339194
12, 7, 13, 25, 10, 49, 46 26, 22, 19, //-> limit(10)에 의해 10개만 출력
0.123451231
0.315451231
0.434123131
0.412331332
0.445457131
📌
Files클래스의 정적 메서드lines()로 파일에서 스트림을 얻을 수 있다.
Path path = Paths.get(FileToStreamTest.class.getResource("Student.txt").toURI());
//-> 파일의 절대경로를 얻고 Path 객체를 얻는다.
Stream<String> fileStream = Files.lines(path, Charset.defaultCharset());
//-> 운영체제의 기본 인코딩을 지정 후, 파일로부터 스트림을 얻는다.
📌 자바 스트림은 중간 처리와 최종 처리 스트림으로 나눌 수 있다.
List<Student> studentList = Arrays.asList(
new Student("kim", 1, 100),
new Student("lee", 2, 80),
new Student("park", 3, 70),
new Student("son", 4, 60)
);
int average =
studentList.stream()
.filter(i->i.getGrade == 1)
.mapToInt(Student::getScore)
.average();
//-> 1학년 점수 평균
Student 컬렉션에서 스트림을 얻는다.Stream 인터페이스의 중간 처리 메서드인 filter() 메서드로 학년별로 학생들을 분류해서Stream 인터페이스의 중간 처리 메서드인 mapToInt() 메서드로 학생들의 점수로 이루어진average()메서드로 시험 점수 평균을 구한 후 , 출력한다.| 리턴타입 | 메서드 | 설명 |
|---|---|---|
| Stream | distinct() | 중복제거 |
| Stream | filter(Predicate) | 조건식을 만족하는 요소만 선택 |
| IntStream | filter(IntPredicate) | 조건식을 만족하는 요소만 선택 |
| DoubleStream | filter(DoublePredicate) | 조건식을 만족하는 요소만 선택 |
📌
disltinct()와filter()메서드 사용하기
List<String> fruitList = Arrays.asList("apple", "banana", "mango", "Strawberry", "banana", "mango");
fruitList.stream()
.distinct() //-> 과일 이름의 중복을 제거
.forEach(System.out::println);
fruitList.stream()
.filter(i->i.length() < 6) //-> 문자 개수가 6개보다 적은 과일만 선택
.forEach(System.out::println);
fruitList.stream()
.distinct()
.filter(i->i.length() < 6)
.forEach(System.out::println);
//-> 중복 과일 제거 후, 문자 개수가 6개보다 적은 과일만 선택
📌매핑 메서드는 스트림의 요소를 다른 요소들로 변환하는 작업을 수행한다.
flatMapXXX()메서드📌
flatMapping메서드는 한 개의 요소를 여러 개의 요소로 변환(1: n) 할 때 사용한다.
| 리턴타입 | 메서드 | 설명 |
|---|---|---|
| Stream\ | flatMap(Function\<T, Stream\>) | 객체 T를 Stream\로 변환 |
| DoubleStream | flatMap(DoubleFunction\) | double 타입을 DoubleStream으로 변환 |
| IntStream | flatMap(IntFunction\) | int 타입을 IntStream 으로 변환 |
| DoubleStream | flatMapToDouble\<Function\<T,DoubleStream> | 객체 T를 DoubleStream 으로 변환 |
List<String> studentList = Arrays.asList("홍길동 2 80", "이순신 3 77", "손흥민 2 80");
studentList.stream()
.flatMap(i-> Arrays.stream(i.split(" ")))
.forEach(System.out::println);
📌
mapXXX메서드는 스트림의 요소를 다른 타입의 요소로 변환(1: 1)시 사용한다.
각각의 데이터 타입을 다른 타입으로 변환하는 더 많은mapXXX()메서드가 API문서에 설명되어 있다.
| 리턴 타입 | 메서드 | 설명 |
|---|---|---|
| IntStream | mapToInt(ToIntFunction\) | 객체 T를 int 타입으로 변환 |
| DoubleStream | mapToDouble(ToDoubleFunction\) | 객체 T 를 double 타입으로 변환 |
| LongStream | mapToLong(ToLongFunction\) | 객체 T를 long 타입으로 변 |
studentList.stream()
.mapToInt(Student::getScore)
.forEach(System.out::println);
//-> 각각의 student 객체의 점수만 얻어와서 스트림을 얻는다.
📌정렬 메서드는 스트림의 요소를 정렬하는 기능을 제공한다.
기본타입이거나 문자열인 경우는 자동으로 정렬하지만, 요소들이 객체타입인 경우
Comparator를 구현해서sorted()매서드의 매개변수로 전달해야 한다.
| 리턴 타입 | 메서드 | 설명 |
|---|---|---|
| Stream\ | sorted() | 스트림 요소들을 정렬 |
| Stream\ | sorted(Comparator \) | 스트림 요소들을 Comparator 에 따라 정렬 |
| DoubleStream | sorted() | double 요소들을 오름차순으로 정렬 |
| IntStream | sorted() | int 요소들을 오름차순으로 정렬 |
💡다음은 기본 타입 데이터를 정렬 후 출력하는 예제이다.
List<Integer> scoreList = Arrays.asList(77,67,88,99,100);
scoreList.stream()
.sorted()
.forEach(System.out::println);
💡다음은
Comparator를 이용해서Student객체를 시험 점수순으로 정렬해서 출력하는 예제이다.
public class student implements Comparable<Student>{
private String name;
...
private int Score;
@Override
public int compareTo(Student O){
return Integer.compare(score, O.score);
}
}
public class TestDriver {
public static void main(String[] args){
studentList.stream()
.sorted() //-> 시험 점수를 오름차순 정렬
.forEach(i->System.out.println(i.getScore()));
studentList.stream()
.sorted(Comparator.reverseOrder()) //-> 시험 점수를 내림차순 정렬
.forEach(i->System.out.println(i.getScore()));
}
}
📌
Comparable인터페이스를 구현한Student객체의sorted()메서드 호출 시compareTo()
메서드에 구현한 대로 시험 점수순으로Student객체를 정렬하는 예시이다.
📌 중간 처리 스트림에서는
peek()메서드를 이용하면 요소 전체에 반복 작업을 할 수 있다.
fruitList.stream()
.distinct()
.filter(i->i.length() < 8)
.peek(a->System.out.println(a)); //-> 단독 실행 X
fruitList.stream()
.distinct()
.filter(i->i.length() < 8)
.peek(a->System.out.println(a))
.filter(i->i.startsWith("a"))
.peek(a->System.out.println(a))
.forEach(System.out::println);
//-> 중간 처리 메서드가 리턴하는 스트림의 요소 출력
💡 이외에도
skip()이나limit()같은 중간 처리 메서드를 제공한다.
Optional 클래스📌 결과값이
null되는 경우를 대비하기 위해서Optional클래스를 제공한다.
Optional클래스는 결과값이null인 경우 디폴트 값을 설정할 수 있다.
| 리턴타입 | 메서드 | 설명 |
|---|---|---|
| boolean | isPresent() | 값 저장 유무 판별 |
| T double | orElse(T) orElse(double) | 저장된 값이 없는 경우 디폴트값 지정 |
| void | ifPresent(Consumer) ifPresent(DoubleConsumer) | 지정된 값이 있는 경우 처리 |
💡 다음은
Optional클래스를 이용해서 3가지 방법으로null을 처리하는 예제이다.
// 첫 번째 방법 (isPresent 사용)
OptionalDouble optional =
studentList.stream()
.mapToInt(Student::getScore)
.average()
.getAsDouble()
if(optional.isPresent()){ //값이 존재하는 경우
System.out.println(optional.getAsDouble());
}else{
System.out.println("시험 점수를 입력해주세요.")
}
// 두 번째 방법 (orElse 사용하기)
double avg = studentList.stream()
.mapToInt(Student::getScore)
.average()
.orElse(0.0) //-> 0.0
// 세 번째 방법(람다식 이용하기)
studentList.stream()
.mapToInt(Student::getScore)
.average()
.ifPresent(System.out::println);
📌
stream에 조건식을 만족하는 요소가 있는지 판별한다.
| 리턴타입 | 메서드 | 설명 |
|---|---|---|
| boolean | allMatch(Predicate\ predicate) | 모든 Stream요소들이 모든 조건을 만족하는지 판별 |
| boolean | anyMatch(Predicate\ predicate) | 임의의 Stream 요소가 조건을 만족하는지 판별 |
| boolean | nontMatch(Predicate\ predicate) | 모든 Stream요소가 조건을 만족하지 않는지 판별 |
| boolean | allMatch(IntPredicate\ predicate) | 모든 IntStream요소들이 모든 조건을 만족하는지 판별 |
| boolean | anyMatch(IntPredicate\ predicate) | 임의의 IntStream 요소가 조건을 만족하는지 판별 |
| boolean | nontMatch(IntPredicate\ predicate) | 모든 IntStream 요소가 조건을 만족하지 않는지 판별 |
💡예제
boolean b = Arrays.stream(strArr)
.anyMatch(i->i>100);
System.out.println(b);
boolean c = Arrays.stream(strArr)
.noneMath(i->i>100);
📌 대량의 데이터 처리 시 ,
집계 메서드를 사용해서 쉽게 구할 수 있다.
이런식으로 대량의 데이터를 가공해서 값을 출력하는 과정을리덕션(Reduction)이라고 한다.
| 리턴타입 | 메서드 | 설명 |
|---|---|---|
| long | count() | 스트림 요소 수량 |
| Optional | findFirst() | 스트림 첫 번째 요소 |
| Optional\ OptionalXXX | max(Comparator\) max() | 스트림 요소 중 최대 요소 |
| Optional<\T> OptionalXXX | min(Comparator\) min() | 스트림 요소 중 최소 요소 |
| OptionalDouble | average() | 스트림 요소 평균 |
| int, long, double | sum() | 스트림 요소 총합 |
💡 다음은 여러 가지 집계 메서드를 이용해서 점수들을 출력하는 예제들이다.
Arrays.stream(new int[]{55,66,77,88,99}).count();
//-> 전체 요소 개수를 얻는다.
Arrays.stream(new int[]{55,66,77,88,99}).sum();
//-> 합을 얻는다.
Arrays.stream(new int[]{55,66,77,88,99}).average().getAsDouble();
//-> 평균을 구한다.
Arrays.stream(new int[]{55,66,77,88,99}).max().getAsInt();
//-> 최대값을 구한다.
Arrays.stream(new int[]{55,66,77,88,99}).findFirst().getAsInt();
//-> 첫번째 값을 구한다.
📌기본 집계 메서드 외에
Reduce()메서드를 이용해서 프로그래머가 직접 원하는 집계 메서드를
만들어서 사용할 수 있다.
| 인터페이스 | 리턴 타입 | 메서드 |
|---|---|---|
| Stream | Optional\ | reduce(BinaryOperator\accumulator) |
| Stream | T | reduce(T identity, BinaryOperator\ accumulator) |
| IntStream | OptionalInt | reduce(intBinaryOperator op) |
| IntStream | int | reduce(int identity, IntBinaryOperator op) |
💡
reduce()예제
studentList.stream()
.map(Student::getScore)
.reduce((a,b)-> a+b) //-> 학생들의 점수를 누적시키는 형태
.get();
studentList.stream()
.map(Student::getScore)
.reduce(0, (a,b)->a+b) // -> 0(기본값)에 점수 누적
📌
collect()메서드를 이용해서 출력 결과를 컬렉션으로 저장할 수 있다.
| 리턴 타입 | 메서드 | 설명 |
|---|---|---|
| List\ | collect(Collectors.toList()) | 스트림 요소를 List로 변환 |
| Collection\ | collect(Collectors.toCollection(\)) | 스트림 요소를 정한 Collection\로 변환 |
| Map\<K, U> | collect(Collectors.toMap(Key, value)) | 스트림 요소를 Map(K, U)로 변환 |
💡
collect()예제
Arrays.stream(array)
.filter(i->i.getGender()==Student.FEMALE)
.collect(Collectoras.toList());
Arrays.stream(array)
.filter(i->i.getGender()==Student.FEMALE)
.collect(Collectoras.toCollection(HashSet::new));
//-> 스트림 요소를 HashSet에 저장
Arrays.stream(array)
.collect(Collectors.toMap(i->i.getName(), s->s))
//-> 스트림 요소를 HashSet에 저장
📌
collect()의 메서드 집계 기능을 이용하면 대량의 데이터를 처리해서 편리하게 통계 데이터를 추출할 수 있다.
partitioningBy() 메서드📌
partitioningBy()는 스트림 요소를 두 개의 집합으로 분할할 때 사용하는 메서드이다.
Map<Boolean, List<Student>> stdByGender =
studentList.stream()
.collect(Collectors.partitioningBy(i->i.getGender() == Student.MALE)); //-> 성별을 기준으로 true, false 로 나눠서 분류함
List<Student> maleList = stdByGender.get(true);
List<Student> femaleList = stdByGender.get(false);
groupingBy() 메서드📌 데이터를 더 세부적으로 분류할 경우
groupingBy메서드를 사용하면 편리하다.
Map<Integer, Map<Integer, List<Student>>> stdGroupByGreade =
studentList.stream()
.collect(Collectors.groupingBy(Student::getGrade
,Collectors.groupingBy(Student::getGender)));
📌 그루핑 데이터에 대해서 여러 가지 집계나 매핑 기능을 제공한다.
간단한 예제를 만들어보고Stream정리를 마친다.
public class MyStream {
public String returnRepeatedString(String string, int times) {
return Stream.of(string)
.flatMap(e-> Arrays.stream(e.split("")))
.map(a-> String.valueOf(a).repeat(times))
.reduce("", (a,b)->a+b);
} //-> 문자열을 반복출력
public void sortAndCount(List<Customer> customers){
Stream.of(customers)
.forEach(i->i.stream()
.collect(Collectors.groupingBy(Customer::getFirstName))
.entrySet()
.stream()
.sorted(Comparator.comparingInt(Key->Key.getValue().size()))
.forEach(Result->System.out.println(Result.getKey() + " " + Result.getValue().size())));
} //-> 어떤 성씨가 가장 많은지 성씨 : count 형식으로 출력
}