[Java] Stream, 람다식 표현들

devdo·2022년 4월 13일
1

Java

목록 보기
43/59
post-thumbnail
post-custom-banner

람다 표현식

람다
(인자 리스트) -> {바디}

인자 리스트

  • 인자가 없을 때: ()
  • 인자가 한개일 때: (one) 또는 one
  • 인자가 여러개 일 때: (one, two)
  • 인자의 타입은 생략 가능, 컴파일러가 추론(infer)하지만 명시할 수도 있다. (Integer one, Integer two)

바디

  • 화상표 오른쪽에 함수 본문을 정의한다.
  • 여러 줄인 경우에 { }를 사용해서 묶는다.
  • 한 줄인 경우에 생략 가능, return도 생략 가능.

자바 8 API의 기본 메소드와 static 메소드

Iterable의 기본 메소드

  • forEach()
  • spliterator()

Collection의 기본 메소드

  • stream() / parallelStream()
  • removeIf(Predicate)
  • spliterator()

Comparator의 기본 메소드 및 스태틱 메소드

  • reversed()
  • thenComparing()
  • static reverseOrder() / naturalOrder()
  • static nullsFirst() / nullsLast()
  • static comparing()

Iterable 예시를 보자. 실제 사용하는 람다식도 이 이터레이터 자료구조에서 forEach()을 도출해서 사용하는 경우가 많다.

Iterable<UserEntity> userList = userService.getUserByAll();

List<ResponseUser> result = new ArrayList<>();
        userList.forEach( v -> {
            result.add(new ModelMapper().map(v, ResponseUser.class));	// forEach(), map() 자주 쓰는 방식
     });

자 그럼 람다식을 사용하면서 기본적인 방식을 정리해보자.


람다식의 편리함

기존

for (int i = 0; i < 10; i++) {
    System.out.println(i);
}

람다식

이라면 이렇게 인라인으로 메서드 이름만으로 어떤연산을 하는 것인지 바로 알 수 있게 해준다.
선언식 프로그래밍 방식, 함수형 프로그래밍 방식임

IntStream.range(0, 10).forEach((int value) -> System.out.println(value));

기존

int sum;
int[] arr = {1,2,3,4,5};
sum = Arrays.stream(arr).sum();
System.out.println("합 : " + sum);

람다식

static int sum;
List<Integer> list = Arrays.asList(1,2,3,4,5);
list.stream().forEach((i) -> { sum += i;}); 
System.out.println("합 : " + sum);

https://napasun-programming.tistory.com/32


iterator vs stream

// iterator 구성 방식
Iterator iter = list.iterator();
// stream 구성 방식
Stream stream = list.stream();
// => 완전 똑같다!

iterator hasNext() 표현식

while (iter.hasNext() == true) {
    Object next = iter.next();
    System.out.println("next: "+ next);
}

예제

중급 57)

// foreach 람다 형식을 이용하여 List 출력하기
        
List<Integer> mmlists = Arrays.asList(1,2,3,4,5);
        
	mmlists.stream()    // stream() 안써도 됨
	.forEach(m -> System.out.println(m));

중급 58)

 // for와 if를 이용하여 홀수에 대한 제곱의 합 구하기
 List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9);
 int sum = 0;
        
 for(Integer odd : list) {
     if(odd%2 == 1) {
        sum += odd*odd;
       }
  }
  System.out.println("sum: "+ sum);
}

중급 59)

  // stream을 이용하여 홀수의 합 구하기
  List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        
  int sum = 0;
        
  sum = list.stream()
     .filter(d -> d % 2 == 1)    
     .reduce(0, Integer::sum);
        
  System.out.println(sum);

중급 60)

    // stream과 map을 이용하여 홀수에 대한 제곱의 합 구하기
   List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        
   int sum = 0;
        
   System.out.println(
   list.stream()
     .filter(d -> d%2 == 1)    // filter 가 if 형식으로! list로 반환 vs map가 비교!
     .map(d -> d*d)            // boolean값으로 반환
     .collect(Collectors.toList())
   );
        
   sum = list.stream()
        .filter(d -> d%2 == 1)
        .map(d -> d*d)
        .reduce(0, Integer::sum);    // 앞인자 0을 안넣으면? OptionalInt[sum]으로 됨!
        
   System.out.println("sum: "+ sum);

중금 ##)

List<String> strList = Arrays.asList("kobi", "dsg", "lbk");

long count = strList
.stream()
.filter(x -> x.contains("s"))
.count();

중금 ##)

IntStream intStream = IntStream.rangeClosed(1, 45);

intStream
.filter(i -> i % 2 ==0)
.limit(10)
.forEach(System.out::println);
  • mapToInt : intStream으로 변환 * Stream에서 sum()메서드를 사용할려면 IntStream, LongStream, DoubleStream 와 같은 기본형특화스트림으로 해줘야 한다.

그래서 보통 mapToInt, mapToLong, mapToDouble로 스트림을 변환해주고 사용한다. => sum(), average(), max(), min()
https://bcp0109.tistory.com/299


stream 메서드 정리

List<Integer> inst = Arrays.asList(1,2,3);

// reduce
int sum = inst.stream()
.reduce(0, Integer::sum);

// sum
int sum = inst.stream()
.mapToInt(i -> i)
.sum();

// average
int average = intlist.stream()
.filter(i -> i % 2 == 0)
.mapToInt(i -> i)
.average(); // sum()같은 것

// peek : 슬쩍 연산 그래서 특정 작업을 수행할 뿐 결과에 영향을 안 미친다!
Stream.of(1,2,3,4)
.peek(System.out::println) // 슬쩍 본다.
.reduce(Integer::sum);

// of 
int sum = Stream.of(1, 2, 3)
.mapToInt(i -> i)
.sum();

// mapToInt
List<String> strl = Arrays.asList("dsg1", "kmb2", "tkj3");

strl.stream()
.map(s-> s.substring(3))
.mapToInt(Integer::parseInt)
.forEach(System.out::println);

List<String> strings = Arrays.asList("10 ", "11", "13 ", "15");

strings.stream()
.map(s->s.trim())
.mapToInt(Integer::parseInt)
.forEach(System.out::println);

// flatMap
// 스트림의 각 요소를 "다른 스트림"으로 변환(평면화)
List<String> strings = Arrays.asList("This", "is", "list", "a", "of", "strings");
List<String> collect1 = strings.stream()
           .flatMap(s -> Arrays.stream(s.split("")))
           .collect(Collectors.toList());
           
System.out.println("collect1 = " + collect1);
// collect1 = [T, h, i, s, i, s, l, i, s, t, a, o, f, s, t, r, i, n, g, s]

// flatMapToInt
// 기존 방식 ;; 람다 쓰면 더 편한것!
  List<String> intList = Arrays.asList(" 10", " 2", " 3", " 4");
        
        intList.stream()
        .flatMapToInt(data -> {
            System.out.println("######"+ data);
            String[] strArr = data.split(",");
            int[] intArr = new int[strArr.length];
            for(int i = 0; i<strArr.length; i++) {
                intArr[i] = Integer.parseInt(strArr[i].trim());
            }
            
            return Arrays.stream(intArr);
        })
        .forEach(number -> System.out.println(number));

// Arrays.stream() 
// 연습문제 : 이 텍스트에 "-", " " 를 없애고 int로 parsing하여 합을 구하시오.
String str = "11-9-2-8-48-39- 28-2-82- 94-9-48-2";
str = str.replace(" ", "");

String[] strArr = str.split("-");

int sum = Arrays.stream(strArr)
.sorted()
.mapToInt(Integer::parseInt)
.sum();

System.out.println("sum: "+ sum);

// boxed() : primitive type의 Stream을 reference type의 Stream으로 변환
List<String> intList = Arrays.asList(" 10", " 2", " 3", " 4");

List<Integer> collect3 = intList.stream()
         .map(it -> it.replaceAll(" ", ""))
         .mapToInt(Integer::parseInt)
         .boxed()
         .collect(Collectors.toList());
         
// 매칭(Match)
// allMatch: Stream의 모든 요소가 주어진 Predicate에 일치하는지 확인
var match1 = intlist.stream()
.allMatch(i -> i % 2 == 0);

// anyMatch: Stream의 어떤 요소가 주어진 Predicate에 일치하는지 확인
var match2 = intlist.stream()
.anyMatch(i -> i % 2 == 0);

// noneMatch: Stream의 어떤 요소도 주어진 Predicate에 일치하지 않는지 확인
var match3 = intlist.stream()
.noneMatch(i -> i % 2 == 0);

System.out.println("match1: "+ match1);
System.out.println("match2: "+ match2);
System.out.println("match3: "+ match3);
// 결과
match1: false
match2: true
match3: false

Dto 활용예제

  • 1단계
        List<Subject> subjects = Arrays.asList(
                new Subject("dsg", 40),
                new Subject("math", 60)
        );
        List<Student> students = Arrays.asList(
                new Student("dsg", 20223303),
                new Student("dsg1", 3455323),
                new Student("ss1", 3455323),
                new Student("dsg2", 78990999)
        );
	// filter, map
        List<Student> collect = students.stream()
                .filter(student -> student.getName().contains("dsg"))
                .map(it -> {
                            it.getSubjectList().add(new Subject("math", 100));
                            return it;
                        }
                ).collect(Collectors.toList());
        System.out.println("collect = " + collect);
  • 2단계
// rangeClosed
// mapToObj: mapToObj 메서드는 map 메서드와 비슷하지만, 기존의 primitive 타입을 객체 타입으로 변환할 때 사용
// mapToObj -> int 숫자들을 -> Dto의 필드에 각각 set 변환
List<SampleDTO> memberList = IntStream
.rangeClosed(1, 20)
.asLongStream()
.mapToObj(i -> {
    return SampleDTO.builder()
    	.sno(i)
    	.first("First.." + i)
    	.last("Last.." + i)
    	.regTime(LocalDateTime.now())
    	.build();
}).collect(Collectors.toList());


  • 3단계
		//1) 이중 for문 => 이중 stream
        // 객체 -> Student + Subject
        List<Student> students = Arrays.asList(
                new Student("dsg", 23),
                new Student("ert", 28),
                new Student("hhh", 21),
                new Student("dsg1", 29)
        );

        List<Subject> subjects = Arrays.asList(
                new Subject(1L, "ert", 30),
                new Subject(2L, "ert", 32),
                new Subject(3L, "dsg", 30),
                new Subject(4L, "dsg", 40),
                new Subject(5L, "dsg", 70)
        );
        // forEachx2개 -> 이중 stream : 먼저 하위객체부터
        subjects.stream().forEach(subject -> {
            students.stream().filter(student -> student.getName().equals(subject.getName()))
                    .forEach(student -> student.add(subject));
        });
        System.out.println("Students = " + students); // => json rest api    
        
	//2) 이중 for문 => 이중 stream
    List<Company> companies = new ArrayList<>();
    companies.add(new Company("Company 1"));
    companies.add(new Company("Company 2"));

    List<Employee> employees = new ArrayList<>();
    employees.add(new Employee("John", 30));
    employees.add(new Employee("Jane", 25));
    employees.add(new Employee("Jim", 35));


    employees.stream().filter(employee -> employee.getAge() >= 10).forEach(employee -> {
      companies.forEach(company -> company.getEmployees().add(employee));
    });
    
   // flatMap() : Array나 Object로 감싸져 있는 모든 원소를 단일 원소 스트림으로 반환해준다
   // 이중stream을 위해 먼저 하위부터!
   List<Company> updatedCompanies = employees.stream()
        .flatMap(employee -> companies.stream().filter(company -> company.getName().equals(employee.getName())).map(company -> {
          company.getEmployees().add(employee);
          return company;
        })).distinct().collect(Collectors.toList());



Map 람다 groupingBy()

  • groupingBy() : sql의 groupby랑 비슷
List<Cat> cats = Arrays.asList(
    new Cat("f", "happy"),
    new Cat("f", "sujan"),
    new Cat("m", "pasan")
);

Map<String, List<Cat>> catsex =
    cats.stream()
    .collect(
    Collectors.groupingBy(Cat::getSex)  // Cat sex 기준으로 묶음
);
System.out.println(catsex);


Map<String, List<String>> group =
    cats.stream()
    .collect(
    Collectors.groupingBy(
            Cat::getSex,
            Collectors.mapping(Cat::getName, Collectors.toList())  // Cat sex, name 기준으로 묶음
        )
);
System.out.println(group);

결과값

-> mapping까지 해주는 것이 결과값으로 깔끔!

profile
배운 것을 기록합니다.
post-custom-banner

0개의 댓글