Stream 인터페이스 2

Jan 30, 2024
Stream 인터페이스 2

1. map() 이란?

Stream 인터페이스의 메소드 요소들을 하나씩 가져와 특정 함수를 적용하여(입력) 새로운 값을 반환하는 연산 (가공)
map은 Function 인터페이스를 구현한 객체를 인자로 받는다. *Function 인터페이스는 입력값을 받아서 출력값을 반환하는 apply 메소드를 가지고 있다. map은 이 Function 인터페이스의 apply 메소드를 사용하여 각 요소에 대해 변환 작업을 수행 (apply 메소드는 T 타입의 입력값을 받아서 R 타입의 출력값을 반환 함)

3-1. map() 메소드 설명 및 코드

notion image
map 메서드는 Function 인터페이스를 사용하기 때문에 람다식을 자주 활용한다.
notion image
💡
stream() = 흩뿌려지는 것 = 타입을 벗긴 것 = 현재 타입이 없는 형태
 

[ stream의 forEach ]
notion image
1. newList에 대해 stream() 메서드를 호출하여 다시 스트림으로 변환 2. forEach() 메서드를 사용하여 (i -> System.out.println(i))를 호출하고, 각 요소에 대해 주어진 동작을 수행한다.
notion image
💡
foreach는 Consumer라서 collect를 안한다. (컨슈머는 void) 컨슈머는 매개변수를 받아서 어떤 동작을 수행하고, 결과를 반환하지 않는 함수 forEach() 메서드와 collect() 메서드는 모두 최종 연산이니 당연히 같이 못쓰지 않나?
 
[ foreach를 사용하지 않을 경우 ]
notion image
둘 중 하나를 사용해서 출력
 
코드 펼쳐보기
package ex14.example1; import java.util.Arrays; import java.util.List; import java.util.function.Function; public class CopyEx02 { public static void main(String[] args) { List<Integer> list = Arrays.asList(1,2,3,4); //고정 크기 리스트. 길이 손 안댈것 //원본 값을 유지하면서 새로운 컬렉션을 만들고싶어. //그럼 가공이지? map 쓰겟지? //map List<Integer> newList = list.stream().map(i -> i*100).toList(); //Function 인터페이스를 활용하여 원본 값을 가공하고 복사하는 것이 가능 //Stream의 forEach newList.stream().forEach(i -> System.out.println(i)); } }
 

2. Filter() 이란?

Stream 인터페이스의 메소드 중간 연산 중 하나로, 주어진 조건을 만족하는 요소만을 걸러내는 역할을 한다. (가공)
코드
package ex14.example1; import java.util.Arrays; import java.util.List; public class CopyEx03 { public static void main(String[] args) { List<Integer> list2 = Arrays.asList(1,2,3,4); List<Integer> newList2 = list2.stream().filter(i -> i < 3).toList(); //스트림 타입을 다시 리스트 타입으로 toList()를 이용해 담는다 newList.stream().forEach(integer -> System.out.println(integer)); } }
notion image
1, 2, 3, 4 중 [i < 3] 에 해당되는 요소만 걸러내겠다.
💡
toList() 메소드는 스트림(Stream)을 리스트(List)로 변환하는 메소드
 
[ forEach 없이 출력 ]
notion image
이렇게 컬렉션울 사용 후, 배열의 foreach문을 돌려도 맵과 필터를 출력할 수 있다.
 
코드
public class Test04 { public static void main(String[] args) { List<Integer> list2 = Arrays.asList(1, 2, 3, 4); List<Integer> list3 = new ArrayList<>(); for (Integer i : list2) { if (i < 3) { list3.add(i); System.out.println(i); } }
 

 
💡
Stream은 기본적으로 컬렉션(Collection) 인터페이스를 구현한 객체들에 대해서 지원 (List, Set, Map 등이 대표적인 컬렉션) 일반적으로 사용되는 클래스(개별적인 객체(Object))는 Stream을 직접적으로 사용할 수는 없다. → Stream으로 사용하려면 컬렉션으로 바꿔야 사용 가능.
 

3. 클래스 복사

코드 펼쳐보기

package ex14.example1; class User { private int id; private String name; private String tel; public User(int id, String name, String tel) { this.id = id; this.name = name; this.tel = tel; } public int getId() { return id; } public String getName() { return name; } public String getTel() { return tel; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", tel='" + tel + '\'' + '}'; } } public class CopyEx04 { public static void main(String[] args) { User u1 = new User(1,"ssar","0102222"); //1. 통째로 복사 User u2 = new User(u1.getId(), u1.getName(), u1.getTel()); //2. 부분변경 복사 (tel만 바꿀것) User u3 = new User(u1.getId(), u1.getName(), "0103333"); } }
 

[ 제일 기본적인 방법 ]

1. 통째로 복사 / 부분 변경 복사
notion image
'통째로 복사' 할 때, User u2 = new User(u1.getId(), u1.getName(), u1.getTel()); 이렇게 객체를 생성할 때마다 하나하나 쓰는거 귀찮지?
💡
u1과 u2는 같은 필드 값을 가지고 있는 다른 객체들. 다른 메모리 공간을 참조함
notion image
notion image
User타입을 받는 생성자 하나를 만들어서, 필드값 (id, name, tel)을 user 매개변수.getter로 받게한다. (즉, 객체를 통째로 넘겨서 걔를 꺼내쓰는 것 = 객체를 통으로 넘기는 것)
매개변수로 전달된 user 객체의 필드 값을 가져와서 새로운 객체를 생성하기 위해서 user.getId(); 라고 작성한다. 이 생성자는 객체를 복사하기 위한 용도로 사용되는데, 매개변수로 전달된 user객체의 필드 값을 하나하나 가져와서 현재 객체의 필드에 복사한다. 이 때, user.getId()는 매개변수로 전달된 user 객체의 getId() 메소드를 호출하여 id 필드 값을 가져오는 것. 즉.. 값을 복사하기 위한 생성자
notion image
이렇게 쓸 수 있다! 훤씬 간편! 부분 변경 복사는 어쩔 수없음 ㅠㅠ 그냥 저렇게 써야함
 

 
2. copy 때려서 복사 (잘 사용x) - 메서드 팩토리 패턴
notion image
ㄱcopy 메소드가 static이니까 this 사용 불가. (this는 heap만) 때문에!!
notion image
return 타입을 User로 변경한 후, 아래에 있는 User 생성자를 때리게 함! User 생성자는 User 타입을 매개변수로 받음. 즉, copy메소드가 자기 자신을 때리게 되며, copy가 객체를 복사한거구나! 하면 됨
notion image
요렇게 씀
notion image
notion image
 

4. 문제 - 추후 확인. 여유있을때 문제 풀어보기

d1, d2를 Member에 올릴 것.
회원가입할때 d1, d2같은 데이터가 통신으로 날아오면 이 데이터를 그냥 db에 저장하는게 아니라 Member 클래스에 옮겼다가 저장한다. 그럼 첫번째로 회원가입한 사람(d1)의 데이터는 no가 1번, 두번째 가입(d2)는 no가 2번으로 저장 된다. 사용자한테 이런 no를 기입해서 받을 수 있나? 아니다. 사용자가 지금까지 홈페이지에 총 몇 명이 가입했는지 알 방법이 없다. 즉, 사용자한테 username, password, email은 받아도 no는 못 받는다. no는 우리가 만들어내야 하는 데이터! 그러니까 DB 저장 전, Member 에 들어갔다가 나온다. createdAT은 회원가입한 시간. 즉, createdAT도 개발자가 만들어내야 하는 데이터!
createdAT는 왜 있나? 개인정보법 때문에. 회원가입 후 1년동안 사용하지 않으면 휴먼 계정으로 전환, 5년간 사용 안하면 강제로 정보삭제
notion image
notion image
LocalDateTime 클래스 : os시간을 맞춰준다.
💡
프로그래머들은 ID를 userName이라고 씀!
 
notion image
now.plusDays(1);은 LocalDateTime을 return해서, 값을 바꿔줌. 즉, return LocalDateTime + 1 같은 느낌... (날짜에 +1 해서 나옴) LocalDateTime은 자체 값이 바뀌는게 아님
 

 
💡
알았으면 이제 객체 복사를 해보자 (문제!)
 
코드 정답
package ex14.example1; import java.time.LocalDateTime; class JoinDTO { private String username; //ssar private String password; private String email; public JoinDTO(String username, String password, String email) { this.username = username; this.password = password; this.email = email; } public String getUsername() { return username; } public String getPassword() { return password; } public String getEmail() { return email; } } class Member { private int no; private String username; private String password; private String email; private LocalDateTime createdAt; //날짜를 저장하는 타입!! 생성시간! public Member(int no, JoinDTO dto) { this.no = no; this.username = dto.getUsername(); this.password = dto.getPassword(); this.email = dto.getEmail(); this.createdAt = LocalDateTime.now(); } } public class CopyEx05 { public static void main(String[] args) { //d1, d2를 Member에 올릴 것. JoinDTO d1 = new JoinDTO("ssar", "1234", "ssar@nate.com"); JoinDTO d2 = new JoinDTO("cos", "1234", "cos@nate.com"); //클래스에 있는 데이터를 옮겨담는 것 Member m1 = new Member(1,d1); Member m2 = new Member(2,d2); } }
 
 

 
Share article

codingb