๋ธ๋ก๊ทธ ์ฎ๊ฒผ์ต๋๋ค! ๐ก integer.blog
์๋ฐ์ ์ ์(๋จ๊ถ์ฑ ์ ) 2๊ถ ํ์ต๋ด์ฉ ์ ๋ฆฌ
1. ์คํธ๋ฆผ(Stream)
์คํธ๋ฆผ์ ๋ฐ์ดํฐ ์์ค๋ฅผ ์ถ์ํํ๊ณ , ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๋๋ฐ ์์ฃผ ์ฌ์ฉ๋๋ ๋ฉ์๋๋ค์ ์ ์ํด ๋์๋ค.
Collection์ด๋ Iterator ๊ฐ์ ์ธํฐํ์ด์ค๋ฅผ ์ด์ฉํด์ ์ปฌ๋ ์ ์ ๋ค๋ฃจ๋ ๋ฐฉ์์ ํ์คํ ํ์ง๋ง, ๊ฐ ์ปฌ๋ ์ ํด๋์ค์๋ ๊ฐ์ ๊ธฐ๋ฅ์ ๋ฉ์๋๋ค์ด ์ค๋ณตํด์ ์ ์๋์ด ์๋ค. List๋ฅผ ์ ๋ ฌํ ๋๋ Collection.sort()๋ฅผ ์ฌ์ฉํด์ผํ๊ณ , ๋ฐฐ์ด์ ์ ๋ ฌํ ๋๋ Arrays.sort()๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค. ์ด๋ ๊ฒ ๋ฐ์ดํฐ ์์ค๋ง๋ค ๋ค๋ฅธ ๋ฐฉ์์ผ๋ก ๋ค๋ฃจ์ด์ผํ๋ ๋ฌธ์ ์ ์ ํด๊ฒฐํด์ฃผ๋ ๊ฒ์ด Stream ์ด๋ค.
// ๊ธฐ์กด
String[] strArr = {"aaa", "bbb", "ccc"};
List<String> strList = Arrays.asList(strArr);
// ์คํธ๋ฆผ ์์ฑ
Stream<String> strStream1 = strList.stream();
Stream<String> strStream2 = Arrays.stream(strArr);
// ์คํธ๋ฆผ ์ถ๋ ฅ
strStream1.sorted().forEach(System.out::println);
strStream2.sorted().forEach(System.out::println);
- ์คํธ๋ฆผ์ ๋ฐ์ดํฐ ์์ค๋ก ๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ธฐ๋ง ํ ๋ฟ, ๋ณ๊ฒฝํ์ง ์๋๋ค.
- ์คํธ๋ฆผ์ ํ๋ฒ ์ฌ์ฉํ๋ฉด ๋ซํ์ ๋ค์ ์ฌ์ฉํ ์ ์๋ค.
- ์คํธ๋ฆผ์ ์์
์ ๋ด๋ถ ๋ฐ๋ณต์ผ๋ก ์ฒ๋ฆฌํ๋ค.
void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action); // ๋งค๊ฐ๋ณ์์ ๋ ์ฒดํฌ
for(T t : src) {
action.accept(T);
}
}
- ์คํธ๋ฆผ์ ์ต์ข ์ฐ์ฐ์ด ์ํ๋๊ธฐ ์ ๊น์ง ์ค๊ฐ ์ฐ์ฐ์ด ์ํ๋์ง ์๋๋ค. ์ค๊ฐ ์ฐ์ฐ์ ํธ์ถํ๋ ๊ฒ์ ๋จ์ง ์ด๋ค ์์ ์ด ์ํ๋์ด์ผํ๋์ง๋ฅผ ์ง์ ํด์ฃผ๋ ๊ฒ์ผ ๋ฟ์ด๋ค.
- ์์์ ํ์
์ด T์ธ ์คํธ๋ฆผ์ Stream
์ด์ง๋ง, ์คํ ๋ฐ์ฑ/์ธ๋ฐ์ฑ์ ๋นํจ์จ์ ์ค์ด๊ธฐ ์ํด ๋ฐ์ดํฐ ์์ค์ ์์๋ฅผ ๊ธฐ๋ณธํ์ผ๋ก ๋ค๋ฃจ๋ InsStream, LongStream, DoubleStream์ด ์ ๊ณต๋๋ค. - ๋ณ๋ ฌ์คํธ๋ฆผ์ ๋ด๋ถ์ ์ผ๋ก fork&join ํ๋ ์์์ ์ด์ฉํด์ ์๋์ ์ผ๋ก ์ฐ์ฐ์ ๋ณ๋ ฌ๋ก ์ํํ๋ค. ์คํธ๋ฆผ์ parallel() ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด ๋ณ๋ ฌ๋ก ์ฐ์ฐํ๊ณ , parallel()์ ์ทจ์ํ๋ ค๋ฉด sequential()์ ํธ์ถํ๋ค.
- parallel()๊ณผ sequential()์ ์๋ก์ด ์คํธ๋ฆผ์ ์์ฑํ๋ ๊ฒ์ด ์๋๋ผ, ๊ทธ์ ์คํธ๋ฆผ์ ์์ฑ์ ๋ณ๊ฒฝํ ๋ฟ์ด๋ค.
int sum = strStream.parallelStream()
.mapToInt(s -> s.length())
.sum();
2. ์คํธ๋ฆผ ๋ง๋ค๊ธฐ
์คํธ๋ฆผ์ ์์ค๊ฐ ๋ ์ ์๋ ๋์์ ๋ฐฐ์ด, ์ปฌ๋ ์ , ์์์ ์ ๋ฑ ๋ค์ํ๋ค.
2.1. ์ปฌ๋ ์
์ปฌ๋ ์ ์ ์ต๊ณ ์กฐ์์ธ Collection์ stream()์ด ์ ์๋์ด ์์ด์ Collection์ ์์์ธ List์ Set์ ๊ตฌํํ ์ปฌ๋ ์ ํด๋์ค๋ค์ ๋ชจ๋ stream()์ผ๋ก ์คํธ๋ฆผ์ ์์ฑํ ์ ์๋ค.
Stream<T> Collection.stream()
// List๋ก๋ถํฐ ์คํธ๋ฆผ ์์ฑ
List<Integer> list = Arrays.asList(1,2,3,4,5);
Stream<Integer> intStream = list.stream(); // list๋ฅผ ์์ค๋ก ํ๋ ์ปฌ๋ ์
์์ฑ
2.2. ๋ฐฐ์ด
๋ฐฐ์ด์ ์์ค๋ก ํ๋ ์คํธ๋ฆผ์ ์์ฑํ๋ ๋ฉ์๋๋ ๋ค์๊ณผ ๊ฐ์ด 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)
- ๋ฌธ์์ด ์คํธ๋ฆผ ์์ฑ
Stream<String> strStream = Stream.of("a","b","c"); // ๊ฐ๋ณ์ธ์
Stream<String> strStream = Stream.of(new String[]{"a","b","c"});
Stream<String> strStream = Arrays.stream(new String[]{"a","b","c"});
Stream<String> strStream = Arrays.stream(new String[]{"a","b","c"}, 0, 3);
- int, long, double๊ณผ ๊ฐ์ ๊ธฐ๋ณธํ ๋ฐฐ์ด์ ์์ค๋ก ํ๋ ์คํธ๋ฆผ ์์ฑ
IntStream IntStream.of(int...values)
IntStream IntStream.of(int[])
IntStream Arrays.stream(int[])
IntStream Arrays.stream(int[] array, int startInclusive, endExclusive)
2.3. ํน์ ๋ฒ์์ ์ ์
IntStream intStream = IntStream.range(1, 5); // 1,2,3,4
IntStream intStream = IntStream.rangeClosed(1, 5) // 1,2,3,4,5
2.4. ์์์ ์
๋์๋ฅผ ์์ํ๋ Randomํด๋์ค์๋ ์๋์ ๊ฐ์ ์ธ์คํด์ค ๋ฉ์๋๋ค์ด ํฌํจ๋์ด ์๋ค.
//ํด๋น ํ์
์ ๋์๋ค๋ก ์ด๋ฃจ์ด์ง ์คํธ๋ฆผ ๋ฐํํ๋ ๋ฉ์๋๋ค
IntStream ints()
LongStream longs()
DoubleStream doubles()
//์์ ๋ฉ์๋๋ค์ด ๋ฐํํ๋ ์คํธ๋ฆผ์ ํฌ๊ธฐ๊ฐ ์ ํด์ง์ง ์์ ๋ฌดํ ์คํธ๋ฆผ(infinite stream)์ด๋ฏ๋ก
//limit()๋ ๊ฐ์ด ์ฌ์ฉํด์ ์คํธ๋ฆผ์ ํฌ๊ธฐ๋ฅผ ์ ํํด ์ฃผ์ด์ผ ํ๋ค. limit()์ ์คํธ๋ฆผ์ ๊ฐ์๋ฅผ ์ง์ ํ๋๋ฐ ์ฌ์ฉ๋๋ค.
IntStream intStream = new Random().ints(); // ๋ฌดํ์คํธ๋ฆผ
intStream.limit(5).forEach(System.out::println); // 5๊ฐ์ ์์๋ง ์ถ๋ ฅ
//๋งค๊ฐ๋ณ์๋ก ์คํธ๋ฆผ์ ํฌ๊ธฐ๋ฅผ ์ง์ ํ๋ฉด limit()์ ์ฌ์ฉํ์ง ์์๋ ๋๋ค.
IntStream ints(long streamSize)
LongStream longs(long streamSize)
DoubleStream doubles(long streamSize)
//์ง์ ๋ ๋ฒ์์ ๋์๋ฅผ ๋ฐ์์ํค๋ ์คํธ๋ฆผ์ ์ป๋ ๋ฉ์๋๋ค(end๋ ๋ฒ์์ ๋ฏธํฌํจ)
IntStream ints(int begin, int end)
LongStream longs(long begin, long end)
DoubleStream doubles(double begin, double end)
IntStream ints(long streamSize, int begin, int end)
LongStream longs(long streamSize, long begin, long end)
DoubleStream doubles(long streamSize, double begin, double end)
2.5. ๋๋ค์ - iterate(), generate()
Stream ํด๋์ค์ iterate()์ generate()๋ ๋๋ค์์ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์์, ์ด ๋๋ค์์ ์ํด ๊ณ์ฐ๋๋ ๊ฐ๋ค์ ์์๋ก ํ๋ ๋ฌดํ ์คํธ๋ฆผ์ ์์ฑํ๋ค.
static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
static <T> Stream<T> generate(Supplier<T> s)
- iterate()๋ ์จ์๊ฐ(seed)์ผ๋ก ์ง์ ๋ ๊ฐ๋ถํฐ ์์ํด์ ๋๋ค์ f์ ์ํด ๊ณ์ฐ๋ ๊ฒฐ๊ณผ๋ฅผ ๋ค์ seed๊ฐ์ผ๋ก ๊ณ์ฐ์ ๋ฐ๋ณตํ๋ค.
- generate()๋ iterate()์ ๋ฌ๋ฆฌ ์ด์ ๊ฒฐ๊ณผ๋ฅผ ์ด์ฉํด์ ๋ค์ ์์๋ฅผ ๊ณ์ฐํ์ง ์๋๋ค.
- iterate()์ generate()์ ์ํด ์์ฑ๋ ์คํธ๋ฆผ์ ๊ธฐ๋ณธํ ์คํธ๋ฆผ ํ์ ์ ์ฐธ์กฐ๋ณ์๋ก ๋ค๋ฃฐ ์ ์๋ค.
// iterate()
Stream<Integer> evenStream = Stream.iterate(0, n->n+2); // 0, 2, 4, 6, ...
// generate()
Stream<Double> randomStream = Stream.generate(Math::random);
Stream<Integer> oneStream = Stream.generate(()->1);
2.6. ๋น ์คํธ๋ฆผ
์์๊ฐ ํ๋๋ ์๋ ๋น ์คํธ๋ฆผ์ ์์ฑํ ์ ์๋ค.
์คํธ๋ฆผ์ ์ฐ์ฐ์ ์ํํ ๊ฒฐ๊ณผ๊ฐ ํ๋๋ ์์ ๋, null๋ณด๋ค ๋น ์คํธ๋ฆผ์ ๋ฐํํ๋ ๊ฒ์ด ๋ซ๋ค.
Stream emptyStream = Stream.empty(); // empty()๋ ๋น ์คํธ๋ฆผ์ ์์ฑํด์ ๋ฐํํ๋ค.
long count = emptyStream.count(); // count์ ๊ฐ์ 0
2.7. ๋ ์คํธ๋ฆผ์ ์ฐ๊ฒฐ
Stream์ static ๋ฉ์๋์ธ concat()์ ์ฌ์ฉํด์ ๋ ์คํธ๋ฆผ์ ํ๋๋ก ์ฐ๊ฒฐํ ์ ์๋ค.
๋ ์คํธ๋ฆผ์ ๊ฐ์ ํ์ ์ด์ด์ผ ํ๋ค.
String[] str1 = {"123", "456", "789"}
String[] str2 = {"ABC", "abc", "DEF"}
Stream<String> strs1 = Stream.of(str1);
Stream<String> strs2 = Stream.of(str2);
Stream<String> strs3 = Stream.concat(strs1, strs2); // ๋ ์คํธ๋ฆผ์ ํ๋๋ก ์ฐ๊ฒฐ
3. ์คํธ๋ฆผ์ ์ค๊ฐ์ฐ์ฐ
3.1. ์คํธ๋ฆผ ์๋ฅด๊ธฐ
Stream
skip(long n) // ์ฒ์ n๊ฐ์ ์์ ๊ฑด๋๋ฐ๊ธฐ
Stremalimit(long maxSize) // ์คํธ๋ฆผ์ ์์๋ฅผ maxSize๊ฐ๋ก ์ ํ
IntStream exampleStream = IntStream.rangeClosed(1, 10); // 1~10์ ์์๋ฅผ ๊ฐ์ง ์คํธ๋ฆผ
exampleStream.skip(3).limit(5).forEach(System.out::print); // 45678
3.2. ์คํธ๋ฆผ ์์ ๊ฑธ๋ฌ๋ด๊ธฐ
distinct()๋ ์คํธ๋ฆผ์์ ์ค๋ณต๋ ์์๋ค ์ ๊ฑฐ
filter()๋ ์ฃผ์ด์ง ์กฐ๊ฑด(Predicate)์ ๋ง์ง ์๋ ์์๋ฅผ ๊ฑธ๋ฌ๋ธ๋ค.
// distinct()
IntStream exampleStream = IntStream.of(1,2,2,3,3,3,4,5,5,6);
exampleStream.distinct().forEach(System.out::print); // 123456
// filter()
IntStream example2Stream = IntStream.rangeClosed(1, 10);
example2Stream.filter(i -> i%2 ==0).forEach(System.out::print); // 246810
// filter()๋ฅผ ๋ค๋ฅธ ์กฐ๊ฑด์ผ๋ก ์ฌ๋ฌ ๋ฒ ์ฌ์ฉ. ๋ ๋ฌธ์ฅ์ ๊ฒฐ๊ณผ๋ ๊ฐ๋ค.
example2Stream.filter(i -> i%2!=0 && i%3!=0).forEach(System.out::print); //157
example2Stream.filter(i -> i%2!=0).filter(i -> i%3!=0).forEach(System.out::print);
3.3. ์คํธ๋ฆผ ์ ๋ ฌ
Stream
sorted()
Streamsorted(Comparator<? super T> comparator)
Stream<String> strStream = Stream.of("dd","aaa","CC","cc","b");
strStream.sorted().forEach(System.out::print); // CCaaabccdd
// ๊ธฐ๋ณธ์ ๋ ฌ ์ญ์
strStream.sorted(Comparator.reverseOrder());
// ๋์๋ฌธ์ ๊ตฌ๋ถ ์์ด
strStream.sorted(String.CASE_INSESITIVE_ORDER)
// ๊ธธ์ด ์ ์ ๋ ฌ
strStream.sorted(Comparator.comparing(String::length))
//์ ๋ ฌ ์กฐ๊ฑด์ ์ถ๊ฐํ ๋๋ thenComparing() ์ฌ์ฉ
studentStream.sorted(Comparator.comparing(Student::getBan)
.thenComparing(Student::getTotalScore)
.thenComparing(Student::getName)
.forEach(System.out::println);
3.4. ์คํธ๋ฆผ ๋ณํ
์คํธ๋ฆผ์ ์์์ ์ ์ฅ๋ ๊ฐ ์ค์์ ์ํ๋ ํ๋๋ง ๋ฝ์๋ด๊ฑฐ๋, ํน์ ํํ๋ก ๋ณํํด์ผ ํ ๋
map() ์ฌ์ฉ
// map()์ผ๋ก Stream<File>์ Stream<String>์ผ๋ก ๋ณํํ๊ธฐ
Stream<File> fileStream = Stream.of(new File("Ex1.java"), new File("Ex2.java"));
Stream<String> filenameStream = fileStream.map(File::getName);
filenameStream.forEach(System.out::println);
// map() ์ฌ๋ฌ ๋ฒ ์ฌ์ฉ
fileStream.map(File::getName) // Stream<File> -> Stream<String>
.filter(s -> s.indexOf('.')!= -1) // ํ์ฅ์ ์๋ ๊ฒ ์ ์ธ
.map(s -> s.substring(s.indexOf('.')+1)) // Stream<String> -> Stream<String>
.map(String::toUpperCase) // ๋ชจ๋ ๋๋ฌธ์๋ก ๋ณํ
.distinct()
.forEach(System.out::print);
4. ์คํธ๋ฆผ์ ์ค๊ฐ์ฐ์ฐ
4.1. ์คํธ๋ฆผ ์กฐํ
์ฐ์ฐ๊ณผ ์ฐ์ฐ ์ฌ์ด์ ์ฌ๋ฐ๋ฅด๊ฒ ์ฒ๋ฆฌ๋์๋์ง ํ์ธํ๋ ค๋ฉด peek() ์ฌ์ฉ
forEach()์ ๋ฌ๋ฆฌ ์คํธ๋ฆผ์ ์์๋ฅผ ์๋ชจํ์ง ์์ผ๋ฏ๋ก ์ฐ์ฐ ์ฌ์ด์ ์ฌ๋ฌ ๋ฒ ์ฌ์ฉ ๊ฐ๋ฅ
fileStream.map(File::getName)
.filter(s -> s.indexOf('.') != -1) //ํ์ฅ์ ์๋ ๊ฒ ์ ์ธ
.peek(s -> System.out.printf("filename=%s%n", s)) //ํ์ผ๋ช
์ถ๋ ฅ
.map(s -> s.substring(s.indexOf('.')+1)) //ํ์ฅ์๋ง ์ถ์ถ
.peek(s -> System.out.printf("extension=%s%n", s)) //ํ์ฅ์ ์ถ๋ ฅ
.forEach(System.out::println);
4.2. Stream ์คํธ๋ฆผ์ ๊ธฐ๋ณธํ ์คํธ๋ฆผ์ผ๋ก ๋ณํ
map()์ ์ฐ์ฐ์ ๊ฒฐ๊ณผ๋ก Stream
ํ์ ์ ์คํธ๋ฆผ์ ๋ฐํํ๋๋ฐ,
์คํธ๋ฆผ์ ์์๋ฅผ ์ซ์๋ก ๋ณํํ๋ ๊ฒฝ์ฐ IntStream๊ณผ ๊ฐ์ ๊ธฐ๋ณธํ ์คํธ๋ฆผ์ผ๋ก ๋ณํํ๋ ๊ฒ์ด ๋ ์ ์ฉํ ์ ์๋ค.
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper)
IntStream mapToInt(ToIntFunction<? super T> mapper)
LongStream mapToLong(ToLongFunction<? super T> mapper)
// ์คํธ๋ฆผ์ ํฌํจ๋ ๋ชจ๋ ํ์์ ์ฑ์ ์ ํฉ์ฐํ๊ธฐ ์ํด map()์ผ๋ก ์๋ก์ด ์คํธ๋ฆผ ์์ฑ
Stream<Integer> studentScoreStream = studentStream.map(Student::getTotalScore);
// ์ ์ด์ Stream<Integer>๊ฐ ์๋ IntStream ํ์
์ ์คํธ๋ฆผ ์์ฑํ๊ธฐ
// ์ฑ์ ์ ๋ํ ๋ Integer๋ฅผ int๋ก ๋ณํํ ํ์ ์์ด์ ๋ ํจ์จ์ ์ด๋ค.
IntStream studentScoreStream = studentStream.mapToInt(Student::getTotalScore);
int allTotalScore = studentScoreStream.sum();
count()๋ง ์ง์ํ๋ Stream
์ ๋ฌ๋ฆฌ IntStream๊ณผ ๊ฐ์ ๊ธฐ๋ณธํ ์คํธ๋ฆผ์ ์๋์ ๊ฐ์ด ์ซ์๋ฅผ ๋ค๋ฃจ๋๋ฐ ํธ๋ฆฌํ ๋ฉ์๋๋ค์ ์ ๊ณตํ๋ค.
int sum() ์คํธ๋ฆผ ๋ชจ๋ ์์์ ์ดํฉ
OptionalDouble average() sum()/count()
OptionalInt max() ์คํธ๋ฆผ ์์ ์ค ์ ์ผ ํฐ ๊ฐ
OPtionalInt min() ์คํธ๋ฆผ ์์ ์ค ์ ์ผ ์์ ๊ฐ
* max()์ min()์ Stream์๋ ์ ์๋์ด ์์ง๋ง ๋งค๊ฐ๋ณ์๋ก Comparator๋ฅผ ์ง์ ํด์ผ ํ๋ค.
* ์ด ๋ฉ์๋๋ค์ ์ต์ข
์ฐ์ฐ์ด๋ค.
* sum()๊ณผ average()๋ฅผ ๋ชจ๋ ํธ์ถํ๋ ค๋ฉด summaryStatistics() ๋ฉ์๋ ์ฌ์ฉ
IntStream์ Stream
๋ก ๋ณํํ ๋๋ mapToObj() ์ฌ์ฉ
IntStream์ Steram๋ก ๋ณํํ ๋๋ boxed() ์ฌ์ฉ
//mapToObj() ์ฌ์ฉํด์ IntStream์ Stream<String>์ผ๋ก ๋ณํ
//๋ก๋๋ฒํธ ์์ฑ๊ธฐ
IntStream intStream = new Random().ints(1,46); // 1~45์ฌ์ด์ ์ ์
Stream<String> lottoStream = intStream.distinct().limit(6).sorted()
.mapToObj(i->i+","); // ์ ์๋ฅผ ๋ฌธ์์ด๋ก ๋ณํ
lottoStream.forEach(System.out::print); // 12,14,23,29,45
CharSequence์ ์ ์๋ chars()๋ String์ด๋ StringBuffer์ ์ ์ฅ๋ ๋ฌธ์๋ค์ IntStream์ผ๋ก ๋ค๋ฃฐ ์ ์๊ฒ ํด์ค๋ค.
IntStream charStream = "12345".chars();
int charSum = charStream.map(ch-> ch-'0').sum() // charSum=15
Stream
-> IntStream ๋ณํ ์, mapToInt(Integer::parseInt) ์ฌ์ฉ
Stream-> IntStream ๋ณํ ์, mapToInt(Integer::intValue) ์ฌ์ฉ
4.3. Stream๋ฅผ Stream๋ก ๋ณํ
flatMap() ์ฌ์ฉ
// ์์๊ฐ ๋ฌธ์์ด ๋ฐฐ์ด(String[])์ธ ์คํธ๋ฆผ
Stream<String[]> strArrStrm = Stream.of(
new String[]{"abc", "def", "ghi"},
new String[]{"ABC", "DEF"m "JKLMN"}
);
// Stream<String[]>์ map(Arrays::stream)์ผ๋ก ๋ณํํ ๊ฒฐ๊ณผ๋ Stream<String>์ด ์๋ Stream<Stream<String>>.
// ์ฆ ์คํธ๋ฆผ์ ์คํธ๋ฆผ์ด๋ค.
Stream<Stream<String>> strStrStrm = strArrStrm.map(Arrays::stream);
//map() ๋์ flatMap() ์ฌ์ฉํ๋ฉด ๊ฐ ๋ฐฐ์ด์ด ํ๋์ ์คํธ๋ฆผ์ ์์๊ฐ ๋๋ค.
Stream<String> strStrm = strArrStrm.flatMap(Arrays::stream);
5. Optional
Optional
์ ์ง๋ค๋ฆญ ํด๋์ค๋ก ‘Tํ์ ์ ๊ฐ์ฒด’๋ฅผ ๊ฐ์ธ๋ ๋ํผ ํด๋์ค๋ค.
๊ทธ๋์ Optionalํ์ ์ ๊ฐ์ฒด์๋ ๋ชจ๋ ํ์ ์ ์ฐธ์กฐ๋ณ์๋ฅผ ๋ด์ ์ ์๋ค.
์ต์ข
์ฐ์ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋ฅ ๋ฐํํ๋ ๊ฒ์ด ์๋๋ผ Optional ๊ฐ์ฒด์ ๋ด์์ ๋ฐํํ๋ฉด,
๋ฐํ๋ ๊ฒฐ๊ณผ๊ฐ null์ธ์ง ๋งค๋ฒ if๋ฌธ์ผ๋ก ์ฒดํฌํ๋ ๋์ Optional์ ์ ์๋ ๋ฉ์๋๋ฅผ ํตํด ๊ฐ๋จํ ์ฒ๋ฆฌํ ์ ์๋ค.
5.1. Optional ๊ฐ์ฒด ์์ฑํ๊ธฐ
of() ๋๋ ofNullable() ์ฌ์ฉ
//์ฐธ์กฐ๋ณ์์ ๊ฐ์ด null์ผ ๊ฐ๋ฅ์ฑ์ด ์์ผ๋ฉด of()๋์ ofNullable() ์ฌ์ฉ
Optional<String> optVal = Optional.of(null); //NullPointerException ๋ฐ์
Optional<String> optVal = Optional.ofNullable(null); // OK
//Optional<T> ํ์
์ ์ฐธ์กฐ๋ณ์๋ฅผ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ด๊ธฐํํ ๋ empty() ์ฌ์ฉ
//null๋ก ์ด๊ธฐํํ ์ ์์ง๋ง empty()๋ก ์ด๊ธฐํํ๋ ๊ฒ์ด ๋ฐ๋์ง
Optional<String> optVal = null;
Optional<String> optVal = Optional.empty(); // ๋น ๊ฐ์ฒด๋ก ์ด๊ธฐํ
5.2. Optional ๊ฐ์ฒด์ ๊ฐ ๊ฐ์ ธ์ค๊ธฐ
get(), orElse()
Optional<String> optVal = Optional.of("abc");
String str1 = optVal.get(); // optVal์ ์ ์ฅ๋ ๊ฐ์ ๋ฐํ. null์ด๋ฉด ์์ธ๋ฐ์
String str2 = optVal.orElse(""); // optVal์ ์ ์ฅ๋ ๊ฐ์ด null์ด๋ฉด ""๋ฅผ ๋ฐํ
String str3 = optVal.orElseGet(String::new); // null์ ๋์ฒดํ ๊ฐ์ ๋ฐํํ๋ ๋๋ค์ ์ง์
String str4 = optVal.orElseThrow(NullPointerException::new); // null์ผ ๋ ์ง์ ๋ ์์ธ๋ฅผ ๋ฐ์
isPresent()๋ Optional ๊ฐ์ฒด์ ๊ฐ์ด null์ด๋ฉด false๋ฅผ, ์๋๋ฉด true ๋ฐํ
ifPresent(Consumerblock)๋ ๊ฐ์ด ์์ผ๋ฉด ์ฃผ์ด์ง ๋๋ค์ ์คํ, ์์ผ๋ฉด ์๋ฌด์ผ ์ํจ
//์กฐ๊ฑด๋ฌธ
if(str!=null) {
System.out.println(str);
}
//์์ ์กฐ๊ฑด๋ฌธ์ isPresent()๋ก ๊ตฌํ
if(Optional.ofNullable(str).isPresent()) {
System.out.println(str);
}
//์์ ์กฐ๊ฑด๋ฌธ์ ifPresent()๋ก ๊ตฌํ
Optional.ofNullable(str).ifPresent(System.out::println);
6. ์คํธ๋ฆผ์ ์ต์ข ์ฐ์ฐ
์ต์ข ์ฐ์ฐ์ ์คํธ๋ฆผ์ ์์๋ฅผ ์๋ชจํด์ ๊ฒฐ๊ณผ๋ฅผ ๋ง๋ ๋ค. ๊ทธ๋์ ์ต์ข ์ฐ์ฐํ์๋ ์คํธ๋ฆผ์ด ๋ซํ์ ๋์ด ์ ์ฌ์ฉํ ์ ์๋ค.
6.1. forEach()
๋ฐํ ํ์ ์ด void์ด๋ฏ๋ก ์คํธ๋ฆผ์ ์์๋ฅผ ์ถ๋ ฅํ๋ ์ฉ๋๋ก ๋ง์ด ์ฌ์ฉ
6.2. ์กฐ๊ฑด๊ฒ์ฌ
allMatch(), anyMatch(), noneMatch(), findFirst(), findAny()
boolean allMatch (Predicate<? super T> predicate)
boolean anyMatch (Predicate<? super T> predicate)
boolean noneMatch (Predicate<? super T> predicate)
// ํ์์ฑ์ ์คํธ๋ฆผ์์ ์ด์ ์ด ๋์ ์ ์ดํ์ธ ํ์ ํ์ธ
boolean noFailed = stuStream.anyMatch(s->s.getTotalScore()<=100);
// ์คํธ๋ฆผ ์์ ์ค ์กฐ๊ฑด์ ์ผ์นํ๋ ์ฒซ ๋ฒ์งธ ๊ฒ ๋ฐํ
Optional<Student> stu = stuStream.filter(s->s.getTotalScore()<=100).findFirst();
// ๋ณ๋ ฌ์คํธ๋ฆผ์ ๊ฒฝ์ฐ findFirst() ๋์ findAny() ์ฌ์ฉ
Optional<Student> stu = parallelStream.filter(s->s.getTotalScore()<=100).findAny();
6.3. ํต๊ณ
count(), sum(), average(), max(), min()
// ๊ธฐ๋ณธํ ์คํธ๋ฆผ์ด ์๋ ๊ฒฝ์ฐ ํต๊ณ ๊ด๋ จ ๋ฉ์๋๋ ์๋ 3๊ฐ ๋ฟ์ด๋ค.
// ๊ธฐ๋ณธํ ์คํธ๋ฆผ์ min(), max()์ ๋ฌ๋ฆฌ ๋งค๊ฐ๋ณ์๋ก Comparator๊ฐ ํ์ํ๋ค.
long count()
Optional<T> max(Comparator<? super T> comparator)
Optional<T> min(Comparator<? super T> comparator)
6.4. reduce()
reduce()๋ ์คํธ๋ฆผ์ ์์๋ฅผ ์ค์ฌ๋๊ฐ๋ฉด์ ์ฐ์ฐ์ ์ํํ๊ณ ์ต์ข ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ค.
๊ทธ๋์ ๋งค๊ฐ๋ณ์์ ํ์ ์ด BinaryOperator์ด๋ค.
์ฒ์ ๋ ์์๋ฅผ ๊ฐ์ง๊ณ ์ฐ์ฐํ ๊ฒฐ๊ณผ๋ก ๊ทธ ๋ค์ ์์์ ์ฐ์ฐํ๋ค.
๋ชจ๋ ์คํธ๋ฆผ์ ์์๋ฅผ ์๋ชจํ๊ฒ ๋๋ฉด ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ค.
Optional<T> reduce(BinaryOperator<T> accumulator)
reduce()์ ์ฌ์ฉ๋ฐฉ๋ฒ์ ์ด๊ธฐ๊ฐ๊ณผ ์ด๋ค ์ฐ์ฐ(BinaryOperator)์ผ๋ก ์คํธ๋ฆผ์ ์์๋ฅผ ์ค์ฌ๋๊ฐ ๊ฒ์ธ์ง๋ง ๊ฒฐ์ ํ๋ฉด ๋๋ค.
7. collect()
์คํธ๋ฆผ์ ์์๋ฅผ ์์งํ๋ ์ต์ข ์ฐ์ฐ
collect()๊ฐ ์คํธ๋ฆผ์ ์์๋ฅผ ์์งํ๊ธฐ ์ํ ์์ง ๋ฐฉ๋ฒ์ด ์ ์๋ ๊ฒ์ด collector.
collector๋ Collector์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ๊ฒ.
collect() ์คํธ๋ฆผ์ ์ต์ข
์ฐ์ฐ. ๋งค๊ฐ๋ณ์๋ก collector๊ฐ ํ์ํ๋ค.
Collector ์ธํฐํ์ด์ค. collector๋ ์ด ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด์ผํ๋ค.
Collectors ํด๋์ค. static ๋ฉ์๋๋ก ๋ฏธ๋ฆฌ ์์ฑ๋ collector๋ฅผ ์ ๊ณตํ๋ค.
collect()์ ๋งค๊ฐ๋ณ์ ํ์
์ Collector์ธ๋ฐ. ๋งค๊ฐ๋ณ์๊ฐ Collector๋ฅผ ๊ตฌํํ ํด๋์ค์ ๊ฐ์ฒด์ด์ด์ผ ํ๋ค๋ ๋ป.
๊ทธ๋ฆฌ๊ณ collect()๋ ์ด ๊ฐ์ฒด์ ๊ตฌํ๋ ๋ฐฉ๋ฒ๋๋ก ์คํธ๋ฆผ์ ์์๋ฅผ ์์งํ๋ค.
7.1. ์คํธ๋ฆผ์ ์ปฌ๋ ์ ๊ณผ ๋ฐฐ์ด๋ก ๋ณํ
toList(), toSet(), toMap(), toCollection(), toArray()
// ์คํธ๋ฆผ์ ๋ชจ๋ ์์๋ฅผ ์ปฌ๋ ์
์ ์์งํ๋ ค๋ฉด, Collectorsํด๋์ค์ toList()์ ๊ฐ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
List<String> names = stuStream.map(Student::getName)
.collect(Collectors.toList());
// List๋ Set์ด ์๋ ํน์ ์ปฌ๋ ์
์ ์ง์ ํ๋ ค๋ฉด,
// toCollection()์ ํด๋น ์ปฌ๋ ์
์ ์์ฑ์ ์ฐธ์กฐ๋ฅผ ๋งค๊ฐ๋ณ์๋ก ๋ฃ์ด์ฃผ๋ฉด ๋๋ค.
ArrayList<String> list = names.stream()
.collect(Collectors.toCollection(ArrayList::new));
// Map์ ๊ฐ์ฒด์ ์ด๋ค ํ๋๋ฅผ ํค์ ๊ฐ์ผ๋ก ์ฌ์ฉํ์ง ์ง์ ํด์ผ ํ๋ค.
// ์์์ ํ์
์ด Person์ธ ์คํธ๋ฆผ์์ ์ฌ๋์ ์ฃผ๋ฏผ๋ฒํธ(regId)๋ฅผ ํค๋ก ํ๊ณ , ๊ฐ์ผ๋ก Person ๊ฐ์ฒด๋ฅผ ๊ทธ๋๋ก ์ ์ฅ
Map<String, Person> map = personStream.collect(Collectors.toMap(p->p.getRegId(), p->p)
// ์คํธ๋ฆผ์ ์ ์ฅ๋ ์์๋ค์ T[] ํ์
์ ๋ฐฐ์ด๋ก ๋ณํํ๋ ค๋ฉด toArray() ์ฌ์ฉ
// ๋จ, ํด๋น ํ์
์ ์์ฑ์ ์ฐธ์กฐ๋ฅผ ๋งค๊ฐ๋ณ์๋ก ์ง์ ํด์ค์ผ ํ๋ค. ์ง์ ํ์ง ์์ผ๋ฉด ๋ฐํ๋๋ ๋ฐฐ์ด์ ํ์
์ Object[]
Student[] stuNames = studentStream.toArray(Student[]::new); // OK
Student[] stuNames = studentStream.toArray(); // ์๋ฌ
Object[] stuNames = studentStream.toArray(); // OK
7.2. ํต๊ณ
counting(), summingInt(), averagingInt(), maxBy(), minBy()
7.3. ๋ฌธ์์ด ๊ฒฐํฉ
joining()
์คํธ๋ฆผ์ ์์๊ฐ String์ด๋ StringBuffer์ฒ๋ผ CharSequence์ ์์์ธ ๊ฒฝ์ฐ์๋ง ๊ฒฐํฉ ๊ฐ๋ฅํ๋ฏ๋ก
์คํธ๋ฆผ์ ์์๊ฐ ๋ฌธ์์ด์ด ์๋ ๊ฒฝ์ฐ์๋ ๋จผ์ map()์ ์ด์ฉํด์ ์คํธ๋ฆผ์ ์์๋ฅผ ๋ฌธ์์ด๋ก ๋ณํํด์ผ ํ๋ค.
String studentNames = stuStream.map(Student::getName).collect(joining());
String studentNames = stuStream.map(Student::getName).collect(joining(","));
String studentNames = stuStream.map(Student::getName).collect(joining(",", "[", "]"));
// ๋ง์ฝ map()์์ด ์คํธ๋ฆผ์ ๋ฐ๋ก joining()ํ๋ฉด, ์คํธ๋ฆผ์ ์์์ toString()์ ํธ์ถํ ๊ฒฐ๊ณผ๋ฅผ ๊ฒฐํฉํ๋ค.
String studentInfo = stuStream.collect(joining(","));
7.4. ๊ทธ๋ฃนํ์ ๋ถํ
groupingBy(), partitioningBy()
๊ทธ๋ฃนํ๋ ์คํธ๋ฆผ์ ์์๋ฅผ ํน์ ๊ธฐ์ค์ผ๋ก ๊ทธ๋ฃนํํ๋ ๊ฒ
๋ถํ ์ ์คํธ๋ฆผ์ ์์๋ฅผ ๋ ๊ฐ์ง, ์ง์ ๋ ์กฐ๊ฑด์ ์ผ์นํ๋ ๊ทธ๋ฃน๊ณผ ์๋ ๊ทธ๋ฃน์ผ๋ก ๋ถํ ํ๋ ๊ฒ
groupingBy()๋ ์คํธ๋ฆผ์ ์์๋ฅผ Function์ผ๋ก, partitioningBy()๋ Predicate๋ก ๋ถ๋ฅ
๋ณดํต ์คํธ๋ฆผ์ ๋ ๊ฐ์ ๊ทธ๋ฃน์ผ๋ก ๋๋ ์ผ ํ๋ฉด partitioningBy()์ฐ๋ ๊ฒ์ด ๋ ๋น ๋ฅด๊ณ , ๊ทธ ์ธ์๋ groupingBy()๋ฅผ ์ฐ๋ฉด ๋๋ค.
๊ทธ๋ฃนํ์ ๋ถํ ์ ๊ฒฐ๊ณผ๋ Map์ ๋ด๊ฒจ ๋ฐํ๋๋ค.
7.5. partitioningBy()์ ์ํ ๋ถ๋ฅ
// 1. ๊ธฐ๋ณธ ๋ถํ
Map<Boolean, List<Student>> stuBySex
= stuStream.collect(partitioningBy(Student::isMale)); // ํ์๋ค์ ์ฑ๋ณ๋ก ๋ถํ
List<Student> maleStudent = stuBySex.get(true); // Map์์ ๋จํ์ ๋ชฉ๋ก์ ์ป๋๋ค.
List<Student> femaleStudent = stuBySex.get(false); // ์ฌํ์ ๋ชฉ๋ก
// 2. ๊ธฐ๋ณธ ๋ถํ + ํต๊ณ ์ ๋ณด
Map<Boolean, Long> stuNumBySex = stuStream.collect(partitioningBy(Student::isMale, counting()));
System.out.println(stuNumBySex.get(true)); // 8 (๋จํ์์)
System.out.println(stuNumBySex.get(false)); // 10 (์ฌํ์์)
// ๋จํ์ 1๋ฑ ๊ตฌํ๊ธฐ, mapBy()์ ๋ฐํํ์
์ Optional<Student>
Map<Boolean, Optional<Student>> topScoreBySex
= stuStream.collect(partitioningBy(Student::isMale, maxBy(comparingInt(Student::getScore))));
System.out.println(topScoreBySex.get(true)); // Optional{[๋จ์ผ๋ฑ, ๋จ, 1, 1, 300]}
// mapBy()์ ๋ฐํํ์
์ด Optional<Student>๊ฐ ์๋ Student๋ฅผ ๋ฐํ ๊ฒฐ๊ณผ๋ก ์ป์ผ๋ ค๋ฉด,
// collectiongAndThen()๊ณผ Optional::get ํจ๊ป ์ฌ์ฉ
Map<Boolean, student> topScoreBySex
= stuStream.collect(
partitioningBy(
Student::isMale, collectingAndThen(
maxBy(comparingInt(Student::getScore))
, Optional::get)));
// 3. ์ด์ค ๋ถํ
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);
7.6. groupingBy()์ ์ํ ๋ถ๋ฅ
groupingBy()๋ก ๊ทธ๋ฃนํํ๋ฉด ๊ธฐ๋ณธ์ ์ผ๋ก List
์ ๋ด๋๋ค.
// 1. ํ์ ์คํธ๋ฆผ์ ๋ฐ ๋ณ๋ก ๊ทธ๋ฃน์ง์ด Map์ ์ ์ฅ
Map<Integer, List<Student>> stuByBan
= stuStream.collect(groupingBy(Student::getBan, toList())); //toList()์๋ต๊ฐ๋ฅ
Map<Integer, HashSet<Student>> stuByHak
= stuStream.collect(groupingBy(Student::getHak, toCollection(HashSet::new)));
// 2. ํ์ ์คํธ๋ฆผ์ ์ฑ์ ์ ๋ฑ๊ธ(Student.Level)์ผ๋ก ๊ทธ๋ฃนํ
Map<Student.Level, Long> stuByLevel
= stuStream.collect(
groupingBy(s-> { if(s.getScore()>=200) return Student.Level.HIGH;
else if(s.getScore()>=100) return Student.Level.MID;
else return Student.Level.LOW;
}, counting()));
// 3. groupingBy() ๋ค์ค ์ฌ์ฉํ๊ธฐ.
// ํ๋
๋ณ๋ก ๊ทธ๋ฃนํํ๊ณ ๋ค์ ๋ฐ๋ณ๋ก ๊ทธ๋ฃนํ
Map<Integer, Map<Integer, List<Student>>> stuByHakAndBan
= stuStream.collect(groupingBy(Student::getHak,
groupingBy(Student::getBan)));
// 4. ๊ฐ ๋ฐ๋ณ 1๋ฑ ์ถ์ถ
Map<Integer, Map<Integer, Student>> topStuByHakAndBan
= stuStream.collect(groupingBy(Student::getHak,
groupingBy(Student::getBan,
collectingAndThen(
maxBy(comparingInt(Student::getScore)),
Optional::get))));
// 5. ํ๋
๋ณ, ๋ฐ๋ณ ๊ทธ๋ฃนํํ ํ์ ์ฑ์ ๊ทธ๋ฃน์ผ๋ก ๋ณํํ์ฌ Set์ ์ ์ฅ
Map<Integer, Map<Integer, Set<Student.Level>>> stuByHakAndBan
= stuStream.collect(groupingBy(Student::getHak,
groupingBy(Student::getBan,
mapping(s-> {
if(s.getScore()>=200) return Student.Level.HIGH;
else if(s.getScore()>=100) return Student.Level.MID;
else return Student.Level.LOW;
} , toSet()))));
8. ์คํธ๋ฆผ ๋ณํ ์ ๋ฆฌ
from | to | ๋ณํ๋ฉ์๋ |
---|---|---|
1. ์คํธ๋ฆผ -> ๊ธฐ๋ณธํ ์คํธ๋ฆผ | ||
Stream |
IntStream | mapToInt (ToIntFunction |
LongStream | mapToLong (ToLongFunction |
|
DoubleStream | mapToDouble (ToDoubleFunction |
|
2. ๊ธฐ๋ณธํ ์คํธ๋ฆผ -> ์คํธ๋ฆผ | ||
IntStream | Stream |
|
LongStream | Stream |
boxed() |
DoubleStream | Stream |
|
Stream | mapToObj (DoubleFunction mapper) | |
3. ์คํธ๋ฆผ์ ์คํธ๋ฆผ -> ์คํธ๋ฆผ | ||
Stream |
Stream |
flatMap (Function mapper) |
Stream |
IntStream | flatMapToInt (Function mapper) |
Stream |
LongStream | flatMapToLong (Function mapper) |
Stream |
DoubleStream | flatMapToDouble (Function mapper) |
4. ์ปฌ๋ ์ -> ์คํธ๋ฆผ | ||
Collection |
Stream |
stream() |