내부반복과 외부반복
외부 반복
• 컬렉션이나 배열의 원소를 다룰 때, 원소의 반복 처리를 프로그램에서 명시적으로 제어하는 방식
원소를 프로그램에서 선언된 변수로 복사한 후, 작업을 처리함
for, 향상된 for, while, do-while, Iterator 등을 이용한 반복 작업이 외부 반복에 해당됨
List<String> names = Arrays.asList("Kim", "Lee", "Park");
for (String name : names) {
System.out.println(name);
}
Iterator<String> iterator = names.listIterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
내부 반복과 forEach 메소드()
• 원소의 반복 처리를 컬렉션 또는 스트림과 같은 데이터 구조 내부에서 반복을 처리하는 방식
프로그램에서 스트림 API를 이용하여 반복 처리를 위임하고, 처리 코드만 람다식으로 제공함
• 예: 컬렉션의 forEach() 메소드를 이용하고, 매개변수로 람다식을 지정
forEach() 메소드는 함수형 인터페이스인 Consumer 객체를 인자로 받음
List<String> names= Arrays.asList("Kim", "Lee", "Park");
// 내부 반복
names.forEach(item > System.out.println("내부 반복: + item));
• 코드가 간결해 지며, 가독성이 좋아지고,
병렬처리가 가능하며, 성능 최적화에 유리
스트림
컬렉션이나 배열과 같은 데이터 소스로부터 만들어지는 원소의 시퀀스를 표현하며,
간결하고 효율적 처리 방법을 제공하는 인터페이스
- 내부반복과 함수형 프로그래밍 방식을지원
- 멀티코어 cpi를 활용한 병렬처리지원
- 관련 클래스와 인터페이스는 java.util.stream패키지에있음
- 다양한 집합체 연산을지원함 (중간처리 연산과 최종처리연산)
filter(), map(), sorted(), count(), collect(), anyMatch()등
스트림의 특성
• 데이터 원본으로부터 스트림을 생성하며, 원본 데이터를 변경하지 않음
원본 데이터의 변경이 필요하면 새로운 스트림을 생성해야 함
• 스트림은 일회용
• 스트림 연산을 파이프라인 형태로 연결할 수 있음
• 지연 평가(lazy evaluation)를 통해 연산을 최적화
java.util.stream 패키지에 포함된 스트림 인터페이스
숫자와 스트림
• IntStream, LongStream, DoubleStream 인터페이스
기본형 int, long, double형의 원소로 이루어진 데이터를 다루기 위한 스트림
주요 기능 (예: IntStream의 경우)
- static IntStream range(int, int), static IntStream rangeClosed(int, int)
- static IntStream of (int... values)
- sum(), average(), min(), max() 등
- filter(), map(), reduce() 등
System.out.println(IntStream.rangeClosed(1, 100) sum();
System.out.println(IntStream.rangeClosed(1, 100). average().getAsDouble());
System.out.println(IntStream.rangeClosed(1, 100).min().getAsInt());
System.out.println(IntStream.rangeClosed(1, 100).max().getAsInt());
배열과 스트림
• Arrays.stream()를 사용하여 배열로부터 스트림을 생성할 수 있음
• Arrays 클래스에서 제공되는 메소드
static DoubleStream stream(double[] array)
static IntStream stream(int[] array) static <T> Stream<T> stream(T[])
- 원소가 객체인 배열로부터 스트림 생성
- Stream은 제네릭 인터페이스
String[] strArray = { "홍길동", "이순신", "김유신" };
Stream<String> strStream = Arrays.stream(strArray);
strStream.forEach(item -> System.out.println(item));
int[] intArray = { 1, 2, 3 };
IntStream intStream = Arrays.stream(intArray);
intStream.forEach(item -> System.out.println(item));
파일과 스트림
• Files.lines(Path)을 사용하여 텍스트 파일로부터 행 단위 문자열로 구성된 스트림을 생성할 수 있음
Path path = Paths.get("c:\\data\\data.txt");
Stream<String> fileStream = Files. lines(path);
fileStream.forEach(line -> System.out.println(line));
fileStream.close();
컬렉션과 스트림
Collection 유형의 객체인 경우
• Collection 인터페이스 에서 stream()과 parallelStream() 메소드가 디폴트 메소드로 제공됨
parallelStream()은 병렬처리가 가능한 스트림을 리턴함
• HashSet, ArrayList, LinkedList 객체 등으로부터 스트림을 생성할 때 사용• HashMap 객체의 경우
먼저 entrySet()을 사용하여 Set 유형의 객체를 얻은 후, stream() 또는 parallelStream() 메소드를 사용하여 스트림을 생성함
HashSet과 스트림 사용 예
import java.util.*;
import java.util.stream.*;
public class Main {
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
for (int i = 0; i < 10; i++) {
set.add(i);
}
// 일반 스트림 사용.
System.out.println("일반 스트림 사용");
Stream<Integer> n_stream = set.stream();
n_stream.forEach(item -> System.out.println(item));
// 병렬 스트림 사용.
System.out.println("병렬처리 스트림 사용");
Stream<Integer> p_stream = set.parallelStream();
p_stream.forEach(item -> System.out.println(item + "(" +
Thread.currentThread().getName() + ")"));
}
}
/*결과
일반 스트림 사용
0
1
2
3
4
5
6
7
8
9
병렬처리 스트림 사용
6(ForkJoinPool.commonPool-worker-2)
7(ForkJoinPool.commonPool-worker-2)
4(ForkJoinPool.commonPool-worker-1)
5(ForkJoinPool.commonPool-worker-1)
8(main)
9(main)
2(ForkJoinPool.commonPool-worker-3)
0(ForkJoinPool.commonPool-worker-4)
1(ForkJoinPool.commonPool-worker-4)
3(ForkJoinPool.commonPool-worker-3)
*/
병렬처리 : 여러개의 스트림으로나뉘어져 해당스레드별로 병렬처리가됨.
현재스레드의이름이 나오는 ... currentThread().getName()
ArrayList와 스트림 사용 예
• 객체를 원소로 갖는 ArrayList와 스트림
import java.util.*;
import java.util.stream.*;
class People {
String name; int age;
People(String name, int age) {
this.name name; this.age = age;
}
public String toString()) {
return ("name : +this.name + ", age: " + this.age);
}
}
public static void main(String[] {
List Peoples list = new ArrayList<People>();
list.add(new People("홍길동", 30));
list.add(new People("이순신", 40));
list.add(new People("김유신", 50));
list.add(new People("유관순", 20));
// 일반 스트림 사용.
System.out.println("일반 스트림 사용");
Stream<People> n stream = list.stream();
n_stream.forEach(item-> System.out.println(item));
// 병렬 스트림 사용
System.out.println("병렬처리 스트림 사용");
Stream<People> p_stream = list.parallelStream()
p_stream.forEach(item -> System.out.println(item + ":" + Thread.currentThread().getName()));
}
/* 결과
일반 스트림 사용
name : 홍길동, age : 30
name : 이순신, age : 40
name : 김유신, age : 50
name : 유관순, age : 20
병렬처리 스트림 사용
name : 김유신, age : 50 : main
name : 이순신, age : 40 : ForkJoinPool.commonPool-worker-1
name : 홍길동, age : 30 : main
nam e: 유관순, age : 20 : ForkJoinPool.commonPool-worker-2
*/
스트림 파이프라인과 중간연산
• 컬렉션, 배열, 또는 파일로부터 생성된 스트림에 어떤 처리를 수행하고 새로운 스트림이 만들어지며, 계속해서 이러한 처리 과정을 반복하는 것
메소드 체이닝을 사용하여 파이프라인을 구축할 수 있음
• 중간연산(또는 중간처리)
원본 스트림에서 데이터를 변환하거나 필터링 등으로 새로운 스트림을 생성하는 연산
중간에서 생성된 스트림은 다음 연산으로 연결되어 파이프라인을 형성함
filter(), map(), sorted( ) 등의 연산
스트림 파이프라인과 종료연산
• 종료연산(또는 최종연산)
중간을 거친 스트림에 대해 최종적인 결과를 만들거나 동작을 수행하는 부분
스트림 파이프라인에서 마지막에 한 번만 수행됨
forEach(), collect(), count(), any Match, reduce() 등
List<String> words= Arrays.asList("apple", "banana", "cherry", "Avocado">
long count = words.stream() // 1.원본 스트림
.map(String::toUpperCase) // 2.중간연산
.filter(word > word.startsWith("A")) // 3.중간연산
.count(); // 4.최종연산
System.out.println(count); // 2가 출력됨
1.원본스트림생성, 2.대문자로변환, 3.A로시작한것만 true로 거른다, 4. 갯수를샌다
스트림 파이프라인과 메소드 체이닝
• 메소드 체이닝의 활용- 메소드의 연속적 호출
List<Book books = new ArrayList<Book>();
Stream<Book> stream = books.stream();
IntStream price = stream.mapToInt(book -> book.getPrice());
double avg = price.average().getAsDouble();
System.out.println(avg);
List<Book> books = new ArrayList<Book>();
... ...
double avg = books.stream()
.mapToInt(book -> book.getPrice())
.average().getAsDouble();
System.out.println(avg);
필터링
• 컬렉션 원소들 중에서 중복을 제거하거나 특정 조건을 만족하는 원소만 추출하여 새로운 스트림을 생성하는 작업
Stream<T>, IntStream, LongStream, DoubleStream에서
필터링 메소드는 중간 연산이므로 같은 스트림을 리턴함
중복 제거에는 distinct() 메소드를 사용
- 원소가 객체인 경우 hashCode()의 리턴값이 같고, equals( )로 비교할 때 true인 경우, 중복으로 판단함
조건을 통한 걸러 내기에는 filter() 메소드를 사용
- filter() 메소드의 매개변수는 함수형 인터페이스이며, true 또는 false를 리턴하는 람다식을 매개변수로 전달
필터링 예(1)
• distinct() 메소드로 중복을 제거하는 필터링 스트림 예
public class Main {
public static void main(String[] args) {
List<Book> books = new ArrayList<Book>();
books.add(new Book ("JAVA", "홍길동", 10000));
books.add(new Book ("PHP", "이순신", 20000));
books.add(new Book("빅데이터 연구", "유관순", 12000));
books.add(new Book("커뮤니케이션 이론", "강감찬", 15000));
books.add(new Book ("PHP", "01", 20000));
books.add(new Book("알고리즘", "권율", 17000));
books.stream()
.distinct()
.forEach (book -> System.out.println(book));
}
}
/* 결과
title: JAVA, author: 홍길동, price: 10000
title: PHP, author: 이순신, price: 20000
title: 빅데이터 연구, author: 유관순, price: 12000
title: 커뮤니케이션 이론, author: 강감찬, price: 15000
title: 알고리즘, author: 권율, price:17000
*/
distinct -중복제거해서 php프로그래밍-이순신이 제거
필터링 예(2)
• filter() 메소드를 사용하는 필터링 스트림 예
public class Main {
public static void main(String[] args) {
String[] strArray = {"ABC", "BCD", "AFE", "CDE", "ABZ", "ACCZ"};
List<String> strList = Arrays.asList(strArray);
strList.stream()
.filter(item -> item.startsWith("A"))
.filter(item -> item.endsWith("Z"))
.filter(item -> item.length() > 3)
.forEach(item-> System.out.println(item));
strList.stream()
.filter(item->item.startsWith("A") & item.endsWith("Z") & item.length() > 3)
.forEach(item-> System.out.println(item));
}
}
/* 출력
ACCZ
ACCZ
*/
filter로 A로시작하는것, Z로끝나는것, 3글자이상인것들만 출력
매핑
• 원소들을 다른 원소로 변환하여 새로운 스트림을 생성하는 작업
Stream<T>에서 map()은 매개변수로 주어지는 함수(람다식)를 적용하여 Stream<T>로, mapTolnt()은 함수를 적용하여
IntStream으로 변환시킴
• flatMap()과 flatMapTolnt( ) 등은 스트림의 각 요소에 매핑 함 수(람다식)를 적용하여 스트림으로 변환한 후, 여러 스트림을 다시 하나의 스트림으로 합침
• IntStream 에서 asDoubleStream() 과 asLongStream() 은 기본형을 다른 기본형으로 변환시키는 것으로
DoubleStream 또는 LongStream 객체를 리턴,
boxed()는 기본형 int 값을 포장
형 Integer 객체로 변환시키고 Stream<Integer> 객체를 리턴
매핑 예(1)
import java.util.*;
public class Main {
public static void main(String[] args) {
//String 배열을 IntStream으로 변환
String[] strNums = {"10", "20", "30"};
Arrays.stream(strNums)
.mapToInt(item-> Integer.parseInt(item))
.forEach(Item System.out.println(item));
//int 배열을 Stream<String>으로 변환
int[] numbers = {10, 20, 30};
Arrays.stream(numbers)
.mapToObj(item -> String.valueOf(item))
.forEach(item -> System.out.println(item + "(" + item.length() +")"));
/* 결과
10
20
30
10(2)
20(2)
30(2)
*/
매핑 예(2)
import java.util.*;
public class Main {
public static void main(String[] args) {
Arrays.asList("Hello world, this is java", "Welcome to java world")
.stream()
.flatMap(sentence -> Arrays.stream(sentence.split(" ")))
.forEach(word -> System.out.println(word));
}
}
/* 결과
Hello
world,
this
is
java Welcome to
java
world
*/
flatMap은 스트림의 각요소에 매핑함수 적용해서 스트림으로 변환후 여러스트림을 하나로 만든다
정렬
• 스트림의 원소들을 오름차순 또는 내림차순으로 정렬하여 새로운 스트림을 생성하는 작업
Stream<T>에서 sorted() 메소드 사용
- 정렬된 새로운 스트림을 리턴
- T 유형의 객체(원소)는 Comparable이어야 하며, 크기 비교에 compareTo() 메소드가 사용됨
- 기본형에 대응되는 포장 클래스는 모두 Comparable임
정렬 예
• compareTo()가 구현된 Book 클래스와 정렬된 스트림 생성 예
class Book implements Comparable<Book> {
String title;
String author;
int price;
Book (String title, String author, int price) {
this.title = title;
this.author = author;
this.price = price;
public String toString() {
return ("title:" + this.title". author:" + this.author", price: " + this.price);
}
public int compareTo(Book book) {
return Integer.compare(this.price, book.price);
}
import java.util.*;
public class Main {
public static void main(String[] args) {
List<Book> books = new ArrayList<Book>();
books.add(new Book ("JAVA", "홍길동", 10000));
books.add(new Book ("PHP", "이순신", 20000));
books.add(new Book("마이크로프로세서 입문", "김유신", 14000));
books.add(new Book("데이터베이스 입문", "신사임당" , 21000));
books.add(new Book("빅데이터 연구", "유관순", 21000));
books.add(new Book("커뮤니케이션 이론", "강감찬",
books.add(new Book ("PHP", "이순신", 20000));
books.add(new Book("알고리즘", "권율", 17000));
books.stream().sorted().forEach(book-> System.out.println(book));
}
}
크기비교에 compareTo() 메소드가 사용됨
루핑
• 스트림의 원소들을 하나씩 순회하면서 반복적으로 처리하고 새로운 스트림을 생성하는 작업
peek() 메소드는 각 요소를 순회하면서 주어진 동작(람다식)을 수행하는 중간 연산으로 디버깅이나 로깅에 자주 사용됨
새로운 스트림을 반환하며, 최종 연산과 함께 사용되어야 함
- 참고로 forEach() 메소드는 각 요소를 반복하면서 주어진 동작(람다식) 을 수행하는 종료 연산
- anyMatch() 메소드는 종료 연산으로 주어진 조건(람다식)을 만족하는 지원소를 순회하면서 조사함. 주어진 조건을 만족하는 요소를 찾으면 즉시 검색을 중단
- 스트림 파이프라인에서 peek()와 anyMatch()를 조합하면 처리되는 원소를, 조건식에서 처음 true가 되는 원소까지로 제한하게 됨
peek()는 중간연산으로 새로운스트림을 리턴하지만 forEach는 종료연산이고 아무것도 리턴하지않음
루핑 예
• peek() 메소드 사용 예
public class Main {
public static void main(String[] args) { List <Book> books = new ArrayList <Book>
books.add(new Book("JAVA", "홍길동", 10000));
books.add(new Book("PHP", "이순신", 20000));
books.add(new Book("마이크로프로세서", "김유신", 14000));
books.add(new Book("데이터베이스", "신사임당", 21000));
books.add(new Book("빅데이터연구", "유관순", 12000));
books.add(new Book("커뮤니케이션이론", "강감찬", 15000));
books.add(new Book("PHP", "이순신", 20000));
books.add(new Book("알고리즘", "권율", 17000));
books.stream()
.peek(book -> System.out.println(book))
.anyMatch(m -> m.price > 20000);
/* 결과
title: JAVA, author: 홍길동, price: 10000
title: PHP, author: 이순신, price: 20000
title: 마이크로프로세서, author: 김유신, price: 14000
title: 데이터베이스, author: 신사임당, price: 21000
*/
종료연산
• 중간처리를 거친 스트림에 대해 집계나 결과 출력 등의 최종처리를 수행하는 최종연산
집계(합계, 평균, 최대, 최소 등), 매칭, 수집 등의 작업을 수행함
• 스트림 파이프라인의 마지막 단계에서 사용되며, 스트림을 다시 사용할 수 없게 함
종료연산이 수행되기 전까지는 스트림 파이프라인이 실제로 실행되지 않음(lazy evaluation)
지연 평가를 통해 스트림 파이프라인을 효율적 구성하며
필요한 결과를 얻음
집계
• 원소들의 개수, 합계, 평균, 최대값, 최소값 등을 구하는 최종처리
• count(), sum(), average(), max(), min() =
IntStream average(), max(), min() 는 Optionallnt 또는 Optional Double 값을 리턴하므로
getAsInt( ) 또는 getAsDouble( )로 값을 변환해야 함
int[] intArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
long count = Arrays.stream(intArray).filter(n -> n%2 == 0).count();
System.out.println("2의 배수의 개수: " + count);
long sum = Arrays.stream(intArray).filter(n -> n%2 == 0).count();
System.out.println("2의 배수의 합: " + sum);
int min = Arrays.stream(intArray).min().getAsInt();
System.out.println("최소값 : " + min);
double avg = Arrays.stream(intArray). average().getAsDouble();
System.out.println("평균 : " + avg);
/* 결과
2의 배수의 개수 : 5
2의 배수의 합: 30
최소값 : 1
평균 : 5.5
*/
매칭
• 스트림의 원소들이 특정 조건을 만족하는지를 확인하는 최종처리
•anyMatch()는 최소 하나의 요소가 조건을 만족할 때, allMatch( )는
모든 원소가 조건을 만족할 때, noneMatch( )는 모든 요소가 조건을 만족하지 못할 때 true를 반환
int[] intArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
boolean result = false;
result = Arrays.stream(intArray).anyMatch(n -> n%2 == 0);
System.out.println("적어도 하나는 2의배수인가? " + result);
result = Arrays.stream(intArray).allMatch(n -> n%2 == 0);
System.out.println("모두 2의배수인가? " + result);
result = Arrays.stream(intArray).filter(n -> n%2 != 0)
.noneMatch(n -> n%2 == 0);
System.out.println("모두 2의 배수가 아닌가? + result);
수집
• 스트림의 원소들을 필터링 또는 매핑한 후 최종적으로 새로운 컬렉션 (List, Set, Map)을 생성하는 종료 연
• Collectors 클래스는 스트림의 요소들을 수집하는 데 사용되는 다양한 static 유틸리티 메소드를 제공
이것의 결과가 collect()의 인자로 사용됨
List<Member> male_members = members.stream().filter(member ->
member.getGender().equals("남")).collect(Collectors.toList());
male_members.stream().forEach(member -> System.out.println(member));
Set<Member> female_members2 = members.stream().filter(member ->
member.getGender().equals("0")).collect (Collectors.toSet());
female_members2.stream().forEach(member -> System.out.println(member));
Map<String, Integer> ages =
female_members2.stream().collect(Collectors.toMap(member ->
member.getName(), member -> member.getAge()));
System.out.println(ages);
collect(Collectors.toList())는 male_members 라는 List를 생성한다
'STUDY > JAVA' 카테고리의 다른 글
[Java] 기본형 매개변수, 참조형 매개변수, 참조형 반환값 (1) | 2024.06.10 |
---|---|
[JAVA] 자바 타이머 Timer, TimerTask (0) | 2024.06.10 |
[JAVA] 비어있는 문자열(String) 체크하는 방법 (0) | 2024.04.24 |
[JAVA] e.printStackTrace to String (0) | 2024.02.21 |
[JAVA] 소켓 개념과 자바 예제 코드 (0) | 2023.12.12 |