아래의 내용은 java_grammer 레파지토리 C05AnonymousLambda/C04StreamApi.java 디렉터리에 저장되어있는 내용을 정리함
- C04StreamApi
전통 for문 → Stream API → Optional 안전처리 워크플로우
설명: 데이터와 객체 중심이 아닌, 입력에 따른 출력만 존재하는 코딩 스타일
Arrays.stream(arr) forEach, map, sum 등 Stream API 특징
forEach : 스트림 요소를 하나씩 소모하며 동작 수행 Arrays.stream(arr) : 원본 → 사본 생성 (원본 보존) (a) -> a * 2핵심 원칙
전통적인 for문 접근
int[] arr = {20, 10, 4, 12};
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]); // 원본 수정 가능
}
함수형 접근
Arrays.stream(arr) // 입력: 원본 → 사본
.forEach(a -> System.out.println(a)); // 출력: 순수 처리
// 원본 arr 그대로 보존됨
▶ 생성 → 중개연산 → 소모연산
List<String> myList = Arrays.asList("java", "python", "c++");
Stream<String> stream1 = myList.stream();
String[] strArr = {"java", "python", "c++"};
Stream<String> stream2 = Arrays.stream(strArr);
int[] intArr = {10, 10, 20, 30, 40, 50};
IntStream stream3 = Arrays.stream(intArr); // 원시 타입 특화
filter – 조건 필터링
IntStream filterStream = Arrays.stream(intArr)
.filter(a -> a >= 30);
map – 값 조작
IntStream mapStream = Arrays.stream(intArr)
.map(a -> a * a);
distinct – 중복 제거
IntStream distinctStream = Arrays.stream(intArr).distinct();
sorted – 정렬
IntStream sortedStream = Arrays.stream(intArr).sorted();
mapToInt – 문자열 길이 변환
IntStream mapToIntStream = Arrays.stream(strArr)
.mapToInt(a -> a.length()); // [5,6,3]
System.out.println("30이상 필터링 → sum : " + filterStream.sum());
System.out.println("제곱 조작 → sum : " + mapStream.sum());
System.out.println("중복 제거 → sum : " + distinctStream.sum());
System.out.println("정렬 후 배열화 → " + Arrays.toString(sortedStream.toArray()));
Arrays.stream(intArr).forEach(a -> System.out.println("forEach : " + a));
System.out.println("count : " + Arrays.stream(intArr).count());
System.out.println("max/min : " + Arrays.stream(intArr).max().getAsInt() + ", " +
Arrays.stream(intArr).min().getAsInt());
System.out.println("누적합 : " + Arrays.stream(intArr).reduce(0, (a,b)->a+b));
System.out.println("누적곱 : " + Arrays.stream(intArr).reduce(1, (a,b)->a*b));
System.out.println("문자열 누적합 : " + Arrays.stream(strArr).reduce("", (a,b)->a+b));
System.out.println("첫번째(조건) : " +
Arrays.stream(strArr).filter(a->a.length()>=5).findFirst().get());
Map<String, Integer> myMap = new TreeMap<>();
myMap.put("hello", 10);
myMap.put("java", 20);
System.out.println("TreeMap 첫 key : " +
myMap.keySet().stream().findFirst().get());
| 메서드 | 설명 | 예제 | 결과 (intArr = {10,10,20,30,40,50}) |
|---|---|---|---|
filter | 조건 필터링 | .filter(a -> a >= 30) | [30,40,50] |
map | 값 조작 | .map(a -> a * a) | [100,100,400,900,1600,2500] |
distinct | 중복 제거 | .distinct() | [10,20,30,40,50] |
sorted | 정렬 | .sorted() | [10,10,20,30,40,50] |
mapToInt | 문자열 → IntStream | .mapToInt(a -> a.length()) | [5,6,3] |
| 메서드 | 설명 | 예제 | 결과 |
|---|---|---|---|
sum | 요소 총합 | .sum() | 120 |
toArray | 배열 변환 | .toArray() | [10,10,20,30,40,50] |
forEach | 요소 출력 | .forEach(...) | 개별 출력 |
count | 요소 개수 | .count() | 6 |
max | 최대값 | .max().getAsInt() | 50 |
min | 최소값 | .min().getAsInt() | 10 |
reduce | 누적합 | .reduce(0,(a,b)->a+b) | 160 |
reduce | 누적곱 | .reduce(1,(a,b)->a*b) | 3000000 |
reduce | 문자열 결합 | .reduce("",(a,b)->a+b) | "javapythonc++" |
findFirst | 첫 요소 | .findFirst().get() | "java" |
문제:
Stream<String>.toArray() 는 Object[] 반환 → 타입 에러 발생
String[] stArr = {"hello", "java", "world"};
// Arrays.stream(stArr).filter(a->a.length()>=5).toArray(); // 에러!
IntStream은 OK
int[] result = Arrays.stream(intArr).filter(a->a>=30).toArray();
해결법
// 방법1: 생성자 참조 (추천)
String[] answer = Arrays.stream(stArr)
.filter(a -> a.length() >= 5)
.toArray(String[]::new);
// 방법2: 람다식
String[] answer2 = Arrays.stream(stArr)
.filter(a->a.length()>=5)
.toArray(a->new String[a]);
출력:
[hello, world]
Arrays.stream(stArr).forEach(a -> System.out.println(a)); // 람다
Arrays.stream(stArr).forEach(System.out::println); // 메소드 참조
홀수 요소만 추출
int[] oddArr = Arrays.stream(arrEle)
.filter(a->a%2!=0)
.toArray();
출력: [1,3,5]
홀수 제곱 배열
int[] oddSquareArr = Arrays.stream(arrEle)
.filter(a->a%2!=0)
.map(a->a*a)
.toArray();
출력: [1,9,25]
홀수 제곱 정렬
int[] oddSortedArr = Arrays.stream(arrEle)
.filter(a->a%2!=0)
.map(a->a*a)
.sorted()
.toArray();
출력: [1,9,25]
리스트 생성
List<Student> studentsList = new ArrayList<>();
studentsList.add(new Student("kim", 20));
studentsList.add(new Student("choi", 30));
studentsList.add(new Student("lee", 35));
studentsList.add(new Student("park", 22));
평균나이
double avgAge = studentsList.stream()
.mapToInt(Student::getAge)
.average()
.getAsDouble();
출력: 26.75
최연소 학생
Student youngest = studentsList.stream()
.sorted((o1,o2)->o1.getAge()-o2.getAge())
.findFirst().get();
출력: kim(20)
30대 이름 배열
String[] age30List = studentsList.stream()
.filter(a->a.getAge()>=30)
.map(Student::getName)
.toArray(String[]::new);
출력: [choi, lee]
문제 상황:
String st1 = br.readLine();
if (st1 == null) st1.length(); // NPE 위험
1. Optional.empty() — 완전한 빈 Optional 생성
Optional<String> opt1 = Optional.empty();
Optional 객체 생성 2. Optional.ofNullable(value) — null 가능성 있는 값 처리
Optional<String> opt2_1 = Optional.ofNullable(null);
Optional<String> opt2_2 = Optional.ofNullable("hello");
null일 수도, 아닐 수도 있는 값을 안전하게 감싸는 방법 null이면 자동적으로 빈 Optional 생성 (Optional.empty()와 같음) null이 아니면 내부에 값("hello")을 저장 3. Optional.of(value) — null 불허
Optional<String> opt3 = Optional.of("hello");
null을 넣으면 NullPointerException 발생 1. isPresent
if (opt1.isPresent()) System.out.println(opt1.get());
else System.out.println("값이 없습니다.");
2. orElse (기본값 제공)
System.out.println(opt2_1.orElse("값이 없습니다."));
3. orElseGet (지연 실행)
System.out.println(opt2_1.orElseGet(() -> "값이 없습니다."));
4. orElseThrow (예외 발생)
System.out.println(opt2_2.orElseThrow(
() -> new RuntimeException("값이 없습니다.")));
평균 나이 안전처리
OptionalDouble optionalAvg =
studentsList.stream().mapToInt(Student::getAge).average();
if (optionalAvg.isPresent())
System.out.println(optionalAvg.getAsDouble());
else
System.out.println("값이 없습니다.");
orElseThrow 방식 (실무 표준)
double value = studentsList.stream()
.mapToInt(Student::getAge)
.average()
.orElseThrow(() -> new NoSuchElementException("값이 없습니다."));
동적 인덱스 조회
Scanner sc = new Scanner(System.in);
int num = Integer.parseInt(sc.nextLine());
Optional<Student> s1 = (num >= 0 && num < studentsList.size())
? Optional.of(studentsList.get(num))
: Optional.empty();
System.out.println(s1.orElseThrow(
() -> new NoSuchElementException("없는 회원입니다.")));
| 단계 | 메서드 | 역할 |
|---|---|---|
| 생성 | stream(), Arrays.stream() | 원본 → 사본 스트림 생성 |
| 중개 | filter, map, distinct, sorted | 연쇄 변환 (지연평가 방식) |
| 소모 | forEach, sum, reduce, findFirst, toArray | 실제 결과 도출 (한 번만 실행) |
핵심 원칙:
원본 보존 + 순수 함수형 처리 + Comparator/람다 연계