⚠️Warning
본 포스트는 당일 학원에서 배운 내용을 복습하는 목적의 공부 기록 시리즈입니다. 정보 전달의 목적이 아님을 유의해주세요! 잘못된 내용에 대한 피드백을 환영합니다:)
java의 Stream
- 입출력 스트림
- 파일 입출력, 콘솔 입출력, 네트워크 입출력..
- 스트림
- Java 8(JDK 1.8)
- 람다식 + 함수형 인터페이스
- 배열(컬렉션)의 탐색(조작) 지원
- 파일 입출력 지원
- 디렉토리 탐색 지원
list.stream().forEach() 의 구조는 ↓↓배열.스트림을 만들고.스트림을 사용하는 도구 이렇게!!forEach() 같은 애들이 파이프)filter()forEach()List<Integer> list = Data.getIntList(20);
//요구사항] 짝수만 출력
//Case 1) for문 활용 출력
for (int n : list) {
if (n % 2 == 0) {
System.out.printf("%-4d", n);
}
}
//Case 2) Stream 활용 출력
list.stream().filter(num -> num % 2 == 0).forEach(num -> {
System.out.printf("%-4d", num);
});
💡 Stream()
.filter(Predicate)
.map(Function)
.sorted(Comparator)
.distinct
.count()/max()/min()/average()/sum()
.allMatch()/anyMatch()/noneMatch()
.forEach()
//배열(컬렉션) 탐색
List<String> list = Data.getStringList(10);
//1. for문(loop 변수 사용)
for (int i=0; i<list.size(); i++) {
System.out.print(list.get(i) + " ");
}
//2. 향상된 for문
for (String word : list) {
System.out.print(word + " ");
}
//3. Iterator
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
System.out.print(iter.next() + " ");
}
//4. Stream
Stream<String> stream = list.stream();
//4-1.
Consumer<String> c1 = str -> System.out.print(str + " ");
stream.forEach(c1);
//4-2. 메소드 체이닝
list.stream().forEach(str -> System.out.print(str + " "));
forEach 메소드
1. stream으로부터 얻어낸 데이터 → 1개씩 가져온다.(like 향상된 for문)
2. 가져온 데이터 → Consumer.accept() 메소드 인자 전달 + 메소드 호출
3. 반복 x 요소만큼
Data.getUserList().stream().forEach(user -> {
System.out.println("[회원정보]");
System.out.println("이름: " + user.getName());
System.out.println("나이: " + user.getAge());
System.out.println("성별: " + (user.getGender() == 1 ? "남자" : "여자"));
System.out.println();
});
//출력
[회원정보]
이름: 유재석
나이: 29
성별: 남자
[회원정보]
이름: 박명수
나이: 23
성별: 남자
Data.getItemList().stream().forEach(item -> {
System.out.println(item.getName());
System.out.println(item.getColor());
System.out.println();
});
//출력
마우스
BLACK
키보드
BLUE
때문에 가독성을 높이기 위해서는 개행을 적절하게 사용해주면 좋다.
Data
.getIntList()
.stream().
forEach(num -> System.out.printf("%4d", num));
- 배열로부터★★★
- 컬렉션으로부터 ★★★
- 숫자 범위로부터
- 파일로부터
- 디렉토리로부터
int[] nums1 = {10, 20, 30};
Arrays.stream(nums1).forEach(num -> System.out.println(num));
//출력
10
20
30
ArrayList<Integer> nums2 = new ArrayList<Integer>();
nums2.add(100);
nums2.add(200);
nums2.add(300);
nums2.stream().forEach(num -> System.out.println(num));
//출력
100
200
300
//Stream<Integer> : 범용 스트림 > forEach > Consumer<Integer>
//IntStream : 전용 스트림 > forEach > IntConsumer
IntStream.range(1, 10).forEach(num -> System.out.print(num)); //일련번호 처리할 때 따로 배열이 없을 때 사용하기 좋음
//출력
123456789
. : 현재 실행 파일이 있는 폴더를 뜻함."C:\\class\\code\\JavaTest\\data\\number.txt" == ".\\data\\number.txt" == "data\\number.txt"try {
//java.io > 버전업 > java.nio
Path path = Paths.get("data\\number.txt"); // '.\\'생략도 가능
Files.lines(path).forEach(line -> System.out.println(line));
} catch (Exception e) {
System.out.println("at Ex74_Stream.m4");
e.printStackTrace();
}
//출력
number.txt 파일 1줄씩 읽어옴
dir.listFiles() try {
Path dir = Paths.get("C:\\class\\dev\\eclipse");
Files.list(dir).forEach(p -> {
System.out.println(p.getFileName());
System.out.println(p.toFile().isFile());
});
//Path는 File 클래스와 목적이 달라 isFile,isDir이없으나 toFile쓰면 파일클래스의 메소드들을 사용할 수 있게 됨!
} catch (Exception e) {
System.out.println("at Ex74_Stream.m4");
e.printStackTrace();
}
//출력
해당 경로 내 목록 출력 및 file이면 'true', file이 아니면 'false'값 출력
filter()Data.getUserList().stream()
.filter(user -> user.getWeight() >= 70)
.filter(user -> user.getGender() == 1) //계속 파이프 걸기 가능!!(스트림의 장점)
.filter(user -> user.getHeight() >= 180)
.forEach(user -> System.out.println(user));
System.out.println();
System.out.println();
distinct() 중복 제거distinct()String[] names = { "홍길동", "아무개", "이순신", "권율", "강감찬", "연개소문", "홍길동", "이순신", "남궁장군", "연개소문"};
Arrays.stream(names)
.distinct() //중복값인 홍길동, 이순신 제거
.filter(name -> name.length() == 3) //중복값 제거된 이름중 이름이 3글자인것만 필터링
.forEach(name -> System.out.println(name));
//출력
//홍길동, 아무개, 이순신, 강감찬
Set, Distinct() → 중복 객체를 제거하려면?
1. hashCode() 재정의
2. equals() 재정의→ java는 객체와 객체를 비교할 때는 주소값 비교를 하기 때문에 아무리 상태가 똑같아도 중복값으로 처리되지 않는다. 때문에 위의 2가지를 재정의 해주어야만 중복 객체를 제거할 수 있다.
private static void m6() {
List<Cup> clist = new ArrayList<Cup>();
//아래서 만든 final static 상수 사용시 내부적으로는 숫자, 외부적으로는 문자라 편리~
clist.add(new Cup(Cup.BLACK, 200));
clist.add(new Cup(Cup.BLUE, 300));
clist.add(new Cup(Cup.RED, 400));
clist.add(new Cup(Cup.WHITE, 500)); //1
clist.add(new Cup(Cup.YELLOW, 600)); //2
clist.add(new Cup(Cup.BLACK, 700));
clist.add(new Cup(Cup.RED, 800));
clist.add(new Cup(Cup.BLUE, 900));
clist.add(new Cup(Cup.YELLOW, 600)); //2
clist.add(new Cup(Cup.WHITE, 500)); //1
clist.stream()
.distinct()
.forEach(cup -> System.out.println(cup));
System.out.println();
System.out.println();
//java는 obj와 obj를 비교할 때는 주소값 비교를 한다 > 아무리 distinct를 해도 컵은 여전히 10개가 나온다.
//그럼 여기서 distinct로 상태가 똑같으면 동일한 것으로 필터링 되어 중복값 걸러지도록 해보자!
//Set, distinct() > 중복 객체를 제거하려면?
//1. hashCode() 재정의
//2. equals() 재정의
//---------> 아래 cup class 보시오!
//아래 Cup class 후작업 후 결과값 출력하면 ----> 컵은 중복값이 제거된 8개가 나온다!!
}
//이전 > Set 수업 내용 중 하나
class Cup {
public final static int BLACK = 1;
public final static int WHITE = 2;
public final static int RED = 3;
public final static int YELLOW = 4;
public final static int BLUE = 5;
//private String color2; //주관식 > color2 = "whte" > 혹여나 오타 발생 시 관련된 모든 유효성검사를 다 해야 하므로.. > 개발자 손을 다 타야하고 사용자가 실수를 할 확률 多 But 위처럼 상수처리하면 .찍으면 나오고 알아보기도 쉽고, 실수할 확률이 줄어듬
private int color; //빨강, 파랑...선택하는 값(나열해놓고 하나 콕 찝어서 선택하는 것들) > 열거값
private int size;
public Cup(int color, int size) {
this.color = color;
this.size = size;
}
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
@Override
public String toString() {
return "Cup [color=" + color + ", size=" + size + "]";
}
//개발자의 의도대로 객체를 비교하려면..
@Override
public int hashCode() {
return ("" + this.color + this.size).hashCode(); //숫자 > 값형 > 참조주소가 없으므로 바로 주소값을 못얻어내므로 숫자를 문자열로 바꿔야함. > 공식적으로 String.value of 이나 앞에 "" 더해서 간단하게 문자열로 바꾸기
}
@Override
public boolean equals(Object obj) {
return this.hashCode() == obj.hashCode();
}
}//Cup
💡 중복값을 제거하는 여러가지 방법
- loop 돌기 (루프 돌며 1:1로 중복값 확인)
- Set
- Stream().distinct()👍
List<Integer> list = Data.getIntList(); //원본 size: 100
Set<Integer> set1 = new HashSet<Integer>();
for (int n : list) {
set1.add(n); //List의 값이 HashSet에 들어오면서 자동으로 중복값 제거
}
System.out.println(set1.size()); //61Set<Integer> set2 = new HashSet<Integer>(list);
System.out.println(set2.size()); //61```java
System.out.println(list.stream().distinct().count()); //61 > 중복값 제거한 개수
//forEach의 역할: 데이터를 1개씩 가져와서 소비
//stream에서도 개수를 셀 수 있음! > stream().count() > 최종 파이프
```map() 매핑: 넘겨받은 값을 뚝딱뚝딱 가공해서 전혀 다른 값으로 돌려주기
map(), mapXXX()List<String> list = Data.getStringList(10);
list.stream() //Stream<String>
.map(word -> word.length()) //String을 Integer로 변환 //Stream<Integer>
.forEach(num -> System.out.println(num));
System.out.println(); //출력되는 숫자는 글자수
String[] names = { "홍길동", "아무개", "이순신", "권율", "강감찬", "연개소문", "홍길동", "이순신", "남궁장군", "연개소문"};
//첫글자가 성, 나머지가 이름이라는 가정하에 이름만 추출해쥬세용
//이름 추출
Arrays.stream(names)
.map(name -> name.substring(1)) //문자열을 받아서 가공된 문자열 스트림 내보내깅
.forEach(name -> System.out.println(name));
private static void m7() {
List<Student> slist = new ArrayList<Student>();
slist.add(new Student("가가가", 100, 90, 80));
slist.add(new Student("나나나", 50, 60, 70));
slist.add(new Student("다다다", 90, 100, 30));
slist.add(new Student("라라라", 40, 90, 20));
slist.add(new Student("마마마", 80, 40, 100));
slist.stream()
.map(s-> {
if ((s.getKor() + s.getEng() + s.getMath()) >= 180) {
Result r = new Result();
r.setName(s.getName());
r.setResult("합격");
return r; //뭘 돌려주는 내맘. 단일 데이터든 객체든 노상관 > 객체의경우 객체에 . 찍으면 이름과 합격여부를 따로 관리가 가능하니 훨 좋음!!
} else {
Result r = new Result();
r.setName(s.getName());
r.setResult("불합격");
return r;
}
})
.forEach(r -> {
System.out.println("이름: " + r.getName());
System.out.println("결과: " + r.getResult());
System.out.println();
});
}//m7
//String<Student>를 받아서 -> map() 을 거치면 -> Stream<Result>를 돌려줄 예정
class Result {
private String name;
private String result;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
}
sorted()Data.getIntList(10)
.stream()
.sorted((a, b) -> b - a) //인자값 없으면 오름차순, 있으면 내맘대로
.forEach(n -> System.out.println(n));
Match() 매칭allMatch(), anyMatch(), noneMatch()//요구사항] 배열 > 짝수만 있는지?
int[] nums = {1, 2, 3, 4, 5};
boolean result = false;
for (int n : nums) {
if (n % 2 == 1) {
result = true;
break;
}
}
if (result) {
System.out.println("홀수 발견!!");
} else {
System.out.println("짝수 배열~");
}
//위 작업을 stream으로~~
System.out.println(Arrays.stream(nums).allMatch(n -> n % 2 == 0)); //모두 짝수니? false
//홀수가 발견되어 모든 요소가 조건을 만족하지 않으므로 false값 반환 > 훨씬 간단~~~
System.out.println(Arrays.stream(nums).anyMatch(n -> n % 2 == 0)); //한개라도 짝수가 있니? true
System.out.println(Arrays.stream(nums).noneMatch(n -> n % 2 == 0)); //모두 짝수가 아니니? false
count(), max(), min(), sum(), avg()System.out.println(Data.getIntList().stream().count()); //100
System.out.println(Data.getIntList().stream().max((a, b) -> a - b).get()); //Optional[99] > 진짜 Integer로 바꿀려면 .get() 쓰면 됨!!
System.out.println(Data.getIntList().stream().min((a, b) -> a - b).get()); //Optional[0] > 진짜 Integer로 바꿀려면 .get() 쓰면 됨!!
//count, max, min 안의 값이 숫자가 아니어도 할 수 있는 일!
//sum, avg는 안의 값이 숫자여야만 할 수 있는 일!! > 전용 스트림으로만 할 수 있음!!!
int sum = Data.getIntList().stream()
.mapToInt(n -> n) //Stream<Integer>(X) IntStream(O)
.sum();
System.out.println();
double avg = Data.getIntList().stream()
.mapToInt(n -> n)
.average().getAsDouble();
System.out.println(avg);
❗️ 주의 동일 stream을 재사용하려면 다시 stream을 생성해야 함!
→ stream은 forEach를 돌면서 요소를 소비하기 때문에 동일한 stream으로는 소비할 요소가 안 남아있음. 때문에 Error 발생
'java.lang.IllegalStateException: stream has already been operated upon or closed at'