스트림은 데이터의 흐름이다.
배열 , 컬렉션의 저장요소를 하나씩 참조해서 람다식으로 처리할 수 있도록 해주는 반복자이다.
스트림은 데이터 소스로부터 데이터를 읽기만 할 뿐, 변경할 수 없다.
스트림을 이용하면 선언형으로 컬렉션 데이터를 처리할 수 있다.
컴퓨터 프로그램을 작성하는 방법 중 하나로,
프로그램의 실행 방법을 기술하는 것이 아니라,
프로그램이 해야 할 일을 기술하는 것이다.
즉 , 코드를 작성하면 동작원리를 모르더라도 어떤 결과가 나올지 알 수 있다.
어떻게 하느냐의 방법은 중요하지 않다.
public class StreamTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("홍길동");
list.add("김길동");
list.add("이길동");
list.add("박길동");
list.add("최길동");
//스트림 생성
Stream<String> stream = list.stream();
//스트림을 이용한 반복
stream.forEach(s -> System.out.println(s));
}
}
외부 반복자는 개발자가 직접 컬렉션의 요소를 반복해서 처리해야 한다. (for while)
반면에 내부 반복자는 컬렉션의 요소를 반복해서 처리하는 반복자를 내부에 가지고 있다.
이 뜻은 스트림을 이용하면 개발자가 직접 요소를 반복해서 처리할 필요가 없다는 것이다.
스트림은 컬렉션, 배열, 정수 범위, 파일, 람다식으로 생성할 수 있다.
public class StreamTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("홍길동");
list.add("김길동");
list.add("이길동");
list.add("박길동");
list.add("최길동");
//스트림 생성
Stream<String> stream = list.stream();
//스트림을 이용한 반복
stream.forEach(s -> System.out.println(s));
}
}
public class StreamTest {
public static void main(String[] args) {
String[] arr = {"홍길동", "김길동", "이길동", "박길동", "최길동"};
Stream<String> stream = Arrays.stream(arr);
stream.forEach(s -> System.out.println(s));
}
}
스트림은 주로 컬렉션 및 배열을 통해서 얻지만 정수 범위, 파일, 람다식으로도 생성할 수 있다.
public class StreamTest {
public static void main(String[] args) {
IntStream stream = IntStream.rangeClosed(1, 10);
stream.forEach(s -> System.out.println(s));
}
}
public class StreamTest {
public static void main(String[] args) {
try {
Stream<String> stream = Files.lines(Paths.get("C:\\Users\\user\\Desktop\\test.txt"), StandardCharsets.UTF_8);
stream.forEach(s -> System.out.println(s));
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class StreamTest {
public static void main(String[] args) {
Stream<String> stream = Stream.of("홍길동", "김길동", "이길동", "박길동", "최길동");
stream.forEach(s -> System.out.println(s));
}
}
중간 연산은 최종 연산이 수행되기 전까지는 중간 연산을 수행하지 않는다.
중간 연산은 여러개를 연결해서 사용할 수 있다.
public class StreamTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("홍길동");
list.add("김길동");
list.add("이길동");
list.add("박길동");
list.add("최길동");
Stream<String> stream = list.stream();
stream.filter(s -> s.startsWith("김"))
.forEach(s -> System.out.println(s));
}
}
filter(Predicate<T> predicate)
: 조건에 맞는 요소만 추출distinct()
: 중복 요소 제거sorted()
: 요소 정렬sorted(Comparator<T> comparator)
: 요소 정렬peek(Consumer<T> action)
: 요소를 소비하면서 중간 결과를 확인limit(long maxSize)
: 요소의 개수 제한skip(long n)
: 요소 건너뛰기map(Function<T, R> mapper)
: 요소의 타입 변환flatMap(Function<T, Stream<R>> mapper)
: 요소의 타입 변환 후 평탄화mapToInt(ToIntFunction<T> mapper)
: 요소의 타입을 int로 변환mapToLong(ToLongFunction<T> mapper)
: 요소의 타입을 long으로 변환mapToDouble(ToDoubleFunction<T> mapper)
: 요소의 타입을 double로 변환mapToObj(IntFunction<R> mapper)
: 요소의 타입을 참조 타입으로 변환mapToLong(LongFunction<R> mapper)
: 요소의 타입을 참조 타입으로 변환mapToDouble(DoubleFunction<R> mapper)
: 요소의 타입을 참조 타입으로 변환boxed()
: 요소의 타입을 참조 타입으로 변환위와 같은 지원 하는 함수들이 있고 중간 연산 을 통해서 다양한
연산을 파이프 라인화 시켜서 연산 할 수 있다.
최종 연산은 스트림을 소모하면서 연산을 수행한다.
forEach(Consumer<T> action)
: 요소를 소비forEachOrdered(Consumer<T> action)
: 요소를 소비toArray()
: 요소를 배열로 변환toArray(IntFunction<A> generator)
: 요소를 배열로 변환reduce(BinaryOperator<T> accumulator)
: 요소를 하나로 합침reduce(T identity, BinaryOperator<T> accumulator)
: 요소를 하나로 합침reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner)
: 요소를 하나로 합침collect(Collector<? super T, A, R> collector)
: 요소를 수집collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner)
: 요소를 수집min(Comparator<? super T> comparator)
: 최소값을 찾음max(Comparator<? super T> comparator)
: 최대값을 찾음count()
: 요소의 개수를 센다.anyMatch(Predicate<? super T> predicate)
: 조건에 맞는 요소가 있는지 확인allMatch(Predicate<? super T> predicate)
: 모든 요소가 조건에 맞는지 확인noneMatch(Predicate<? super T> predicate)
: 모든 요소가 조건에 맞지 않는지 확인findFirst()
: 첫 번째 요소를 찾음findAny()
: 임의의 요소를 찾음iterator()
: 요소를 순회하는 반복자를 반환상당히 지원하는 최종 연산자가 많지만 대표적인 것들만 나열했다.
Match 연산은 조건에 맞는 요소가 있는지 확인하는 연산이다.
public class MatchExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("홍길동", "신용권", "김자바", "람다식", "박병렬");
boolean hasName = list.stream().anyMatch(s -> s.startsWith("신"));
System.out.println("hasName = " + hasName);
boolean noName = list.stream().noneMatch(s -> s.startsWith("신"));
System.out.println("noName = " + noName);
}
}
실행 결과
hasName = true
noName = false
Reduce 연산은 요소를 하나로 합치는 연산이다.
public class ReduceExample {
public static void main(String[] args) {
List<Student> studentList = Arrays.asList(
new Student("홍길동", 10),
new Student("신용권", 20),
new Student("김자바", 30)
);
int sum1 = studentList.stream()
.mapToInt(Student::getScore)
.sum();
int sum2 = studentList.stream()
.map(Student::getScore)
.reduce((a, b) -> a + b)
.get();
int sum3 = studentList.stream()
.map(Student::getScore)
.reduce(0, (a, b) -> a + b);
System.out.println("sum1 = " + sum1);
System.out.println("sum2 = " + sum2);
System.out.println("sum3 = " + sum3);
}
}
실행 결과
sum1 = 60
sum2 = 60
sum3 = 60