17 스트림 API
18장에서 스트림을 사용하게 된다. IO STREAM
입출력스트림인지 그냥 스트림인지 문맥에 맞게 봐야한다.
스트림은 elememt를 순차적으로 처리하기 위해 모아놓은 클래스이다.
순차적으로 하는것은 while문이나 for문으로 처리해왓다.
스트림으로 처리한걸 고전적으로 처리해도 된다.
스트림이 더빠르다고 할 순 없다. 코드가 간결해지냐의 차이일뿐이다.
초보자가 배우기 어렵다.
17.1 stream.of()
stream.of()메소드는 스트림을 직접만드는 것이다.
여러 원소(element)의 순차적 처리 기능(메소드)을 제공하는 클래스, 인터페이스이다.
스태틱 메소드이다. 파라미터로 여러개의 값을 받는다.
스트림 연산은 최종연산(terminal operation)과 중간연산(intermediate operation)으로 나뉜다.
최종연산의 리턴타입은 스트림이 아니고 중간연산의 리턴타입은 스트림이다.
스트림은 흘러간다는 느낌이라 최종연산후 재사용이 불가능하다.
최종연산을 한번하면 다시 스트림을 열어야한다.
중간연산이 중간마다 실행되는게 아니라 최종연산시 중간연산도 같이 실행된다.
public class C01Stream {
public static void main(String[] args) {
//스트림 만들기
Stream<Integer> stream1 = Stream.of(5, 1, 3, 9, 10, 0);
//개수세기
stream1.count();
//제일 처음 것 찾기
stream1.findFirst();
}
}17.2 컬렉션 stream
element들을 순차적으로 처리하기 위한 목적의 메소드를 가지고 있다.
그래서 element들을 가지고 있는 것 즉 컬렉션을 가지고 스트림을 만드는 경우가 많다.
컬렉션.stream();하면 컬렉션의 스트림을 얻을 수 있다.
public class C03Stream {
public static void main(String[] args) {
// collection -> stream
Collection<Integer> list = List.of(9, 8, 7, 1, 2, 3, 0);
Stream<Integer> stream1 = list.stream();
long count = stream1.count();
System.out.println(count); //7
Stream<Integer> stream2 = list.stream();
Optional<Integer> max = stream2.max(Integer::compareTo);
System.out.println(max.get()); //9
}
}public class C04Stream {
public static void main(String[] args) {
// collection -> stream
Collection<Integer> set = Set.of(9, 2, 1, 10, 20, 90, 0, 3);
Stream<Integer> stream1 = set.stream();
long count = stream1.count();
System.out.println(count); //8
Stream<Integer> stream2 = set.stream();
Optional<Integer> max = stream2.max(Integer::compareTo);
System.out.println(max.get()); //90
}
}17.2.1 배열 스트림
배열에서도 스트림을 얻을 수 있다.
Arrays.stream(배열) 하면 스트림을 얻을 수 있다.
public class C05Stream {
public static void main(String[] args) {
// array -> stream
String[] arr = { "java", "spring", "jsp", "html" };
Stream<String> stream1 = Arrays.stream(arr);
System.out.println(stream1.count());
Stream<String> stream2 = Arrays.stream(arr);
System.out.println(stream2.max(String::compareTo).get());
}
}17.2.2 기본타입 스트림
스트림에도 종류가 여러개가 잇다.
그냥 스트림은 참조타입을 위한 것이고 기본타입을 위한 스트림들도있다.
IntStream / LongStream / DoubleStream
public class C06Stream {
public static void main(String[] args) {
// 원소가 참조타입인 스트림
Stream<String> stream1 = Stream.of("java", "spring", "css");
// 원소가 기본타입인 스트림
IntStream stream2 = IntStream.of(3, 9, 10, 1);
LongStream stream3 = LongStream.of(9, 10, 11, 2);
DoubleStream stream4 = DoubleStream.of(3.14, 9.8, 7.1, 12.12);
int[] intarr = { 9, 10, 11, 12 };
IntStream stream5 = Arrays.stream(intarr);
// Stream<Integer> stream6 = Arrays.stream(intarr); // x
}
}결론은 스트림은 원소의 순차적 처리를 위한 것이다.
최종연산 중간연산이 잇다는 것만 알아두자.
17.2.3 range(), rangeClosed()
스트림은 보통 컬렉션의 원소로 만드는 경우가 많이 있다.
IntStream의 스트림을 만드는 특별한 메소드가 있다.
특정영역의 정수들을 스트림으로 만들 수 있다.
range(시작값, 종료값) : 시작값 이상 ~ 종료값 미만
rangeClosed(시작값, 종료값) : 시작값 이상 종료값 이하
public class C07Stream {
public static void main(String[] args) {
// IntStream
IntStream stream1 = IntStream.range(0, 10); // 0~9
System.out.println(stream1.count());
IntStream stream2 = IntStream.rangeClosed(0, 10); // 0~10
System.out.println(stream2.count());
}
}17.3 최종연산(terminal)
최종연산에는 여러가지가 있다.
count : 원소의 개수
forEach : 각 원소 처리
max : 최대값
min : 최소값
findFirst : 첫원소
findAny : 아무 원소
allMatch : 모두 일치하는지?
anyMatch : 하나라도 일치하는지?
noneMatch : 모두 일치하지 않는지
reduce : 집계연산(평균 등)
collect : 다른 컬렉션으로 만들기
17.3.1 count
스트림을 얻고 하나씩 나가는데 보통 어떻게 사용하느냐?
바로 이어서 사용하는것을 많이 사용한다.
Stream<String> stream2 = list.stream();
long count2 = stream2.count();
long count3 = list.stream().count();
System.out.println(count3); // 3중간연산할때마다 앞에 엔터를 눌러주면 가독성이 좋아진다.
long count4 = list.stream()
.map(s -> s + s)
.filter(s -> s.startsWith("c"))
.count();
System.out.println(count4); // 1중간중간에 변수에 담지 않고 한번에 사용하는 방식을 사용하는데
이것을 메소드 체이닝이라고 한다.
17.3.2 ForEach
컬렉션에서 본 것과 같은 역할을 한다. 람다식으로 처리를 한다.
Comsumer타입의 인터페이스가 들어간다.
accept(T t)메소드를 가지고 잇는데 element들이 하나씩 들어가는 메소드이다.
그래서 이 추상메소드가 어떤일을 하는지 정해주어야 한다.
스트림은 functional interface를 받는 일이 많아서 람다식을 잘 활용해야한다.
public class C03ForEach {
public static void main(String[] args) {
List<String> list = List.of("java", "css", "html", "jsp");
list.stream()
.forEach(s -> System.out.println(s));
list.stream()
.forEach(s -> System.out.println("원소: " + s));
}
}17.3.3 StringBuilder
StringBuilder String과는 다르다.
String은 중간에 객체를 변경할 수 없다.
StringBuilder는 문자열 수정이 가능하다.
String의 + 연산은 새로운 String객체가 생성되고 이전객체가 계속 버려지는 것 때문에 효율성이 좋다고 볼수는 없다.
public class StringBuilderExample {
public static void main(String[] args) {
String a = "java"; // hello
String b = "html";
String c = a + "hello";
String d = b + "hello";
StringBuilder e = new StringBuilder("java");
StringBuilder f = new StringBuilder("html");
System.out.println(e); //java
System.out.println(f); //html
e.append("hello");
f.append("hello");
System.out.println(e); //javahello
System.out.println(f); //htmlhello
}
}17.3.4 Match
allMatch(모두만족) / anyMatch(한개라도) / noneMatch(다 불만족)
모두 리턴타입 boolean타입이다.
predicate 함수형 인터페이스를 파라미터로 받는다.
test(T t)라는 메소드를 가지고 있다. 파라미터를 하나씩 받아서 boolean타입을 리턴한다.
모두 통과하는지 아닌지를 확인하는 메소드이다.
추상메소드는 정해진게 아니니 작성해주기 나름이다.
public class C04Match {
public static void main(String[] args) {
List<Integer> list = List.of(3, 5, 7, 9, 10);
boolean r1 = list.stream().allMatch(e -> e > 0);
System.out.println(r1); //true
boolean r2 = list.stream().anyMatch(e -> e < 0);
System.out.println(r2); //false
boolean r3 = list.stream().noneMatch(e -> e < 0);
System.out.println(r3); //true
}
}17.3.4.1 leet문제 활용
고전적방법으로 순회하는 것을 스트림으로 변경하였다.
람다도 익숙해야하고 스트림도 익숙해야한다.
Set<Entry<Integer, Integer>> entrySet = map.entrySet();
for (Entry<Integer, Integer> entry : entrySet) {
if (entry.getValue() % 2 != 0) {
return false;
}
}return map.values()
.stream()
.anyMatch(e -> (e % 2) == 0);17.3.5 Find
findFirst : 처음것 찾기 findAny: 아무거나 찾기
스트림의 T타입을 반환하는게 아니라 Optional<T> 객체를 반환한다.
Optional<T>은 이름에서 부터 느껴지듯이 있을수도 있고 없을수도 있는 느낌이다.
감싸고 있는 object이다. 감싸고 잇는 것이 제네릭 타입이다.
품고잇는 element가 비엇냐 라는 것 같은 것이다.
isPresent() : 있나?
isEmpty() : 비어있나?
get() : 가져와라
orElse(파라미터) : 있으면 가져오고 없으면 파라미터를 출력
public class C06Find {
public static void main(String[] args) {
List<Integer> list = List.of(3, 1, 9, 0, 2, 4);
Optional<Integer> v1 = list.stream().findFirst();
System.out.println(v1.get()); // 3
Integer v2 = list.stream().findAny().get();
System.out.println(v2); // 3
//빈 리스트
List<Integer> list2 = List.of();
Optional<Integer> v3 = list2.stream().findFirst();
System.out.println(v3.isPresent()); // false
if (v3.isPresent()) {
System.out.println(v3.get());
}
System.out.println(v3.orElse(0)); // 0
}
}17.4 max, min
comparator를 파리미터로 받는다.
스트림은 체이닝을 많이 사용해서 이해하기 어렵다.
public class C07MaxMin {
public static void main(String[] args) {
List<Integer> list = List.of(3, 4, 1, 2, 10, 20);
Integer max = list.stream()
.max(Integer::compare) // (x, y) -> Integer.compare(x,y)
.get();
System.out.println(max); //20
Integer min = list.stream()
.min(Integer::compareTo) // (x, y) -> x.comparTo(y)
.get();
System.out.println(min); //1
}
}public class C08MaxMin {
public static void main(String[] args) {
int[] arr = { 3, 1, 9, 0, 2, 4, 5 };
int max = Arrays.stream(arr)
.max()
.getAsInt();
System.out.println(max); //9
int min = Arrays.stream(arr)
.min()
.getAsInt();
System.out.println(min); //0
}
}17.5 reduce() 집계연산
줄이다 감소시키다. 여러 원소들을 하나로 줄이는 것이다.
이전에 배운것들도 reduce의 일종이다. 이것들을 커스텀하는 것이 reduce()이다.
intstream에는 항목들을 더하는 sum()이 있지만 리스트스트림에는 없어서 reduce()를 사용해줘야한다.
reduce메소드는 파라미터로 binaryoperator인터페이스를 받는 것이 있다.
apply라는 추상메소드를 가지고 있다. 두개의 파라미터를 받아서 하나를 리턴한다.
두개의 element가 들어가고 그 결과가 다시 첫번째 파라미터로 들어가는 형식이다.
public class C09Reduce {
public static void main(String[] args) {
List<Integer> list = List.of(9, 1, 2, 3, 5);
Integer sum = list.stream()
.reduce((a, b) -> a + b)
.get();
System.out.println(sum); //20
Integer max = list.stream()
.reduce(Math::max) // (a,b) -> Math.max(a,b)
.get();
System.out.println(max); //9
Integer min = list.stream()
.reduce(Math::min) //(a,b) -> Math.min(a,b)
.get();
System.out.println(min); //1
}
}public class C10Reduce {
public static void main(String[] args) {
List<String> list = List.of("java", "css", "html", "jsp");
Integer sum = list.stream()
.map(e -> e.length()) //map하면 리스트 스트림이 인트스트림이 된다.
.reduce(Integer::sum)
.get();
System.out.println(sum); //14
}
}17.5.1 reduce메소드 - 2
초기값을 파라미터로 받는 reduce도 있다.
reduce(초기값, 파라미터2)
초기값과 다음 파라미터를 연산한 후 다음 element를 하나씩 넣는다.
public class C11Reduce {
public static void main(String[] args) {
List<Integer> list = List.of(3, 4, 5, 1);
Integer r1 = list.stream()
.reduce(0, Integer::sum);
System.out.println(r1); // 13
Integer r2 = list.stream()
.reduce(1, (a, b) -> a * b);
System.out.println(r2); // 60
}
}17.6 collect()
다른 컬렉션으로 만드는 최종연산이다.
17.6.1 collect(supplier, biconsumer, biconsumer)
세개의 파라미터가 있다. 자주일어나는것은 하나의 파라미터만 넣는 것이다.
첫번째 파라미터는 수집한 element의 목적지 supplier 원소가 들어간 객체가 리턴된다
두번째 파라미터는 어떻게 수집할 것인지 어떻게 넣을 건지
세번째 파라미터는 여러 supplier에서 만든 객체를 어떻게 합칠건지
public class C12Collect {
public static void main(String[] args) {
List<Integer> list = List.of(1, 2, 3, 9, 8, 7);
List<Integer> r1 = list.stream()
.collect(ArrayList::new, List::add, List::addAll);
//.collect(ArrayList::new, (a, b) -> a.add(b), (a, b) -> a.addAll(b));
System.out.println(r1);
}
}map 스트림으로 만들기
map.compute() 키밸류쌍을 받아서 새로운 value를 리턴하는 메소드이다.
키가 null이면 value를 1 아니라면 value + 1
그리고 여러 HashMap이 만들어졌으니 map의 putAll을 사용해서 하나의 map으로 만들기
public class C13Collect {
public static void main(String[] args) {
List<Integer> list = List.of(3, 2, 3, 2, 2, 2);
Map<Integer, Integer> r1 = list.stream()
.collect(HashMap::new,
(map, e) -> map.compute(e, (k, v) -> v == null ? 1 : v + 1),
Map::putAll);
System.out.println(r1);
}
}17.6.2 collect(collector)
위의 파라미터를 여러개 사용하는 것은 거의 사용하지 않는다.
collector인터페이스를 파라미터로 받는 collect()메소드가 있다.
collector를 만드는 유틸리티 클래스가 있다.
Collectors라는 클래스의 메소드들이 collector를 리턴한다.
여기서 필요한것을 꺼내서 사용하면된다.
17.6.3 toList()
public class C14Collect {
public static void main(String[] args) {
List<Integer> list = List.of(1, 2, 5, 9, 10);
List<Integer> r1 = list.stream()
.collect(Collectors.toList());
System.out.println(r1);
}
}17.6.4 Collectors.groupingBy()
Function인터페이스를 파라미터로 받는다.
파라미터를 하나받고 리턴하는 추상메소드를 가지고 있다.
어떤 그룹으로 받냐면 classifier로 묶는다.
키에 리스트인 값이 포함되게 하는 것이다.
고전적 for문으로 사용할 수도 있다.
public class C15Collect {
public static void main(String[] args) {
List<String> list = List.of("java", "css", "html", "jsp");
Map<Integer, List<String>> r1 = list.stream()
.collect(Collectors.groupingBy(s -> s.length()));
System.out.println(r1); // {3=[css, jsp], 4=[java, html]}
//고전적 for문
Map<Integer, List<String>> r2 = new HashMap<>();
for (String s : list) {
Integer key = s.length();
List<String> val = r2.get(key);
if (val == null) {
val = new ArrayList<>();
r2.put(key, val);
}
val.add(s);
}
System.out.println(r2); //{3=[css, jsp], 4=[java, html]}
}
}17.6.5 Collectors.groupingBy()-2
하나의 파라미터만 받으면 그 파라미터가 키가 된다.
groupingBy(변수기준키, reduce)
첫번째 파라미터는 변수기준 키를 어떻게 할건지를 받고
두번째파라미터는 어떻게 reduce할건지를 받는다.
고전적인 방법으로도 가능하다.
public class C16Collect {
public static void main(String[] args) {
List<Integer> list = List.of(3, 2, 3, 2, 2, 2);
Map<Integer, Long> r1 = list.stream()
.collect(Collectors.groupingBy(e -> e, Collectors.counting()));
System.out.println(r1);
Map<Integer, Integer> r2 = new HashMap<>();
for (Integer n : list) {
Integer oldValue = r2.get(n);
if (oldValue == null) {
r2.put(n, 1);
} else {
r2.put(n, oldValue + 1);
}
}
System.out.println(r2);
}
}17.6.6 groupingBy()
우리가 만든 클래스로도 가능하다.
class Car {
private String name;
private String type;
private int price;
//생성자 getter setter toString
}public class C17Collect {
public static void main(String[] args) {
List<Car> list = List.of(
new Car("avante", "middle", 2000),
new Car("genesis", "heavy", 4000),
new Car("casper", "light", 1500),
new Car("morning", "light", 1000),
new Car("sonata", "heavy", 2500),
new Car("k5", "middle", 2500));
// 타입이 키
Map<String, List<Car>> r1 = list.stream()
.collect(Collectors.groupingBy(Car::getType));
for (var entry : r1.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
// 타입이 키가되고 밸류는 가격의 합
Map<String, Integer> r2 = list.stream()
.collect(Collectors.groupingBy(Car::getType,
Collectors.summingInt(Car::getPrice)));
r2.entrySet()
.stream()
.forEach(e -> System.out.println(e.getKey() + ":" + e.getValue()));
}
}17.6.7 확인문제 8번
public class Member {
private String name;
private String job;
//생성자 getter..
}public class Example1 {
public static void main(String[] args) {
List<Member> list = List.of(
new Member("홍길동", "개발자"),
new Member("김나리", "디자이너"),
new Member("신용권", "개발자"));
Map<String, List<Member>> map = new HashMap<>();
// code..
for (Member member : list) {
List<Member> val = map.get(member.getJob());
if (val == null) {
val = new ArrayList<>();
map.put(member.getJob(), val);
}
val.add(member);
}
System.out.println("[개발자]");
List<Member> dev = map.get("개발자");
for (Member d : dev) {
System.out.println(d);
}
//[개발자]
//{name:홍길동, job:개발자}
//{name:신용권, job:개발자}
System.out.println();
System.out.println("[디자이너]");
List<Member> des = map.get("디자이너");
for (Member d : des) {
System.out.println(d);
//[디자이너]
//{name:김나리, job:디자이너}
}
}
}public class Example2 {
public static void main(String[] args) {
List<Member> list = List.of(
new Member("홍길동", "개발자"),
new Member("김나리", "디자이너"),
new Member("신용권", "개발자"));
Map<String, List<Member>> map = list.stream()
.collect(Collectors.groupingBy(Member::getJob));
System.out.println("[개발자]");
map.get("개발자").stream()
.forEach(System.out::println);
System.out.println();
System.out.println("[디자이너]");
map.get("디자이너").stream()
.forEach(System.out::println);
}
}'국비 > Java' 카테고리의 다른 글
| 2023.03.21 38일차 Java (0) | 2023.03.21 |
|---|---|
| 2023.03.20 37일차 Java (0) | 2023.03.20 |
| 2023.03.16 35일차 Java (0) | 2023.03.16 |
| 2023.03.15 34일차 Java (0) | 2023.03.15 |
| 2023.03.14 33일차 Java (0) | 2023.03.14 |