신세게 - Java 공부

6주차 배운점 느낀점 - stream, 필터링, 매핑, 정렬, 루핑, 매칭, 집계, Optimal, 요소 수집, 그룹핑

휘로그 2024. 10. 5. 15:21
728x90
반응형

6주차

 

실습하다가 Student클래스를 사용했다. 합(sum)과 평균(average)를 구해야되서 필드로 놨두려고 했는데 강사님이 합, 평균은 student 정보데이타는 보관할필요가없다고 하셨다.
뭐 경우에 따라 다르겠지만, 이정도는 그때그때 계산해서 사용해야겠다.
어제까지 배웠던, stream에서 사용하던 중간,최종 연산자들을 자세하게 배웠다. filter, distinct등 하나하나 어떻게 동작하는지, 각각 예제를 실습했다.그외 comparable, 루핑, 매칭, Optimal, 요소 수집, 구룹핑을 학습했다.

 

배운점

 

컬렉션으로부터 스트림 얻기
java.util.Collection 인터페이스는 스트림과 parallelStream() 메소드를 가지고 있어 자식 인터페이스인 List와 Set 인터페이스를 구현한 모든 컬렉션에서 객체 스트림을 얻을 수 있음

 

배열로부터 스트림 얻기
java.util.Arrays 클래스로 다양한 종류의 배열로부터 스트림을 얻을 수 있음

 

숫자 범위로부터 스트림 얻기
IntStream 또는 LongStream의 정적 메소드인 range()와 rangeClosed() 메소드로 특정 범위의 정수스트림을 얻을 수 있음

 

파일로부터 스트림 얻기
java.nio.file.Files의 lines() 메소드로 텍스트 파일의 행 단위 스트림을 얻을 수 있음

 

=>여러개의 리소스에서 stream을 이용해서 데이터를 추출해낼 수 있구나 ! 정도 기억

 

필터링

 

필터링은 요소를 걸러내는 중간 처리 기능

 

 


distinct() 메소드: 요소의 중복을 제거

 

 


filter() 메소드: 매개값으로 주어진 Predicate가 true를 리턴하는 요소만 필터링

 


Predicate: 함수형인터페이스

 

 


모든 Predicate는 매개값을 조사한 후 boolean을 리턴하는 test() 메소드를 가지고 있다. 검사 후 true, false 리턴

 

 

필터링 예제
.distinct 중복제거
.startWith("정") 해당 문자열로 시작하는거 true, false로 반환

public static void main(String[] args) {

    //1. List<String> 컬렉션 AraayList namelist 생성하세요
    List<String> nameList = new ArrayList<>();


    //2. namelist에 이름을 추가해주세요. 같은 이름을 두번 넣어주세요.
    nameList.add("정휘제");
    nameList.add("서유미");
    nameList.add("이한강");
    nameList.add("이수연");
    nameList.add("정휘제");


    //3. 중복 요소 제거 해 봅시다.
    nameList.stream().distinct().forEach(System.out::println);

    //4. "정"으로 시작하는 요소만 필터링
    Stream<String> jungganae = nameList.stream().filter(name -> name.startsWith("정"));

    //중복요소를 제거하고, "정으로 시작하는 요소만 필터링 하여 lastnamelist로 저장하세요
    Stream<String> lastnameList = nameList.stream().filter(name -> name.startsWith("정")).distinct();

    //lastnameList를 출력하세요.
    lastnameList.forEach(System.out::println);

  }

 

매핑

스트림의 요소를 다른 요소로 변환하는 중간 처리 기능
매핑 메소드: mapXxx(), asDoubleStream(), asLongStream(), boxed(), flatMapXxx() 등

 

요소를 다른 요소로 변환

mapXxx() 메소드: 요소를 다른 요소로
변환한 새로운 스트림을 리턴
원하는 타입으로 바꾸고 새 스트림으로 리턴

 

 


매개타입인 Function은 함수형 인터페이스

 

 


모든 Function은 매개값을 리턴값으로 매핑(변환)하는 applyXxx() 메소드를 가짐

 

 

stream을 리스트로

외부에서 가져온 변수를 상수로 침, 바꿀 수 없다.
indxOf로 번호부여함: map(st ->((stulist.indexOf(st)+1) + " " + ...
map안에서 + 해서 변수 여러개 쓸 수 있음, 결국 각 요소가 문자열 되는거임
리스트로 받을때 (List<Integer> mathScoreList =) .toList로 해주면 스트림 -> 리스트로 반환

public class StudentMappingEx {
  public static void main(String[] args) {
    //1. Student 클래스 타입의 stulist 리스트 컬렉션을 생성하세요
    List<Student> stulist = new ArrayList<>();

    //2. Scanner 클래스로 학생의 이름과 성적을 입력받아 5명의 학생 객체를 stulist에 저장하세요
    //    Scanner sc = new Scanner(System.in);
    //    for (int i = 0; i < 5; i++) {
    //      String name = sc.nextLine();
    //      int kor = Integer.parseInt(sc.next());
    //      int eng = Integer.parseInt(sc.next());
    //      int mat = Integer.parseInt(sc.next());
    //      stulist.add(new Student(name, kor, eng, mat));
    //    }

    stulist.add(new Student("정휘제1",12,23,23));
    stulist.add(new Student("정휘제2",32,45,76));
    stulist.add(new Student("정휘제3",67,45,65));
    stulist.add(new Student("정휘제4",13,24,23));
    stulist.add(new Student("정휘제5",43,23,78));

    //3. 학생별 이름만 저장한 namelist를 만들고 출력하세요
    Stream<String> nameList = stulist.stream().map(Student::getName);
    nameList.forEach(System.out::println);

    //4. 학생별 국어점수를 저장한 korScoreList 만들고 출력하세요
    List<Integer> korScoreList = stulist.stream().map(Student::getKorscore).collect(Collectors.toList());
    korScoreList.forEach(System.out::println);

    //5. 국어점수의 총합과 평균을 구하여 출력하세요
    System.out.println(stulist.stream().map(Student::getKorscore).mapToInt(Integer::intValue).sum());
    System.out.println(stulist.stream().map(Student::getKorscore).mapToDouble(Double::valueOf).average().orElse(0.0));

    //6. 학생별 영어점수를 저장한 engScoreList 만들고 출력하세요
    List<Integer> engScoreList = stulist.stream().map(Student::getKorscore).collect(Collectors.toList());
    engScoreList.forEach(System.out::println);

    //7. 영어 점수의 총합과 평균을 구하여 출력하세요
    System.out.println(stulist.stream().map(Student::getEngscore).mapToInt(Integer::intValue).sum());
    System.out.println(stulist.stream().map(Student::getEngscore).mapToDouble(Double::valueOf).average().orElse(0.0));

    //8. 학생별 수학저수를 저장한 mathScoreList 만들고 출력하세요
    List<Integer> mathScoreList = stulist.stream().map(Student::getKorscore).collect(Collectors.toList());
    mathScoreList.forEach(System.out::println);

    //9. 수학점수의 총합과 평균을 구하여 출력하세요
    System.out.println(stulist.stream().map(Student::getMathscore).mapToInt(Integer::intValue).sum());
    System.out.println(stulist.stream().map(Student::getMathscore).mapToDouble(Double::valueOf).average().orElse(0.0));

    //10. 학생별 총합 점수와 평균을 구하여 totalExam에 저장하여 성적표를 출력하세요
    //totalExam 리스트
    System.out.println("번호 이름 국어 영어 수학 총점 평균");
    List<String> totalExam = stulist.stream().map(st -> (stulist.indexOf(st)+1) + "   " + st.getName() + "  " + st.getKorscore() + "  " + st.getEngscore() + "  " + st.getMathscore()
                    + "  " + (st.getKorscore() + st.getEngscore() + st.getMathscore()) + "  " + ((double)(st.getKorscore() + st.getEngscore() + st.getMathscore())/3))
            .collect(Collectors.toList());

    totalExam.stream().forEach(System.out::println);

  }
}

 

정수 스트림을 실수 스트림으로 변환, 기본 타입 스트림을 래퍼

 

public static void main(String[] args) {
    //1. 정수형 배열 intArray 1,2,3,4,5 초기화
    int[] intArray = {1,2,3,4,5};

    //2. intArray를 intStream으로 생성해주세요.
    IntStream intStream = Arrays.stream(intArray);

    //3. intStream 객체를 double로
    intStream.asDoubleStream().forEach(System.out::println);

    System.out.println();

    // int타입을 wrapper객체로 변환 (Object 타입)
    intStream = Arrays.stream(intArray);
    intStream.boxed().forEach(obj-> System.out.println(obj.intValue()));
  }

 

기본 타입 간의 변환이거나 기본 타입 요소를 래퍼(Wrapper) 객체 요소로 변환하려면 간편화 메소드를 사용할 수 있음


요소를 복수 개의 요소로 변환
flatMapXxx() 메소드: 하나의 요소를 복수 개의 요소들로 변환한 새로운 스트림을 리턴

 

public static void main(String[] args) {
    List<String> list1 = new ArrayList<>();
    list1.add("this is java back end course.");
    list1.add("i am a best developer");
    list1.stream()
            .flatMap(data-> Arrays.stream(data.split(" ")))
            .forEach(System.out::println);
    System.out.println();
  }

 

정렬
요소를 오름차순 또는 내림차순으로 정렬하는 중간 처리 기능


Comparable 구현 객체의 정렬
스트림의 요소가 객체일 경우 객체가 Comparable을 구현하고 있어야만 sorted() 메소드를
사용하여 정렬 가능. 그렇지 않다면 ClassCastException 발생

 


sorted()를 쓸 수 있는 이유: Student에 implement해줬기 때문
역순할때: sorted(Comparator.reverseOrder())

public class Student implements Comparable<Student>{

@Override
  public int compareTo(Student o){
    return Integer.compare(score, o.score);
  }

 

Comparator를 이용한 정렬
요소 객체가 Comparable을 구현하고 있지 않다면, 비교자를 제공하면 요소를 정렬시킬 수 있음


괄호 안에는 o1이 o2보다 작으면 음수, 같으면 0, 크면 양수를 리턴하도록 작성
o1과 o2가 정수일 경우에는 Integer.compare(o1, o2)를, 실수일 경우에는 Double.compare(o1, o2)를 호출해서 리턴값을 리턴 가능

 

score 기준 오름차순:
sorted((s1,s2)->Integer.compare(s1.getScore(),s2.getScore())..
역순은 s1, s2 자리바꿔줄 것

public static void main(String[] args) {
    List<Student> studentList = new ArrayList<>();
    studentList.add(new Student("김나나",90));
    studentList.add(new Student("신지수",90));

    //점수를 기준으로 오름차순 정렬한새 스트림을 얻어오기
    //sorted()를 쓸 수 있는 이유: Student에 implement해줬기 때문
    studentList.stream().sorted().forEach(student -> System.out.println(student.getName()+":"+student.getScore()));

    //점수를 기준으로 내림차순 정렬한 새 스트림 얻어오기
    studentList.stream().sorted(Comparator.reverseOrder()).forEach(student-> System.out.println(student.getName()+":"+student.getScore()));

    //만약 Comparable 인터페이스가 구현되지 않은 구현체를 정렬하려면 Comparator를 이용한다.
    List<Student1> studentList2 = new ArrayList<>();
    studentList2.add(new Student1("김이이",90));
    studentList2.add(new Student1("신지송",90));

    //점수를 기준으로 오름차순으로 정렬한 새 스트림 생성
    // score기준 오름차순 하겠다.
    studentList2.stream()
            .sorted((s1,s2)->Integer.compare(s1.getScore(),s2.getScore()))
            .forEach(student1 -> System.out.println(student1.getName()+" "+student1.getScore()));

    //점수를 기준으로 내림차순으로 정렬한 새 스트림 얻기
    //s1, s2 순서바꾸기
    studentList2.stream()
            .sorted((s1,s2)->Integer.compare(s2.getScore(),s1.getScore()))
            .forEach(student1 -> System.out.println(student1.getName()+" "+student1.getScore()));


  }

 

루핑
스트림에서 요소를 하나씩 반복해서 가져와 처리하는 것

 


매개타입인 Consumer는 함수형 인터페이스. 모든 Consumer는 매개값을 처리(소비)하는
accept() 메소드를 가지고 있음, 매개값을 처리(소비)

 

 

요소 루핑: peek() : 중간처리 메소드, foreach() : 최종처리 메소드
최종처리가 아니기때문에 결과가 안나옴:
.peek(n-> System.out.println(n))

public static void main(String[] args) {
    int[] intArray = {1,2,3,4,5};

    //최종처리가 아니기때문에 결과가 안나옴
    Arrays.stream(intArray).filter(number->number%2==0).peek(n-> System.out.println(n));

    // 디버깅용 작업한내용 눈으로 확인용, peek 출력
    // 마지막 sum 출력
    int total = Arrays.stream(intArray).filter(number->number%2==0).peek(n-> System.out.println(n)).sum();
    System.out.println(total);
  }

 

매칭
요소들이 특정 조건에 만족하는지 여부를 조사하는 최종 처리 기능
allMatch(), anyMatch(), noneMatch() 메소드는 매개값으로 주어진 Predicate가 리턴하는 값에 따라 true 또는 false를 리턴
모든 조건 참 true -> allMatch
하나라도 참 true -> anyMatch
모든요소가 아닌지 true -> noneMatch()
=> 데이터 조사!!

 

public static void main(String[] args) {
    int[] intArray = {2,4,6};

    // 모두 2의 배수인가?
    boolean result = Arrays.stream(intArray).allMatch(number->number%2==0);
    // 하나라도 2의 배수가 있는가?
    boolean result1 = Arrays.stream(intArray).anyMatch(number->number%2==0);
    // 2의 배수가 없는가?
    boolean result2 = Arrays.stream(intArray).noneMatch(number->number%3==0);
    System.out.println(result);
    System.out.println(result1);
    System.out.println(result2);
  }

 

집계
최종 처리 기능으로 요소들을 처리해서 카운팅, 합계, 평균값, 최대값, 최소값 등 하나의 값으로
산출하는 것

 

스트림이 제공하는 기본 집계
스트림은 카운팅, 최대, 최소, 평균, 합계 등을 처리하는 다음과 같은 최종 처리 메소드를 제공

 

optinal 일때 getAsInt, getDouble 사용, orElse()해줘도됨

 

public static void main(String[] args) {
    int[] array = {1,2,3,4,5,6,7,8,9,10};

    //1. 카운팅
    System.out.println(Arrays.stream(array).count());

    //2. 총합
    System.out.println(Arrays.stream(array).sum());

    //3. 평균
    System.out.println(Arrays.stream(array).average().orElse(0.0));

    //4. 최댓값
    System.out.println(Arrays.stream(array).max().orElse(0));

    //5. 최솟값
    System.out.println(Arrays.stream(array).min().getAsInt());

    //6. 첫요소 추출
    System.out.println(Arrays.stream(array).findFirst().getAsInt());
  }

 

Optional 클래스


Optional, OptionalDouble, OptionalInt, OptionalLong 클래스는 단순히 집계값만 저장하는 것이 아니라, 집계값이 없으면 디폴트 값을 설정하거나 집계값을 처리하는 Consumer를 등록
isPresent , orElse 이런건 써봄

 


최종 처리에서 average 사용 시 요소 없는 경우를 대비하는 방법
1) isPresent() 메소드가 true를 리턴할 때만 집계값을 얻는다.
nullpointexception 나지 않게, 값이 있을 때만 집계
2) orElse() 메소드로 집계값이 없을 경우를 대비해서 디폴트 값을 정해놓는다.
집계값이 없을때를 대비해서 0.0같은거 넣어줌
3) ifPresent() 메소드로 집계값이 있을 경우에만 동작하는 Consumer 람다식을 제공한다.
집계값 있으면 람다로 처리

 

public static void main(String[] args) {
    List<Integer> list = new ArrayList<>();

    OptionalDouble optionalDouble = list.stream()
            .mapToInt(Integer::intValue).average();

    //Optional 처리 방법 1
    // 만약에 표현할게 있다고 하면
    if(optionalDouble.isPresent()){
      System.out.println("평균 :"+optionalDouble.getAsDouble());
    }else {
      System.out.println("평균 :"+0.0);
    }

    //Optional 처리 방법 2
    double avg = list.stream()
            .mapToInt(Integer::intValue).average().orElse(0.0);
    System.out.println(avg);

    //Optional 처리 방법 3
    list.stream().mapToInt(Integer::intValue).average()
            .ifPresent(average -> System.out.println("방법3 avg: "+average));

  }

 

요소 수집
스트림은 요소들을 필터링, 매핑한 후 요소들을 수집하는 최종처리 메소드 collect()제공한다. coolect()메소드를 사용하면 필요한 요소만 컬렉션에 담을 수 있고, 요소들을 그룹핑한 후 집계도 할 수 있다.

 

Stream의 collect(Collector<T,A,R> collector)
T요소를 A에 누적시켜서 R에 저장시킨다.

 

collector은 어떤 요소를 어떤 컬렉션에 수집할 것인지 결정

 

Stream의 collect(Collector<T,A,R> collector) 메소드는 필터링 또는 매핑된 요소들을 새로운
컬렉션에 수집하고, 이 컬렉션을 리턴
매개값인 Collector는 어떤 요소를 어떤 컬렉션에 수집할 것인지를 결정
타입 파라미터의 T는 요소, A는 누적기accumulator, 그리고 R은 요소가 저장될 컬렉션

 

요소 그룹핑
Collectors.groupingBy () 메소드에서 얻은 Collector를 collect() 메소드를 호출할 때 제공
groupingBy()는 Function을 이용해서 T를 K로 매핑하고, K를 키로 해 List<T>를 값으로 갖는 Map 컬렉션을 생성

 

 

Collectors.groupingBy() 메소드는 그룹핑 후
매핑 및 집계(평균, 카운팅, 연결, 최대, 최소, 합계)를 수행할 수 있도록 두 번째 매개값인 Collector를 가질 수 있음

  public static void main(String[] args) {
    List<Member> memberList = Arrays.asList(
            new Member(1, "홍길동", "F"),
            new Member(2, "홍삼동", "M"),
            new Member(3, "김길동", "M"),
            new Member(4, "손지나", "F"),
            new Member(5, "윤성렬", "M"));

    // 남자회원 "M" 따로 menList에 저장하고 출력

    List<Member> menList1 = memberList.stream()
            .filter(member -> member.getGender().equals("M")).toList();

    List<Member> menList2 = memberList.stream()
            .filter(member -> member.getGender().equals("M")).collect(Collectors.toList());

    System.out.println(menList1 + " " + menList2);

    //회원정보 리스트에서 이름만 추출하여 출력하세요
    memberList.stream().forEach(member -> System.out.println(member.getName()));

    //회원의 회원번호를 키로 회원이름을 값으로 하여 Map생성
    // 넘버가 키가되도록 매핑
    Map<Integer, String> map = memberList.stream()
            .collect(Collectors.toMap(Member::getNo, Member::getName));
    System.out.println(map);

    //"M" "F" 키로 설정하여 List<Member>
    Map<String, List<Member>> genderGroupmap
            = memberList.stream().collect(Collectors.groupingBy(Member::getGender));

    System.out.println("====남성회원리스트====");
    List<Member> maleMemberList = genderGroupmap.get("M");
    maleMemberList.stream().forEach(member -> System.out.println(member.getName()));

    System.out.println("====여성회원리스트====");
    List<Member> femaleMemberList = genderGroupmap.get("F");
    femaleMemberList.stream().forEach(member -> System.out.println(member.getName()));

  }

회고

 

강사님 스타일이 한 번에 많이 나가고 그거에 뒷 수업에서 세세히 나가시는 것 같다. 처음 배우는 내용이 빡세더라도 멘탈 깨지지말자.

 

stream이 마냥 짧고 간결해서 좋다고 느꼈었다. 더 많은 기능이 있는걸알았다. 인텔리제이에서 . 만찍어도 다음 함수를 가르쳐줘서 더 편했다. 이 때까지 한 내용중에 stream 수업은 재밌다.

반응형