Nested Class (내포 클래스)
- 클래스 안에 존재하는 클새스
- 자바는 클래스 안에 클래스를 생성하는 문법을 지원
Inner Class
- 클래스 안에 존재하는 클래스
- 클래스 내부에서만 사용할 목적으로 생성
- 일반 클래스는 접근 지정자가 package(접근 지정자가 없는 경우) 와 public 만 가능하지만 inner class 에서는 private 과 protected 도 가능
- 클래스가 컴파일 되었을 때는 외부클래스이름$내부클래스이름.class로 만들어짐
Static Inner Class
- class 앞에 static 을 붙이는 클래스
- 내포 클래스에 static 멤버가 있
- 내포 클래스에 static 멤버가 있는 경우는 인스턴스 생성없이 사용할 수 있도록 static을 붙여주어야 함
- static 이 붙은 클래스는 인스턴스 생성없이 사용 가능
Local Inner Class
- 메서드 안에 만들어지는 내포 클래스
- 메서드 안에서만 사용 가능
- 보통 메서드 안에서 사용하다가 다른 메서드에서도 똑같이 사용해야 한다면 메서드 밖으로 꺼내놓고 씀
똑같은 코드를 사용하지 않고, 스파게티 코드 사용을 줄이기 위해서 ▶ 유지, 보수 목적
Anonymous Class
- 이름없는 클래스 또는 객체 = 익명 객체 또는 익명 클래스
- 인터페이스를 구현하거나 클래스를 상속받아야 할 때 별도의 클래스를 만들지 않고, 필요한 메서드만 재정의해서 사용하는 문법
- 이벤트 처리할 때 많이 사용
- 이 경우에 인터페이스 안에 메서드가 1개인 경우는 람다 표현식으로 작성하는 것이 가능
( 백엔드 개발자가 많이 씀 )
→ 문법적으로 수업 도중 많이 사용
- 작성하는 방법을 필수로 알아야 함
- 인터페이스를 구현하거나 클래스를 상속받아서 사용을 하고자 하는 경우에 별도의 클래스를 생성하지 않고 사용하는 방법
상속이나 구현을 하는 클래스를 만들지 않고 활용
new 인터페이스이름 or 상위클래스이름 (매개변수 나열) {
필요한 메서드 정의
};
실습
nestedclass
package _naver.leejw0199.nestedclass;
public class OuterClass {
//내포 클래스 - 다른 클래스 안에 만들어진 클래스
class Inner{
public int num;
}
//내포 클래스 안에 static 멤버가 있으면 인스턴스 생성없이 사용할 수 있도록 static을 추가
static class StaticInner{
public int num;
public static int share;
}
public void method() {
//메서드 안에 만들어진 클래스 - Local Inner
//메서드 안에서만 사용이 가능한 클래스
class LocalInner{
public int num;
}
}
}
- Inner, StaticInner, method 안 LocalInner 사용 방법
AnonymousMain - 인터페이스를 구현한 클래스의 인스턴스 생성
package _api.util.stream.nestedclass;
import java.util.Arrays;
import java.util.Comparator;
//메서드가 1개인 인터페이스
interface Sample{
//추상 메서드 선언
public void display();
}
//인터페이스를 구현한 클래스
class SampleImpl implements Sample{
@Override
public void display() {
System.out.println("클래스를 만들어서 사용");
}
}
public class AnonymousMain {
public static void main(String[] args) {
//인터페이스를 구현한 클래스의 인스턴스를 생성해서 메서드 호출
//인스턴스를 여러 개 만들어야 한다면 클래스를 만드는 것이 효율적
Sample sample = new SampleImpl();
sample.display();
}
}
-------------------------------출력----------------------------------
클래스를 만들어서 사용
- 인터페이스를 1개 만들고 그 안에 추상메서드 선언
- Sample 인터페이스를 implements 받는 SampleImpl 클래스 생성 후 추상메서드 오버라이드
- main에서 인터페이스(Sample)를 구현한 클래스(SampleImpl)의 인스턴스를 생성 후 display() 메서드 호출
- 인스턴스를 여러 개 만들어야 한다면 클래스를 만드는 것이 효율적
AnonymousMain2 - 인터페이스의 클래스를 생성하지 않고 사용
package _naver.leejw0199.nestedclass;
interface Sample2{
public void display();
}
public class AnonymousMain2 {
public static void main(String[] args) {
new Sample2() {
@Override
public void display() {
System.out.println("클래스를 생성하지 않고 사용");
}
}.display();
}
}
-------------------------------출력------------------------------
클래스를 생성하지 않고 사용
- 추상메서드가 있는 인터페이스 생성 후 main에서 인스턴스를 생성하지 않음
- new 인터페이스 이름 (매개변수) {
추상메서드 오버라이드
}.추상메서드 호출;
AnonymousMain3 - 배열의 정렬
package _api.util.stream.nestedclass;
import java.util.Arrays;
import java.util.Comparator;
public class AnonymousMain3 {
public static void main(String[] args) {
//배열의 정렬
String[] arr = {"SM", "JYP", "스타쉽", "YG", "나무엑터스"};
//배열의 내림차순 정렬
//Arrays.sort(배열, 비교를 위한 Comparator<T> 인터페이스를 구현한 클래스의 객체);
//를 호출해야 함
Arrays.sort(arr, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
//이 내부에는 공부를 해야 쓸 수 있음
//지금은 여기까지 틀을 만들어내는 게 중요
return o2.compareTo(o1); //내림차순
}
});
//배열의 요소를 빠르게 확인
System.out.println(Arrays.toString(arr));
//하나씩 확인
for(String app : arr) {
System.out.println(app);
}
}
}
----------------------------------------출력------------------------------------------
[스타쉽, 나무엑터스, YG, SM, JYP]
스타쉽
나무엑터스
YG
SM
JYP
- Anonymous Class 작성 부분
Arrays.sort(arr, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
//이 내부에는 공부를 해야 쓸 수 있음
return o2.compareTo(o1);
}
}); - Comparator 인터페이스 부분은 링크 참조
2022.10.29 - [프로그래밍 언어/Java] - [Java] Comparable VS Comparator
Lambda (람다)
- jdk1.7 부터 함수형 프로그래밍을 위해서 추가
- 메서드를 하나의 식으로 표현한 것으로 메서드의 이름과 반환값이 없어지므로 익명함수라고도 함
- 이름없는 함수 = anonymous function 를 만들기 위해서 사용
- 익명 객체를 만들어서 사용하던 것을 조금 더 간결하게 함수를 대입하는 것처럼 보이도록 하기 위해서 사용
메서드를 변수처럼 사용 가능 - 추상메서드가 1개만 존재하는 (함수형)인터페이스에서만 사용 가능
그래야 람다식과 인터페이스의 메서드가 1:1로 연결될 수 있음 - @FunctionalInterface 붙이면 컴파일러가 한 번 검증해줌
- 람다식을 다루기 위한 참조변수의 타입은 함수형 인터페이스
( 매개변수 나열 ) -> { 내용을 작성; }
- 매개변수를 작성할 때는 자료형과 매개변수 이름을 작성하는데 매개변수 자료형은 생략이 가능
자료형을 생략하면 호출할 때 자료형을 결정함
- 매개변수가 1개인 경우는 ()를 생략하는 것이 가능
매개변수가 없거나 2개 이상인 경우는 생략하면 안됨
- 메서드에 return type이 있다면 return 만들면 됨
중괄호 안에 return하는 문장이 있다면 { } 생략 가능
return도 생략이 가능
- { } 안의 문장이 하나면 생략 가능 ▶ 문장의 맨 끝에 ;을 붙이면 안 됨
LambdaBasicTest
package chapter14;
@FunctionalInterface
interface MyFunction {
void run();
}
public class LamdaBasicTest{
//execute 메서드의 매개변수로 람다식을 받겠다
static void execute(MyFunction f) {
f.run();
}
static MyFunction getMyFunction() {
MyFunction f = () -> System.out.println("f3.run()");
return f;
}
public static void main(String[] args) {
//람다식을 다루기 위한 참조변수의 타입은 함수형 인터페이스로 함
MyFunction f1 = () -> System.out.println("f1.run()");
MyFunction f2 = new MyFunction() {
@Override
public void run() { //오버라이딩 - 접근제어자는 좁게 못 바꿈
System.out.println("f2.run()");
}
};
MyFunction f3 = getMyFunction();
f1.run();
f2.run();
f3.run();
execute(f1);
execute( () -> System.out.println("run()") );
}
}
-----------------------------------------------------------------------
f1.run()
f2.run()
f3.run()
f1.run()
run()
자바가 제공하는 람다 인터페이스
1. Consumer (O,X)
매개변수는 있고, 리턴 값을 없는 메서드를 소유한 인터페이스
소비자
일만 함
- accept(T t)
2. Supplies (X,O)
매개변수는 없고, 리턴값만 있는 메서드를 소유한 인터페이스
공급
중요도 ↓
- get()
3. Function = Map
매개변수와 리턴이 모두 있는 메서드를 소유한 인터페이스
변환 작업
- apply(T t)
4. Operator
매개변수가 있고, 리턴도 있는 메서드를 소유한 인터페이스인데 이 인터페이스는 일반적으로 메서드 내부에서 연산 작업 수행
계산 연산을 하려면 2개 이상을 주고 리턴하도록 해야함
5. Predicate
매개변수가 있고, 리턴 값이 boolean인 메서드를 소유한 인터페이스
필터링 골라줘야 하니까 판정 필요
조건
- test(T t)
- and(), or(), negate() = !(not)
- isEquals()
실습
LambdaMain
package _api.util.stream.nestedclass;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
public class LambdaMain {
public static void main(String[] args) {
//배열의 정렬
String[] arr = {"SM", "JYP", "스타쉽", "YG", "나무엑터스"};
//배열의 내림차순 정렬
//Arrays.sort(배열, 비교를 위한 Comparator<T> 인터페이스를 구현한 클래스의 객체);
//를 호출해야 함
//Comparator 는 메서드가 1개만 존재함
/*
* Arrays.sort(arr, new Comparator<String>() {
* @Override
* public int compare(String o1, String o2) {
* //이 내부에는 공부를 해야 쓸 수 있음
* //지금은 여기까지 틀을 만들어내는 게 중료
* return o2.compareTo(o1); //내림차순
* }
*});
*/
//1. 이 부분과
Arrays.sort(arr, (o1, o2) -> { return o2.compareTo(o1); });
//Comparator 인터페이스는 메서드가 1개 밖에 없으므로 람다로 표현하는 것이 가능
//람다를 만들 때는 인터페이스 이름과 메서드 이름은 중요하지 않음
//매개변수의 개수 와 리턴 타입만 확인하면 됨
//매개변수는 2개 이고, 리턴 타입은 정수
//매개변수가 1개 이면 () 생략 가능
//return 하는 문장만 존재한다면 {} 와 return을 생략하는 것이 가능
//메서드의 매개변수로 코드(함수)를 대입한 것처럼 보이도록 함
//메서드의 매개변수로 코드(함수)를 대입할 수 있는 방식을 함수형 프로그래밍이라고 함
//2. 이 부분 같은 결과 리턴
Arrays.sort(arr, (o1, o2) -> o2.compareTo(o1));
//배열의 요소를 빠르게 확인
//내림차순
System.out.println(Arrays.toString(arr));
}
}
-----------------------------------------출력---------------------------------------------
[스타쉽, 나무엑터스, YG, SM, JYP]
- Arrays.sort에서 람다식으로 표현
- 매개변수는 2개 이므로 ( o1, o2 )에서 ()생략할 수 없음
- compareTo 메서드를 이용하면 리턴값이 나오기 때문에 return, {} 생략가능 - 1,2가 다른 점
- 메서드의 매개변수로 코드(함수)를 대입한 것처럼 보이도록 함
FunctionPackageTest
package chapter14;
import java.util.*;
import java.util.function.*;
public class FunctionPakageTest {
public static void main(String[] args) {
Supplier<Integer> s = () -> (int)(Math.random()*100)+1;
Consumer<Integer> c = i -> System.out.print(i + ", ");
Predicate<Integer> p = i -> i%2 == 0;
Function<Integer, Integer> f = i -> i/10*10; //i의 1의 자리 없앰
List<Integer> list = new ArrayList<>();
makeRandomList(s, list);
System.out.println(list);
printEvenNum(p, c, list);
List<Integer> newList = doSomething(f, list);
System.out.println(newList);
} //end main
//변환 작업
static <T> List<T> doSomething(Function<T, T> f, List<T> list) {
List<T> newList = new ArrayList<T>(list.size());
for(T i : list) {
newList.add(f.apply(i)); //일의 자리를 없애서 새로운 list에 저장
}
return newList;
} //end doSomething
//필터링, 소비자
static <T> void printEvenNum(Predicate<T> p, Consumer<T> c, List<T> list) {
System.out.print("[");
for(T i : list) {
if(p.test(i)) //짝수 검사
c.accept(i); //i -> System.out.print(i + ", "); 화면에 i출력
}
System.out.println("]");
} //end printEvenNum
//공급자
static <T> void makeRandomList(Supplier<T> s, List<T> list) {
for(int i = 0; i < 10; i++) {
list.add(s.get());
}
} //end makeRandomList
}
---------------------------------------------------------------------------------
[99, 84, 69, 98, 86, 78, 86, 9, 71, 29]
[84, 98, 86, 78, 86, ]
[90, 80, 60, 90, 80, 70, 80, 0, 70, 20]
PredicateTest
package chapter14;
import java.util.function.*;
public class PredicateTest {
public static void main(String[] args) {
//두개의 함수 하나로 연결하기
Function<String, Integer> f = (s) -> Integer.parseInt(s, 16); //<입력, 출력>
Function<Integer, String> g = (i) -> Integer.toBinaryString(i);
Function<String, String> h = f.andThen(g); //f 적용 후 g 적용해라
Function<Integer, Integer> h2 = f.compose(g); //g 적용 후 f 적용해라
System.out.println(h.apply("FF")); //"FF" -> 255 -> "11111111"
System.out.println(h2.apply(2)); //2 -> "10" -> 16
Predicate<Integer> p = i -> i < 100;
Predicate<Integer> q = i -> i < 200;
Predicate<Integer> r = i -> i%2 == 0;
Predicate<Integer> notP = p.negate(); // i >= 100
Predicate<Integer> all = notP.and(q.or(r));
System.out.println(all.test(150));
String s1 = "abc";
String s2 = "abc";
Predicate<String> p2 = Predicate.isEqual(s1);
boolean result = p2.test(s2);
System.out.println(result);
}
}
----------------------------------------------------------------------------------
11111111
16
true
true
2023.05.07 - [프로그래밍 언어/Java] - 진법 변환 / StringBuilder & StringBuffer / Arrays.stream
2023.07.05 - [프로그래밍 언어/Java] - [Java] java.io 패키지 - 로그 파일에서 ip 접속 횟수 출력
Stream API
- Collection 데이터를 다룰 때 내부 반복자를 만들어서 접근하기 위한 API
- 예시로 sort를 할 때 List는 Collections사용, 배열은 Arrays 사용
- 데이터 소스를 추상화하고, 데이터를 다루는데 자주 사용되는 메서드들을 정의
- 데이터 소스가 무엇이던 간에 같은 방식으로 다룰 수 있게 하기 위해서 사용
- 파일에 저장된 데이터도 모두 같은 방식으로 다룰 수 있음
배열 | ArrayList |
저장할 데이터의 개수 정적 | 저장할 데이터의 개수 동적 |
연속 | 연속 X |
삽입/삭제 비효율적 (값을 뒤로 이동) |
삽입/삭제 효율적 (전후 노드의 참조 관계만 수정) |
특정 위치의 데이터를 조회하는 작업 多 | 특정 위치를 조회하는 작업 少 (처음부터 순차 검색) |
primitive type, object 저장 가능 | object 만 저장 가능 |
Generic 사용 X | Generic 사용 O |
length 변수 | size() 메서드 사용 |
= | add() 메서드 사용 |
Array(배열) : 크기가 고정인 List { 50, 40, 30, 20, 10 }
▼
Vector : 크기가 가변인 배열
ArrayList : 삽입과 삭제가 가벼움
▼
Linked List : 다음 데이터를 가리키는 용도, 조회가 느림
- 내부 반복자를 이용해서 접근하면 반복문이나 빠른 열거를 사용하는 것보다 빠르게 작업을 수행할 수 있음
- How 보다 What (어떻게 보다 무엇을?)
Stream 특징
- 데이터 소스로부터 읽기만 할 뿐 변경하지 않음
- Iterator처럼 일회용
- 작업을 내부 반복으로 처리
- 최종 연산 전까지는 중간 연산이 수행되지 않음 - 지연된 연산
- 데이터가 많을 때 Stream<Integer>보다 IntStream가 빠름 - 오토박싱, 언박싱 관련
reduce에는 계산을 하기 위한 Integer → int, int → Integer 과정이 들어가므로 IntStream에서 제공하는 sum을 사용하는게 빠름 - 병렬 처리 쉬움
Stream API 작업과정
생성 → 중간 작업 → 최종 작업(연산)
(중간 작업은 여러 개를 묶어도 됨)
- 생성은 배열이나 List를 가지고 수행
- 중간 작업은 스트림의 데이터를 순회하면서 작업을 수행해서 다시 스트림을 리턴하기 때문에 다른 중간 작업을 연속해서 배치하는 것이 가능
- 최종 작업은 1번만 수행 가능
집계를 수행하는 함수나 forEach 처럼 하나씩 순회하면서 작업을 수행하기 위한 함수
또는 collect 처럼 배열이나 List를 생성해주는 함수를 사용
Stream 생성
1. Collection 인터페이스로부터 상속받은 객체는 stream()이나 parallelStream() 을 호출
→ parallelStream()을 호출하면 병렬 처리가 가능한 스트림
2. 배열의 경우는 Arrays.Stream(배열)을 이용해서 생성
→ Stream.of()는 객체 배열이나 여러 가지 유형의 객체를 가지고 스트림 생성
3. 일련번호 형태의 정수 스트림은 IntStream.range(start, end) 나 rangeClosed(start, end)를 이용해서 생성하는 것이 가능
4. Random클래스의 ints()/ longs() / doubles()
→ 난수 생성, 무한 스트림이므로 limit으로 유한 스트림 들거나 크기 지정으로 유한 스트림 생성 가능
5. iterate(), generate()는 람다식을 매개변수로 받아 계산되는 값들을 요소로 하는 무한 스트림 생성
→ 기본형 스트림 타입의 참조변수로 다룰 수 업고, 필요하면 mapToInt() 같은 메서드로 변환해야 함
6. csv 파일 List<String[]>로 한번에 가져오기
7. 파일과 빈 스트림 생성은 java.io 패키지 확인
//Collections Stream 생성 List<String> list = Arrays.asList("red", "yellow", "green", "blue"); Stream<String> stream = list.stream(); //배열 Stream 생성 String[] array = {"red", "yellow", "green", "blue"}; Stream<String> stream = Arrays.stream(array); //기본 타입, 객체 타입 Stream<String> stream = Stream.of(array); //객체 타입 //일련번호 형태(연속된) 정수 스트림 IntStream intStream = IntStream.range(1, 5); //1, 2, 3, 4 IntStream intStream = IntStream.rangeClose(1, 5); //1, 2, 3, 4, 5 //임의의 수1 IntStream intStream = new Random().ints(); //무한스트림 intStream.limit(5).forEach(System.out::println); //유한 스트림, 5개 요소 출력 //임의의 수2 IntStream intStream = new Random().ints(1, 10); //1~9까지의 숫자 중 intStream.limit(5).forEach(System.out::println); //5개 요소 출력 //임의의 수3 IntStream intStream = new Random().ints(10, 1, 9); //1~8까지의 숫자 중 intStream.forEach(System.out::println); //랜덤으로 10번 출력 //iterate(T seed, UnaryOperator f) - 단항 연산자, 계산 반복 Stream<Integer> intStream = Stream.iterate(0, n -> n + 2); intStream.limit(10).forEach(System.out::println); //generate(Supplier s) - 주기만 하는 것. 입력x, 출력o, 이전 결과를 이용해서 다음 요소를 계산하지 않음 Stream<Integer> oneStream = Stream.generate(() -> 1); oneStream.limit(5).forEach(System.out::println); //'1'만 5번 출력
중간 작업 - 작업을 수행한 후 Stream을 리턴
Java에서는 Stream, IntStream, LongStream, DoubleStream 객체를 제공함
distinct() 중복 제거 filter(매개변수를 1개 받아서 Boolean을 리턴하는 람다) 람다가 true를 리턴하는 데이터만 모아서 스트림을 생성 map(매개변수를 1개 받아서 리턴하는 람다) 리턴하는 데이터를 모아서 새로운 스트림을 리턴 mapToInt 스트림을 IntStream으로 변환 sorted(비교처리를 수행해주는 람다) 람다를 기준으로 정렬 skip(long n) n만큼 건너뜀 limit(long n) n개 만큼 추출 flatMap 중첩된 리스트나 배열을 평면화
IntStream은 문자를 생성하고 사용 가능
(ex) chars(): 문자열을 구성하고 있는 문자들의 ASCII 코드값을 스트림 형태로 뽑아줌)
최종 작업 (연산)
count(): 데이터 개수 리턴
max(): 최대값 리턴
min(): 최소값 리턴
average(): 평균 리턴
findFirst(): 첫번째 요소 반환
forEach(): 데이터를 순회하면서 작업
toArray(): 스트림의 요소를 담은 배열 리턴
collect(): 데이터를 모아서 다른 자료형으로 변경
sum(): 스트림의 총 합을 구함
reduce(초기값, 연산): 초기값부터 연산 수행 후 누적 결과 리턴
# 계산에 의해서 나오는 max, min, average는 리턴 타입이 Optional 임
# 스트림의 분할 - partitioningBy
# 스트림의 그룹 - groupingBy
//Stream → Collections, 배열 List<String> list = stream.collect(Collectors.toList()); //List, Set String[] array = stream.toArray(String[]::new); //Array나 특정 컬렉션 지정 Map<String, Integer> map = stream.collect(Collectors.toMap(k -> k, v -> 1, (oldValue, newValue) -> newValue += oldValue)); //키, 초기값, 중복된 키 존재할 경우에 어떻게 합칠지 })
더보기//filter를 사용하여 null 확인 int[] answer = list.stream() .filter(i -> i != null) .mapToInt(i -> i) .toArray();
//Stream 클래스의 toArray() 메서드 Object[] toArray(); <A> A[] toArray(IntFunction<A[]> generator); //제네릭 타입, 새로운 배열을 생성하는 생성자 함수가 매개변수 // 예시 // 1. 메서드 참조 사용 Person[] arr1 = person.stream() .toArray(Person[]::new); // 2. 람다 사용 Person[] arr2 = person.stream() .toArray(size -> new Person[size]);
Optional<T>
- T타입의 객체를 감싸는 래퍼 클래스므로 모든 타입의 객체를 담을 수 있음
- 합계는 값이 없으면 0이 나오면 되지만 최대값, 최소값, 평균은 값이 없으면 NullPointerException 발생
Optional<T> 객체 생성
of(): 객체 생성 (매개변수의 값이 null이면 NullPointerException 발생)
ofNullable(): null 가능성 있을때 사용
empty(): 빈 객체로 초기화
중간 작업
get() 실제 데이터 가져옴 orElse(T other) 값이 null일 때 대체할 값을 지정 orElseGet(Supplier<? extends T> supplie) null을 대체할 값을 반환하는 람다식을 지정 orElseThrow(Supplier<? extends X> exceptionSupplier) null일 때 지정된 예외를 발생, 매개변수가 없으면 NoSuchElementException isPresent() - boolean 데이터의 존재 여부를 확인 ifPresent() - void Optional 객체 값을 가지고 있으면 실행값이 나오고, 없으면 넘어감
반환값이 없으므로 성공 여부를 확인하는 용도로 사용하지 X
최종 작업 (연산)
Optional<T>
OptionalInt, OptionalLong, OptionalDouble 반환
# 꺼낼 때는 get(), getAsInt(), getAsLong(), getAsDouble() 사용
# OptionalInt.of(0) - 0을 저장 (존재O)
OptionalInt.empty() - 빈 객체를 생성 (존재X)
//일반적으로 생각하기에는 평균의 결과가 정수나 실수가 나와야 하는데
//자바에서는 OptionalDouble 이 됨
//Optional이 붙으면 null을 저장할 수 있는 자료형이 되며
//isPresent 라는 메서드를 이용해서 null 여부를 판단하고
//get 이라는 메서드로 데이터를 가져옴
OptionalDouble result = list.stream()
.mapToInt(Integer::parseInt)
.filter(o -> o % 2 == 1)
.average();
//isPresent - 연산을 정상적으로 수행한 경우
if(result.isPresent()) {
System.out.println(result.getAsDouble());
} else {
System.out.println("평균 계산 실패 - 아마도 데이터가 없는 것 같음");
}
//ifPresent - 연산을 정상적으로 수행한 경우
result.ifPresent(average -> {
System.out.println(result.getAsDouble());
//성공한 경우의 추가 동작 구현 가능
});
실습
LoopingMain
package _api.util.stream;
import java.util.ArrayList;
public class LoopingMain {
public static void main(String[] args) {
ArrayList <String> list = new ArrayList<>();
list.add("프로그래밍 언어");
list.add("데이터베이스");
list.add("프레임워크");
list.add("소프트웨어 공학");
list.add("Toy Project");
//전체 데이터 출력 - 실행 속도는 가장 빠르지만 list 의 데이터 개수가 변경되면 수정을 해야 됨
System.out.println(list.get(0));
System.out.println(list.get(1));
System.out.println(list.get(2));
System.out.println(list.get(3));
System.out.println(list.get(4));
//반복문 이용
//list의 데이터 개수를 이용해서 순회하면 list의 데이터 개수가 변경되도 수정할 필요 없음
//계속 메서드를 불러
for(int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//변하지 않는 메서드의 호출 결과를 반복문에서 여러번 부르는 것은 자원의 낭비
//메서드를 부르는 것보다 변수를 불러오는게 속도가 훨씬 빠름
//괄호를 열고 닫는건 메서드를 불러오는 것 - stack 생성
int len = list.size();
for(int i = 0; i < len; i++) {
System.out.println(list.get(i));
}
//모든 데이터를 순회하는 경우라면 빠른 열거를 이용하는 것이 효율적
for(String subject : list) {
System.out.println(subject);
}
//빠른 열거는 반복자를 외부에 만들어서 사용하는데 Stream API는 내부 반복자를 사용
//데이터가 많을 때 효율적
list.stream().forEach(subject -> {
System.out.println(subject);
});
}
}
- 전체 데이터 출력 - System.out.println();
- 반복문 이용 - for(int i = 0; i < list.size(); i++) { };
- 변수 설정 후 반복문 이용
// 괄호를 열고 닫는건 stack 생성해서 메서드를 불러오는 것이므로 자원의 낭비
int len = list.size();
for(int i = 0; i < len; i++) { }; - 빠른 열거, 향상된 for문 사용
// 모든 데이터를 순회하는 경우 향상된 for문 사용하는 것이 효율적
for(String subject : list) { system.out.println(subject); }; - 내부 반복자를 사용하는 Stream API 이용 + 람다식
list.stream().forEach(subject -> {system.out.println(subject);} );
StreamMain1 - 데이터 출력
package _api.util.stream;
import java.util.ArrayList;
public class StreamMain {
public static void main(String[] args) {
//숫자 형태의 문자열의 리스트
ArrayList<String> list = new ArrayList<>();
list.add("28");
list.add("2");
list.add("3");
list.add("6");
list.add("5");
list.add("9");
//최종 연산을 이용해서 출력
//forEach 는 매개변수 1개를 받고 리턴이 없는 메서드를 매개변수로 받음
//Collection 의 모든 데이터를 매개변수에 대입해서 내용을 수행
//list 안의 데이터를 순차적으로 e에 대입해서 {} 안의 내용을 수행
//최종 작업만 수행해서 데이터 출력, 최종 작업은 한번 밖에 못 함
list.stream().forEach(e -> {System.out.println(e);}); // 28, 2, 3, 6, 9, 5
//데이터 3개만 출력
list.stream().limit(3).forEach(e -> {System.out.println(e);}); // 28, 2, 3
}
}
- <String> 타입의 ArrayList 생성 후 add()로
- 람다식 사용: e -> {System.out.println(e);}
StreamMain2 - 데이터 정렬 후 출력
package _api.util.stream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.OptionalDouble;
public class StreamMain {
public static void main(String[] args) {
//숫자 형태의 문자열의 리스트
ArrayList<String> list = new ArrayList<>();
list.add("28");
list.add("2");
list.add("3");
list.add("6");
list.add("5");
list.add("9");
//데이터 정렬 후 출력
//오름차순 정렬 2, 28, 3, 5, 6, 9
list.stream().sorted().forEach(e -> {System.out.println(e);});
//내림차순 정렬 9, 6, 5, 3, 28, 2
//sorted 메서드에 내림차순 정렬을 위한 Comparator 인터페이스를 구현한 클래스의 객체를 설정하면 됨
//Comparator 인터페이스는 매개변수가 2개이고 정수를 리턴하는 메서드 1개만 존재함
//Comparator를 생성할 때 new Comparator<String>() {} 중괄호까지 해줘야 add override가 뜸!!!!!!!
1. list.stream().sorted(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}} ).forEach(e -> {System.out.println(e);});
2. list.stream().sorted((o1, o2) -> o2.compareTo(o1)).forEach(e -> {System.out.println(e);});
//숫자의 경우는 크기 비교가 가능해서 별도의 인스턴스를 대입하지 않아도 정렬이 되고
//내림차순을 하고자 하는 경우 reverse 옵션 설정해주면 됨
3. list.stream()
.map(Integer::parseInt)
.sorted(Comparator.reverseOrder())
.forEach(e -> System.out.println(e));
- 오름차순: 데이터 정렬을 하려면 .sorted() 추가
- 내림차순
1. Comparator 인터페이스를 구현한 클래스의 객체를 설정
Comparator를 생성할 때 new Comparator<String>() {} 중괄호까지 해줘야 add override가 뜸
2. 람다식 (o1, o2) -> o2.compareTo(o1) 사용
3. map(Integer :: parseInt로 숫자로 변환 후 Comparator.reverseOrder() - 여기까지는 2다음 28이 나옴
2022.10.29 - [프로그래밍 언어/Java] - [Java] Comparable VS Comparator
StreamMain3 - 데이터를 정수로 변환해서 정렬
//데이터를 정수로 변환해서 정렬
//중간 처리 메서드 중에는 Int로 리턴해주는 mapToInt 라는 메서드가 존재하고
//이 메서드를 사용할 때는 변환에 사용하는 메서드를 설정만 해주면 됩니다.
//클래스이름::메서드이름
list.stream()
.mapToInt(Integer::parseInt)
.forEach(e -> {System.out.println(e);});
int result = list.stream()
.mapToInt(Integer::parseInt)
.sum();
System.out.println(result); //숫자인지 확인
- Int로 리턴해주는 mapToInt 메서드 사용: 클래스이름::메서드이름
StreamMain4 - filter를 사용하여 조건에 맞는 데이터 추출
//홀수의 합
//filter: 조건에 맞는 데이터만 추출
//조건에 맞는 추출하고자 할 때는 하나의 매개변수를 받아서 boolean을 리턴하는 람다를 만들어서 대입해주면 됨
/*
int result = list.stream()
.mapToInt(Integer::parseInt)
.filter(o -> o % 2 == 1)
.sum();
System.out.println(result);
*/
//홀수의 평균
//일반적으로 생각하기에는 평균의 결과가 정수나 실수가 나와야 하는데
//자바에서는 OptionalDouble 이 됨
//Optional이 붙으면 null을 저장할 수 있는 자료형이 되며
//isPresent 라는 메서드를 이용해서 null 여부를 판단하고
//get 이라는 메서드로 데이터를 가져옴
OptionalDouble result = list.stream()
.mapToInt(Integer::parseInt)
.filter(o -> o % 2 == 1)
.average();
//연산을 정상적으로 수행한 경우
if(result.isPresent()) {
System.out.println(result.getAsDouble());
} else {
System.out.println("평균 계산 실패 - 아마도 데이터가 없는 것 같음");
}
- 홀수의 합
정수형 변수 result 생성 - stream()호출 - mapToInt 정수 변환 - filter 홀수만 추출 - sum 합계 구하기
result 값 출력 - 홀수의 평균
max, min, average를 구할 때 OptionalDouble 타입으로 설정 - stream()호출 - mapToInt 정수 변환 - filter 홀수만 추출 - average 평균 구하기
다만 여기서는 isPresent 메서드를 활용해 null여부 판단 후, get 메서드로 데이터를 가져와야 함
StreamCreate - List, 배열
package chapter14;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class StreamCreate {
public static void main(String[] args) {
//List
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> intStream = list.stream(); //list를 Stream으로 변환
intStream.forEach(System.out::print); //최종연산 - Stream 닫힘
//같은 Stream에 대해서 사용하지 못 하고
intStream = list.stream(); //Stream 다시 생성 후 연산 가능
intStream.forEach(System.out::print);
System.out.println();
//배열 - 문자
// Stream<String> strStream = Stream.of(new String[] {"a", "b", "c", "d"});
Stream<String> strStream = Arrays.stream(new String[] {"a", "b", "c", "d"});
strStream.forEach(System.out::println);
//배열 - 숫자
int[] intArr = {1, 2, 3, 4, 5};
IntStream intStream2 = Arrays.stream(intArr);
// intStream2.forEach(System.out::println); 주석 안 걸면 오류남, 최종연산
System.out.println("count = " + intStream2.count()); //IntStream에는 count, sum, average 등 있음
//Stream<Integer>에는 count밖에 없음, 숫자외에도 여러 타입의 스트임이 가능해야하므로 숫자 스트림에만 사용할 수 있는 것은 없음
}
}
------------------------------------------------------------------------------------------------------------------------------
1234512345
a
b
c
d
count = 5
StreamRandomRange
package chapter14;
import java.util.Random;
import java.util.stream.IntStream;
public class StreamRandomRange {
public static void main(String[] args) {
//임의의 수
IntStream intStream = new Random().ints(1, 10); //혹은 ints(10, 1, 9) 하면 limit가 10임
intStream.limit(5).forEach(System.out::print); //limit이 없으면 무한스트림
System.out.println();
//특정 범위의 정수
IntStream rangeStream = IntStream.range(1, 10);
rangeStream.forEach(System.out::print);
System.out.println();
IntStream rangeStream2 = IntStream.rangeClosed(1, 10);
rangeStream2.forEach(System.out::print);
}
}
---------------------------------------------------------------
63769
123456789
12345678910
IterateAndGenerate
package chapter14;
import java.util.stream.Stream;
public class IterateAndGenerate {
public static void main(String[] args) {
//iterate(T seed, UnaryOperator f) 단항 연산자
Stream<Integer> intStream = Stream.iterate(0, n -> n + 2);
intStream.limit(10).forEach(System.out::println);
//generate(Supplier s) : 주기만 하는 것 입력x, 출력o
Stream<Integer> oneStream = Stream.generate(() -> 1);
oneStream
.limit(5)
.forEach(System.out::println);
}
}
--------------------------------------------------------------
0
2
4
6
8
10
12
14
16
18
1
1
1
1
1
StreamMap
package chapter14;
import java.io.*;
import java.util.stream.Stream;
public class StreamMap {
public static void main(String[] args) {
File[] fileArr = { new File("Ex1.java"), new File("Ex1.bak"),
new File("Ex2.java"), new File("Ex1"), new File("Ex1.txt")
};
Stream<File> fileStream = Stream.of(fileArr);
//map()으로 Stream<File>을 Stream<String>으로 변환
//Stream<String> filenameStream = fileStream.map(File::getName);
Stream<String> filenameStream = fileStream.map((s)-> s.getName()); //File.getName의 리턴 타입 String
filenameStream.forEach(System.out::println); //모든 파일의 이름 출력
System.out.println();
fileStream = Stream.of(fileArr); //스트림을 다시 생성
fileStream.map(File::getName) //Stream<File> -> Stream<String>
.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))
.map(String::toUpperCase) //모두 대문자로 변환
.distinct() //중복 제거
.forEach(System.out::print); //JAVA BAK TXT
System.out.println();
}
}
------------------------------------------------------------------------------------------------------------
Ex1.java
Ex1.bak
Ex2.java
Ex1
Ex1.txt
JAVABAKTXT
StreamFlatMap
package chapter14;
import java.util.*;
import java.util.stream.Stream;
public class StreamFlatMap {
public static void main(String[] args) {
Stream<String[]> strArrStrm = Stream.of(
new String[] {"abc", "def", "jkl"},
new String[] {"ABC", "GHI", "JKL"}
);
// Stream<Stream<String>> strStrmStrm = strArrStrm.map(Arrays::stream);
Stream<String> strStrm = strArrStrm.flatMap(Arrays::stream); //Stream<String>
strStrm.map(String::toLowerCase)
.distinct()
.sorted()
.forEach(System.out::println);
System.out.println();
String[] lineArr = {
"Believe or not It is true",
"Do or do not There is no try",
};
Stream<String> lineStream = Arrays.stream(lineArr);
lineStream.flatMap(line -> Stream.of(line.split(" +"))) //Stream<String>, s.split(" +"): 공백을 구분자로 자른 문자배열
.map(String::toLowerCase)
.distinct()
.sorted()
.forEach(System.out::println);
System.out.println();
}
}
---------------------------------------------------------------------------------------------------
abc
def
ghi
jkl
believe
do
is
it
no
not
or
there
true
try
StreamOptional
package chapter14;
import java.util.Optional;
import java.util.OptionalInt;
public class StreamOptional {
public static void main(String[] args) {
Optional<String> optStr = Optional.of("abcde");
Optional<Integer> optInt = optStr.map(String::length);
System.out.println("optStr = " + optStr.get());
System.out.println("optInt = " + optInt.get());
int result1 = Optional.of("123")
.filter(x -> x.length() > 0)
.map(Integer::parseInt).get();
int result2 = Optional.of("")
.filter(x -> x.length() > 0)
.map(Integer::parseInt).orElse(-1);
System.out.println("result1 = " + result1);
System.out.println("result2 = " + result2);
Optional.of("456").map(Integer::parseInt)
.ifPresent(x -> System.out.printf("result3 = %d%n", x));
OptionalInt optInt1 = OptionalInt.of(0); //0을 저장
OptionalInt optInt2 = OptionalInt.empty(); //빈 객체를 생성
System.out.println(optInt1.isPresent()); //true
System.out.println(optInt2.isPresent()); //false
System.out.println(optInt1.getAsInt()); //0
//System.out.println(optInt2.getAsInt()); //NoSuchElementException
System.out.println(optInt1); //OptionalInt[0]
System.out.println(optInt2); //OptionalInt.empty
System.out.println(optInt1.equals(optInt2)); //false
}
}
StreamReduce
package chapter14;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class StreamReduce {
public static void main(String[] args) {
String[] strArr = {
"Inheritance", "Java", "Lambda", "stream", "OptionalDouble", "IntStream", "count", "sum"
};
Stream.of(strArr).forEach(System.out::println);
boolean noEmptyStr = Stream.of(strArr).noneMatch(s -> s.length() == 0); //길이가 0인게 없다
System.out.println(noEmptyStr); //true
Optional<String> sWord = Stream.of(strArr).filter(s -> s.charAt(0) =='s').findFirst();
System.out.println(sWord.get()); //stream
//Steam<String>을 Stream<Integer>로 변환
Stream<Integer> intStream = Stream.of(strArr).map(String::length);
//Steam<String>을 IntStream(기본형)으로 변환
IntStream intStream1 = Stream.of(strArr).mapToInt(String::length);
IntStream intStream2 = Stream.of(strArr).mapToInt(String::length);
IntStream intStream3 = Stream.of(strArr).mapToInt(String::length);
IntStream intStream4 = Stream.of(strArr).mapToInt(String::length);
int count = intStream1.reduce(0, (a, b) -> a + 1);
int sum = intStream2.reduce(0, (a, b) -> a + b);
System.out.println(count);
System.out.println(sum);
OptionalInt max = intStream3.reduce(Integer::max);
OptionalInt min = intStream4.reduce(Integer::min);
System.out.println(max.getAsInt());
System.out.println(min.getAsInt());
}
}
'프로그래밍 언어 > Java' 카테고리의 다른 글
[Java] Process - Thread, synchronized, deadLock, semaphore (0) | 2022.10.31 |
---|---|
[Java] Comparable VS Comparator (0) | 2022.10.29 |
[Java] API_ java.io (file, stream, buffer) / Input & Output (0) | 2022.10.27 |
[Java] API_ Collection Framework (List, Set, Map) (0) | 2022.10.26 |
[Java] API_ java.util (Generic, 제네릭)(컬렉션 프레임워크), ArrayList (0) | 2022.10.26 |