테스트 데이터로 실습
package javabasic.stream;
import static java.util.stream.Collectors.counting;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.summarizingLong;
import static java.util.stream.Collectors.toList;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.LongSummaryStatistics;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.TreeMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class FuncEx01 {
private static final int USER_SIZE = 100;
static <T> void println(T data) {
System.out.println(data);
}
public static void main(String[] args) {
List<User> users = DB.users();
/*
* 실습, 만든 Stream과 실제 java Stream 비교
* 단, 자바 Stream.toList()의 경우 JDK 16부터 추가된 것이라 collect()메서드를 사용했다.
*/
//나이가 30대, 남자, 돈이 250_000원 이상 있는 사람
List<User> list = Stream.stream(users)
.filter(u->u.age-u.age%10 == 30)
.filter(u->"남자".equals(u.gender))
.filter(u->u.money>=250_000)
.toList();
List<User> list2 = users.stream()
.filter(u->u.age-u.age%10 == 30)
.filter(u->"남자".equals(u.gender))
.filter(u->u.money>=250_000)
.collect(toList());
println("비교 결과 = " + list.containsAll(list2)+"\n"+list);
println("\n");
//나이가 30대, 남자, 돈이 250_000원 이상 있는 사람의 돈의 합
Optional<Integer> reduce = Stream.stream(users)
.filter(u->u.age-u.age%10 == 30)
.filter(u->"남자".equals(u.gender))
.map(u->u.money)
.filter(m->m>=250_000)
.reduce(Integer::sum);
Optional<Integer> reduce2 = users.stream()
.filter(u->u.age-u.age%10 == 30)
.filter(u->"남자".equals(u.gender))
.map(u->u.money)
.filter(m->m>=250_000)
.reduce(Integer::sum);
println(reduce+" "+reduce2);
println("비교 결과 = " + reduce.equals(reduce2));
println("\n");
//20대 여자 중에 kakao 이메일을 쓰는 사람
int[] cnt1 = new int[] {0};
List<User> list3 = Stream.stream(users)
.filter(u-> {cnt1[0]++; return u.email.contains("@kakao");} )//분포도가 좋다.
.filter(u-> {cnt1[0]++; return u.age/10 == 2;})
.filter(u-> {cnt1[0]++; return Objects.equals("여자", u.gender);})
.toList();
//스트림 순서도 중요하다. 분포도를 따졌을 때 첫번째 필터 조건에서 최대한 걸러내야
//다음 로직에 넘겨줄 요소가 적기 때문이다.
int[] cnt2 = new int[] {0};
List<User> collect = users.stream()
.filter(u-> {cnt2[0]++; return Objects.equals("여자", u.gender);})//분포도가 안좋다.
.filter(u-> {cnt2[0]++; return u.email.contains("@kakao");} )
.filter(u-> {cnt2[0]++; return u.age/10 == 2;})
.collect(toList());
println("비교 결과 = " + list3.containsAll(collect)+"\n"+list3);
println("cnt 비교 결과 = cnt1("+cnt1[0]+") cnt2("+cnt2[0]+")");
println("\n");
//저 요인 만이 전부는 아니다. 자바 Stream은 지연로딩 기법으로 최종연산 전까진 로직을 수행하지 않는다.
//최종 연산이 호출된 순간 리스트 속 요소 하나하나가 스트림 중간연산을 통과한다.
//모든 요소가 중간연산을 통과하고 그 결과를 다음 중간연산으로 보내는 방식이 아니다.
cnt1[0] = 0;
cnt2[0] = 0;
long count1 = Stream.stream(users)
.map(u->{cnt1[0]++; return u.age;})
.filter(age->{cnt1[0]++; return age>40;})
.count();
long count2 = list.stream()
.map(u->{cnt2[0]++; return u.age;})
.filter(age->{cnt2[0]++; return age>40;})
.count();
println("비교 결과 = " + (count1==count2));
println("내가 만든 stream은 최적화가 전혀 없기 때문에 users 크기 * 2 (map,filter) 만큼 반복될 것이다.");
println(cnt1[0]+" "+cnt2[0]);
println("\n");
//남자, 여자 수
Map<String, Long> countBy = Stream.stream(users)
.countBy(u->u.gender);
Map<String, Long> collect2 = users.stream()
.collect(groupingBy(u->u.gender, counting()));
println("비교 결과 = " + countBy.entrySet().equals(collect2.entrySet()) +"\n"+collect2);
println("\n");
//40대 남자, 30대 여자 유저
Map<String, List<User>> groupBy = Stream.stream(users)
.filter(u-> (u.age/10==3 && Objects.equals("여자", u.gender))
|| u.age/10==4 && Objects.equals("남자", u.gender))
.groupBy(u-> Objects.equals("남자", u.gender)
? "40대 남자" :"30대 여자");
Map<String, List<User>> collect3 = users.stream()
.filter(u-> (u.age/10==3 && Objects.equals("여자", u.gender))
|| u.age/10==4 && Objects.equals("남자", u.gender))
.collect(groupingBy(u-> Objects.equals("남자", u.gender)
? "40대 남자" :"30대 여자"));
println("비교 결과 = " + groupBy.entrySet().equals(collect3.entrySet()) +"\n"+collect3);
println("\n");
//새대별 남,여 자산 정보
//이 정도로 그룹화하는 정보는 현재 구현한 스트림으로 불가
Map<String, Map<Integer, LongSummaryStatistics>> collect4 = users.stream()
.collect((groupingBy(u->u.gender
,groupingBy(u->u.age/10*10, TreeMap::new ,summarizingLong(u->u.money)))));
collect4.forEach((gender,data)->{
println(gender);
data.forEach((age, data2)-> println(age+" "+data2));
});
//메일별, 세대 , 남녀 사용자
TreeMap<String, TreeMap<Integer, Map<String, List<User>>>> collect5 = users.stream()
.collect(groupingBy(u->u.email.substring(u.email.indexOf("@")+1, u.email.lastIndexOf(".")), TreeMap::new ,
groupingBy(u->u.age/10*10,TreeMap::new,
groupingBy(u->u.gender))));
collect5.forEach( (email, data1)->{
println(email);
data1.forEach((age,data2)->{
println(" "+age);
data2.forEach((gender,user)->{
println(" "+gender);
user.forEach(u->println(" "+u));
});
});
});
}
static class StreamUtils{
private StreamUtils() {}
/*
* filter는 처리 결과가 입력 결과와 타입은 같다.
* 길이는 같거나, 작을 수 밖에 없다.
*/
static <T> List<T> filter(List<T> list, Predicate<T> predi) {
//함수형 프로그래밍은 원본 데이터를 수정하지 않는다. 새로운 데이터를 리턴하여
//부수효과를 극단적으로 배제한다.
ArrayList<T> newList = new ArrayList<>();
each(list,data->{
if(predi.test(data))
newList.add(data);
});
return newList;
}
/*
* filter 정반대, 주어진 조건에 해당하지 않은 값만 걸러낸다.
*/
static <T> List<T> reject(List<T> list, Predicate<T> predi){
return filter(list, predi.negate());
}
/*
* map은 처리 결과가 입력 결과와 타입은 같거나 다르다.
* 길이는 항상 같다.
*/
static <T,R> List<R> map(List<T> list, Function<T, R> mapper){
ArrayList<R> newList = new ArrayList<>();
each(list, data->newList.add(mapper.apply(data)));
return newList;
}
/*
* 반복문을 중복을 대체할 함수
*/
static <T> List<T> each(List<T> list, Consumer<T> iter){
for(T data : list)
iter.accept(data);
return list;
}
/*
* 자바는 스트림 안에서 외부 변수를 참조하면 그 변수는 final 속성이 된다.
* 따라서 갱신하면서 누산을 하지 못해, 간접적으로 값을 수정해야 한다.
*/
static <T> T reduce(List<T> list, BinaryOperator<T> reducer ,T memo) {
//간소화된 유효성 검사, 본질을 흐리지 않는 선에서 간략화
if(memo == null ) return reduce(list.subList(1, list.size()), reducer, list.get(0));
if(list.size() < 2) return list.get(0);
HashMap<Class<?>, T> map = new HashMap<>();
map.put(memo.getClass(), memo);
each(list, data->map.compute(memo.getClass(), (k,v)->reducer.apply(v, data)));
return map.get(memo.getClass());
}
/*
* 자바는 람다에서 사용하는 변수는 final 속성을 띄기 때문에 memo 값 처리가 애매해진다.
* 삼항연산자 등 별도 처리하기보다 오버로딩으로 처리했다.
*/
static <T> T reduce(List<T> list, BinaryOperator<T> reducer) {
if(list.size() < 2) return list.get(0);
return reduce(list.subList(1, list.size()), reducer, list.get(0));
}
/*
* reduce 특화 함수
*/
static <T> T min(List<T> list, Comparator<T> comparator) {
return reduce(list, BinaryOperator.minBy(comparator));
}
static <T> T max(List<T> list, Comparator<T> comparator) {
return reduce(list, BinaryOperator.maxBy(comparator));
}
//주어진 조건으로 검사한 결과로 최대 최소 값을 판단한다.
static <T,R> T minBy(List<T> list,Comparator<R> comparator ,Function<T, R> mapper) {
return reduce(list, (a,b)-> comparator.compare(mapper.apply(a), mapper.apply(b)) > 0 ? b:a);
}
static <T,R> T maxBy(List<T> list,Comparator<R> comparator ,Function<T, R> mapper) {
return minBy(list,comparator.reversed(),mapper);
}
/*
* 그룹 방법 2, 리스트의 요소를 하나로 축약한 다는 점에서 groupBy는 reduce의 특화 메서드여야 한다.
* 작성은 까다롭고 가시성이 안좋지만, 어차피 사용자입장에선 방법1과 똑같은 방식으로 호출한다.
*/
static <T,R> Map<R,List<T>> groupBy(List<T> list, Function<T,R> mapper){
BiFunction<Map<R,List<T>> , T, Map<R,List<T>> > bi = (group, val) -> {
group.compute(mapper.apply(val), (k,v)->{
if(v == null) v = new ArrayList<>();
v.add(val);
return v;
});
return group;
};
return reduce(list, bi, new HashMap<R, List<T>>());
}
//groupBy,countBy 용 reduce 오버로딩
static <T,R> R reduce(List<T> list, BiFunction<R ,T, R> reducer, R memo) {
each(list, val-> reducer.apply(memo, val));
return memo;
}
/*
* 기본 groupBy로직을 재사용했다.
* 만약 groupBy로직이 변경되면 똑같이 적용받는다.
*/
static <T,R> Map<R,Long> countBy(List<T> list, Function<T,R> mapper){
Map<R, Long> countBy = new HashMap<>();
for(Entry<R, List<T>> entry:groupBy(list,mapper).entrySet())
countBy.put(entry.getKey(), Long.valueOf(entry.getValue().size()));
return countBy;
}
static <T> Optional<T> find(List<T> list, Predicate<T> predi) {
for(int i=0;i<list.size();i++) {
T value = list.get(i);
if(predi.test(value)) return Optional.of(value);
}
return Optional.empty();//편의상 null리턴
}
static <T> Integer findIndex(List<T> list, Predicate<T> predi) {
for(int i=0;i<list.size();i++) {
if(predi.test(list.get(i))) return i;
}
return -1;
}
static <T> Boolean some(List<T> list, Predicate<T> predi) {
return findIndex(list, predi) != -1;
}
static <T> Boolean every(List<T> list, Predicate<T> predi) {
return findIndex(list, predi.negate()) == -1;
}
static <T> Boolean contains(List<T> list, T data){
return findIndex(list, val-> Objects.equals(val, data) ) != -1 ;
}
static <T,R> List<R> pluck(List<T> list, String key,Class<R> typeToken){
List<R> result = map(list, val-> pluckHelper(val, key, typeToken));
return some(result, Objects::isNull).booleanValue() ? Collections.emptyList() :result;
}
//런타임 시 타입이 확정되는 경우 어쩔 수 없이 Object를 리턴해야 한다.
static <T,R> R pluckHelper(T val,String key,Class<R> typeToken) {
try {
Field field = val.getClass().getDeclaredField(key);
return typeToken.cast(field.get(val));
} catch (Exception e) {
return null;
}
}
}
/*
* pipe, go 구현을 시도하려 했지만, 근본적으로 자바는 함수가 개념이 없어
* 호출부를 단일로 추상화할 수 없다. apply, test ...
* 따라서 하나의 클래스로 묶었다.
*/
static class Stream<T> {
final List<T> list;
private Stream(List<T> list) {
this.list = list;
}
static <T> Stream<T> stream(List<T> list){
return new Stream<>(list);
}
Stream<T> filter(Predicate<T> predi) {
//함수형 프로그래밍은 원본 데이터를 수정하지 않는다. 새로운 데이터를 리턴하여
//부수효과를 극단적으로 배제한다.
ArrayList<T> newList = new ArrayList<>();
forEach(data->{
if(predi.test(data))
newList.add(data);
});
return stream(newList);
}
Stream<T> reject(Predicate<T> predi){
return filter(predi.negate());
}
<R> Stream<R> map(Function<T, R> mapper){
ArrayList<R> newList = new ArrayList<>();
forEach(data->newList.add(mapper.apply(data)));
return stream(newList);
}
void forEach(Consumer<T> iter){
for(T data : list) iter.accept(data);
}
Optional<T> reduce(BinaryOperator<T> reducer ,T memo) {
if(memo == null) return reduce(reducer);
if(this.list.size() < 2) return Optional.of(this.list.get(0));
//람다에서 참조하는 변수는 final 특성을 부여받는다. 따라서 간접적으로 값을 갱신하기 위해 Map을 활용
HashMap<String, T> reduceStore = new HashMap<>();
String accumulation = "누산용임시키";
reduceStore.put(accumulation, memo);
forEach(data->reduceStore.compute(accumulation, (k,v)-> reducer.apply(v, data)));
return Optional.of(reduceStore.get(accumulation));
}
Optional<T> reduce(BinaryOperator<T> reducer) {
T tmpValue = this.list.get(0);
if(this.list.size() < 2) return Optional.of(tmpValue);
List<T> subList = this.list.subList(1, list.size());
return Stream.stream(subList).reduce(reducer, tmpValue);
}
//groupBy,countBy 용 reduce 오버로딩
<R> R reduce(BiFunction<R ,T, R> reducer, R memo) {
forEach(val-> reducer.apply(memo, val));
return memo;
}
/*
* reduce 특화 함수
*/
Optional<T> min(Comparator<? super T> comparator) {
return reduce(BinaryOperator.<T>minBy(comparator));
}
Optional<T> max(Comparator<? super T> comparator) {
return reduce(BinaryOperator.<T>maxBy(comparator));
}
//주어진 조건으로 검사한 결과로 최대 최소 값을 판단한다.
<R> Optional<T> minBy(Comparator<? super R> comparator ,Function<T, R> mapper) {
return reduce( (a,b)-> comparator.compare(mapper.apply(a), mapper.apply(b)) > 0 ? b:a);
}
<R> Optional<T> maxBy(Comparator<? super R> comparator ,Function<T, R> mapper) {
return minBy(comparator.reversed(),mapper);
}
/*
* 주어진 조건에 따라 그룹화
*/
<R> Map<R,List<T>> groupBy(Function<T,R> mapper){
BiFunction<Map<R,List<T>> , T, Map<R,List<T>> > bi = (group, val) -> {
group.compute(mapper.apply(val), (k,v)->{
if(v == null) v = new ArrayList<>();
v.add(val);
return v;
});
return group;
};
return reduce(bi, new HashMap<R, List<T>>());
}
/*
* 기본 groupBy로직을 재사용했다.
* 만약 groupBy로직이 변경되면 똑같이 적용받는다.
*/
<R> Map<R,Long> countBy(Function<T,R> mapper){
Map<R, Long> countBy = new HashMap<>();
for(Entry<R, List<T>> entry:groupBy(mapper).entrySet())
countBy.put(entry.getKey(), Long.valueOf(entry.getValue().size()));
return countBy;
}
Optional<T> find(Predicate<T> predi) {
for(int i=0;i<this.list.size();i++) {
T value = this.list.get(i);
if(predi.test(value)) return Optional.of(value);
}
return Optional.empty();//편의상 null리턴
}
Integer findIndex(Predicate<T> predi) {
for(int i=0;i<this.list.size();i++)
if(predi.test(this.list.get(i))) return i;
return -1;
}
Boolean some(Predicate<T> predi) {
return findIndex(predi) != -1;
}
Boolean every(Predicate<T> predi) {
return findIndex(predi.negate()) == -1;
}
<R> List<R> pluck(String key,Class<R> typeToken){
Stream<R> result = map(val-> pluckHelper(val, key, typeToken));
return result.some(Objects::isNull).booleanValue() ?
Collections.emptyList() :result.toList();
}
//런타임 시 타입이 확정되는 경우 어쩔 수 없이 Object를 리턴해야 한다.
<R> R pluckHelper(T val,String key,Class<R> typeToken) {
try {
Field field = val.getClass().getDeclaredField(key);
return typeToken.cast(field.get(val));
} catch (Exception e) {
return null;
}
}
List<T> toList(){
//방어적 복사
return new ArrayList<>(this.list);
}
long count() {
return this.list.size();
}
}
static class User{
private static long serial;
final long id;
final String name;
final int age;
final String gender;
final int money;
final String email;
public User(String name, int age, String gender, int money, String email) {
id = ++serial;
this.name = name;
this.age = age;
this.gender = gender;
this.money = money;
this.email = email;
}
public String toString() {
return "[id=" + id + ", name=" + name + ", age=" + age + ", gender=" + gender + ", money=" + money
+ ", email=" + email + "]\n";
}
}
static class DB{
static String[] nameDB = {
"남재인", "한재인", "신이경", "오유리", "장경완", "봉숙자", "황여진", "심재인", "복미래", "신여진",
"배미란", "배영신", "이애정", "송여진", "남영애", "안미란", "문재규", "홍숙자", "장경님", "양경님",
"양장미", "추자경", "백장미", "권민서", "전재규", "윤재규", "전지해", "설재신", "배경완", "황보혜린",
"정무열", "조경구", "남성한", "조경모", "남치원", "유병헌", "최성한", "윤남규", "문성한", "강승헌",
"백성한", "표경구", "조치원", "오재섭", "하경구", "정요한", "송광조", "백재범", "안남규", "배덕수",
"노광조", "복일성", "안재범", "임경구", "유무영", "남궁덕수", "하치원", "하동건", "유무영", "유무영",
"성란", "임재", "심진", "설린", "강현", "전진", "이성", "사공리", "탁재", "복상", "하진", "이상",
"심리", "송설", "조진", "문성", "문지", "임린", "예현", "손진", "유리", "전현", "유재", "배은",
"예상", "황성", "임진", "심재", "백현", "한리", "남훈", "심광", "예철", "정건", "하웅", "권훈",
"성훈", "한훈", "하훈", "심광", "성호", "봉건", "류혁", "노훈", "서훈", "권훈", "윤호", "송건",
"윤광", "하웅", "노호", "이철", "문혁", "추철", "정광", "한호", "예철", "오훈", "사공웅", "고훈",
"제갈다솜", "하햇살", "양보다", "정자람", "전우리", "노보람", "최한울", "봉빛나", "장비", "전누리",
"전두리", "전마음", "예한별", "김은별", "김민들레", "홍자람", "안꽃", "전나빛", "안아름", "고보름",
"백나라", "전아람", "설빛나", "강나비", "문샛별", "유새벽", "성여름", "남다솜", "양하다", "권하루",
"손우람", "허버들", "봉미르", "남궁나라우람", "노힘찬", "황보달", "류빛가람", "윤나라우람", "유미르",
"황보힘찬", "이믿음", "남궁샘", "남한길", "황보나길", "한한길", "전나라우람", "최한길", "권한길", "임믿음",
"고한결", "설믿음", "황샘", "표나길", "안달", "양샘", "임달", "황빛가람", "홍한길", "제갈한결", "배버들",
"강광일", "송세은", "문준웅", "백은선", "설서우", "강경희", "권윤미", "봉재경", "표수민", "조동근",
"추진석", "황민숙", "남원석", "심시준", "이선우", "조정우", "유태일", "추경윤", "권규환", "임은주",
"표연웅", "류창기", "풍병수", "서인숙", "남궁명욱", "박시현", "전창현", "남궁주원", "이우태", "사공혜윤"
};
static String[] mailDB = {
"@naver.com", "@nate.com" , "@daum.net" , "@kakao.com" , "@gmail.com" , "@outlook.com",
"@hotmail.com" , "@yahoo.com"
};
static String[] gender = {
"남자", "여자"
};
static ThreadLocalRandom ran = ThreadLocalRandom.current();
static List<User> users() {
return IntStream.range(0, USER_SIZE)
.mapToObj(n-> getUser())
.collect(Collectors.toList());
}
static User getUser() {
return new User(getUsername(), getAge(), getGender(), getMoney(), getEmail());
}
static String getUsername() {
return nameDB[ran.nextInt(nameDB.length)];
}
static String getEmail() {
StringBuilder sb = new StringBuilder();
for(int i=0,len=ran.nextInt(5, 15);i<len;i++)
sb.append(getWord());
sb.append(mailDB[ran.nextInt(mailDB.length)]);
return sb.toString();
}
static char getWord() {
return (char)ran.nextInt(97, 122+1);
}
static String getGender() {
return gender[ran.nextInt(gender.length)];
}
static int getAge() {
return ran.nextInt(10, 80);
}
static int getMoney() {
return ran.nextInt(10_000, 1_000_000);
}
}
}
비교 결과 = true
[[id=15, name=표경구, age=33, gender=남자, money=314489, email=gtmfnbdzobddnp@gmail.com]
, [id=41, name=송건, age=35, gender=남자, money=290228, email=udrsmmbey@outlook.com]
, [id=45, name=임진, age=39, gender=남자, money=444444, email=sdxmigxegegcf@daum.net]
]
Optional[1049161] Optional[1049161]
비교 결과 = true
비교 결과 = true
[[id=96, name=예현, age=26, gender=여자, money=21656, email=tjczaz@kakao.com]
]
cnt 비교 결과 = cnt1(109) cnt2(158)
비교 결과 = false
내가 만든 stream은 최적화가 전혀 없기 때문에 users 크기 * 2 (map,filter) 만큼 반복될 것이다.
200 6
비교 결과 = true
{여자=53, 남자=47}
비교 결과 = true
{40대 남자=[[id=23, name=이선우, age=44, gender=남자, money=468012, email=fggpaencwz@outlook.com]
], 30대 여자=[[id=3, name=남치원, age=33, gender=여자, money=333145, email=vndezofeom@daum.net]
, [id=29, name=손우람, age=32, gender=여자, money=728586, email=oyidwlrfsg@daum.net]
, [id=33, name=남궁나라우람, age=36, gender=여자, money=650486, email=mnpsm@kakao.com]
, [id=75, name=강현, age=38, gender=여자, money=403083, email=kawgqtx@daum.net]
]}
여자
10 LongSummaryStatistics{count=12, sum=5059276, min=60107, average=421606.333333, max=988529}
20 LongSummaryStatistics{count=6, sum=2765477, min=21656, average=460912.833333, max=934750}
30 LongSummaryStatistics{count=4, sum=2115300, min=333145, average=528825.000000, max=728586}
40 LongSummaryStatistics{count=8, sum=3903000, min=152607, average=487875.000000, max=865687}
50 LongSummaryStatistics{count=3, sum=2001864, min=133476, average=667288.000000, max=962910}
60 LongSummaryStatistics{count=12, sum=5947893, min=33604, average=495657.750000, max=994724}
70 LongSummaryStatistics{count=8, sum=3752254, min=104377, average=469031.750000, max=835425}
남자
10 LongSummaryStatistics{count=9, sum=4812207, min=77748, average=534689.666667, max=949794}
20 LongSummaryStatistics{count=9, sum=4359975, min=10394, average=484441.666667, max=877966}
30 LongSummaryStatistics{count=5, sum=1368168, min=154407, average=273633.600000, max=444444}
40 LongSummaryStatistics{count=1, sum=468012, min=468012, average=468012.000000, max=468012}
50 LongSummaryStatistics{count=8, sum=2717464, min=22002, average=339683.000000, max=827747}
60 LongSummaryStatistics{count=7, sum=3496732, min=91445, average=499533.142857, max=999230}
70 LongSummaryStatistics{count=8, sum=3086908, min=220017, average=385863.500000, max=638264}
daum
10
여자
[id=26, name=전지해, age=14, gender=여자, money=903863, email=cafnlba@daum.net]
[id=42, name=설빛나, age=16, gender=여자, money=120450, email=xpeuzh@daum.net]
[id=51, name=안재범, age=19, gender=여자, money=60107, email=bpmpwyjbylht@daum.net]
남자
[id=1, name=임린, age=11, gender=남자, money=704492, email=cwhbycqugqgczp@daum.net]
[id=10, name=풍병수, age=17, gender=남자, money=310580, email=wjlkup@daum.net]
[id=85, name=남한길, age=12, gender=남자, money=77748, email=rghcntv@daum.net]
20
여자
[id=20, name=전진, age=28, gender=여자, money=351280, email=guslzndju@daum.net]
30
여자
[id=3, name=남치원, age=33, gender=여자, money=333145, email=vndezofeom@daum.net]
[id=29, name=손우람, age=32, gender=여자, money=728586, email=oyidwlrfsg@daum.net]
[id=75, name=강현, age=38, gender=여자, money=403083, email=kawgqtx@daum.net]
남자
[id=45, name=임진, age=39, gender=남자, money=444444, email=sdxmigxegegcf@daum.net]
50
남자
[id=9, name=임은주, age=52, gender=남자, money=562328, email=gmnpagm@daum.net]
60
여자
[id=86, name=문성, age=62, gender=여자, money=434243, email=nbnhnr@daum.net]
남자
[id=35, name=예상, age=60, gender=남자, money=478077, email=xlgttj@daum.net]
70
여자
[id=80, name=권민서, age=73, gender=여자, money=104377, email=bxdkw@daum.net]
남자
[id=11, name=문샛별, age=79, gender=남자, money=462553, email=lstuztooumdut@daum.net]
gmail
10
여자
[id=12, name=장경님, age=12, gender=여자, money=988529, email=tyzizhpapm@gmail.com]
[id=28, name=추자경, age=18, gender=여자, money=549524, email=lmjwbxek@gmail.com]
[id=62, name=권민서, age=17, gender=여자, money=141048, email=zrmkngqsg@gmail.com]
[id=73, name=문준웅, age=18, gender=여자, money=241932, email=ctnqmeyhnirka@gmail.com]
남자
[id=61, name=한호, age=11, gender=남자, money=939524, email=iuhaczelpkrr@gmail.com]
[id=83, name=예한별, age=14, gender=남자, money=949794, email=upidffv@gmail.com]
20
여자
[id=99, name=안재범, age=25, gender=여자, money=94538, email=qssefp@gmail.com]
남자
[id=17, name=전지해, age=20, gender=남자, money=598154, email=uoxuvjvffc@gmail.com]
30
남자
[id=15, name=표경구, age=33, gender=남자, money=314489, email=gtmfnbdzobddnp@gmail.com]
50
여자
[id=54, name=전누리, age=57, gender=여자, money=133476, email=ztaybdb@gmail.com]
남자
[id=8, name=조진, age=53, gender=남자, money=37517, email=dfjxs@gmail.com]
hotmail
10
여자
[id=46, name=오유리, age=13, gender=여자, money=241904, email=xxpbplynagby@hotmail.com]
남자
[id=100, name=심시준, age=13, gender=남자, money=573493, email=xbmkixzungwtk@hotmail.com]
20
남자
[id=37, name=이애정, age=29, gender=남자, money=712619, email=fwmqlppeaqonlx@hotmail.com]
[id=76, name=추경윤, age=24, gender=남자, money=10394, email=kwlczlnfmacbpx@hotmail.com]
30
남자
[id=88, name=정무열, age=37, gender=남자, money=164600, email=ozkspnsnjqbone@hotmail.com]
40
여자
[id=7, name=노광조, age=42, gender=여자, money=633359, email=bsflfhq@hotmail.com]
[id=14, name=양샘, age=47, gender=여자, money=637883, email=rqwrb@hotmail.com]
[id=48, name=하웅, age=48, gender=여자, money=181082, email=wafjklvk@hotmail.com]
50
여자
[id=39, name=양하다, age=51, gender=여자, money=962910, email=liwtgqiyxriq@hotmail.com]
남자
[id=89, name=윤나라우람, age=54, gender=남자, money=22002, email=xwmnt@hotmail.com]
60
남자
[id=19, name=고보름, age=63, gender=남자, money=669109, email=vpoqukkrnkk@hotmail.com]
[id=44, name=봉재경, age=64, gender=남자, money=999230, email=gjpxo@hotmail.com]
[id=57, name=오재섭, age=66, gender=남자, money=599934, email=vgyvflqlxlt@hotmail.com]
70
여자
[id=93, name=남성한, age=72, gender=여자, money=533141, email=czgpofbrg@hotmail.com]
남자
[id=58, name=권하루, age=73, gender=남자, money=324715, email=vcwuumugk@hotmail.com]
kakao
10
여자
[id=71, name=서인숙, age=14, gender=여자, money=272687, email=xomdc@kakao.com]
[id=98, name=김민들레, age=18, gender=여자, money=471026, email=nupqlxvcah@kakao.com]
남자
[id=24, name=사공리, age=16, gender=남자, money=570165, email=ztsep@kakao.com]
[id=91, name=유무영, age=16, gender=남자, money=471883, email=eaumdfxbca@kakao.com]
20
여자
[id=96, name=예현, age=26, gender=여자, money=21656, email=tjczaz@kakao.com]
30
여자
[id=33, name=남궁나라우람, age=36, gender=여자, money=650486, email=mnpsm@kakao.com]
50
여자
[id=55, name=전아람, age=52, gender=여자, money=905478, email=gsreid@kakao.com]
남자
[id=60, name=오재섭, age=57, gender=남자, money=827747, email=mefrk@kakao.com]
nate
20
여자
[id=34, name=노광조, age=21, gender=여자, money=788194, email=vmqdlkrtd@nate.com]
30
남자
[id=72, name=강광일, age=36, gender=남자, money=154407, email=pkmufvpouvnyre@nate.com]
40
여자
[id=25, name=예철, age=49, gender=여자, money=405311, email=gbmzvm@nate.com]
50
남자
[id=21, name=류창기, age=58, gender=남자, money=33594, email=eeuct@nate.com]
60
여자
[id=22, name=배덕수, age=67, gender=여자, money=65668, email=latpigtfm@nate.com]
[id=64, name=남치원, age=64, gender=여자, money=153221, email=fmrcmnyoarw@nate.com]
[id=84, name=송광조, age=66, gender=여자, money=838967, email=rqfrl@nate.com]
70
남자
[id=43, name=유무영, age=78, gender=남자, money=220017, email=wctljzwkh@nate.com]
naver
10
남자
[id=97, name=문재규, age=19, gender=남자, money=214528, email=vvyjgpk@naver.com]
20
여자
[id=67, name=임재, age=21, gender=여자, money=934750, email=ynnmctxyyhcye@naver.com]
남자
[id=32, name=남영애, age=28, gender=남자, money=43114, email=zpqfmykq@naver.com]
[id=56, name=안미란, age=25, gender=남자, money=514538, email=nqtlblqmhiic@naver.com]
40
여자
[id=2, name=문준웅, age=43, gender=여자, money=865687, email=ztdcawg@naver.com]
[id=81, name=백장미, age=42, gender=여자, money=152607, email=effcvgwmbm@naver.com]
50
남자
[id=31, name=남성한, age=53, gender=남자, money=591379, email=zzzuxykkl@naver.com]
60
여자
[id=6, name=조경구, age=68, gender=여자, money=871043, email=xlcrpcyvmimdm@naver.com]
[id=38, name=홍숙자, age=67, gender=여자, money=499318, email=ztvnsysg@naver.com]
[id=40, name=전우리, age=66, gender=여자, money=33604, email=agntgnhynsto@naver.com]
[id=66, name=백장미, age=65, gender=여자, money=803355, email=hmiorgusuvr@naver.com]
[id=77, name=이상, age=64, gender=여자, money=579251, email=iujjz@naver.com]
남자
[id=68, name=이믿음, age=64, gender=남자, money=216739, email=lwkvuffwd@naver.com]
[id=95, name=권한길, age=63, gender=남자, money=91445, email=cmwjs@naver.com]
70
여자
[id=4, name=표수민, age=77, gender=여자, money=649938, email=zvcpwjw@naver.com]
남자
[id=30, name=유태일, age=70, gender=남자, money=518323, email=ctwythbsezz@naver.com]
[id=47, name=강현, age=78, gender=남자, money=347609, email=qkjjteznfp@naver.com]
outlook
20
남자
[id=13, name=남치원, age=21, gender=남자, money=877966, email=vtfwmg@outlook.com]
[id=70, name=백장미, age=25, gender=남자, money=320175, email=ifziofwkklcm@outlook.com]
[id=92, name=문성한, age=20, gender=남자, money=837607, email=egtuakkqsopl@outlook.com]
30
남자
[id=41, name=송건, age=35, gender=남자, money=290228, email=udrsmmbey@outlook.com]
40
여자
[id=50, name=양보다, age=46, gender=여자, money=780840, email=lmicguahzefqq@outlook.com]
남자
[id=23, name=이선우, age=44, gender=남자, money=468012, email=fggpaencwz@outlook.com]
60
남자
[id=16, name=복미래, age=65, gender=남자, money=442198, email=fdbgaxjheqzxed@outlook.com]
70
여자
[id=82, name=강승헌, age=75, gender=여자, money=141987, email=fnyblldow@outlook.com]
남자
[id=65, name=안남규, age=73, gender=남자, money=339445, email=psnsnvg@outlook.com]
yahoo
10
여자
[id=74, name=강현, age=15, gender=여자, money=926397, email=eqtpd@yahoo.com]
[id=78, name=남다솜, age=15, gender=여자, money=141809, email=olqanipipcy@yahoo.com]
20
여자
[id=53, name=심진, age=25, gender=여자, money=575059, email=lencs@yahoo.com]
남자
[id=79, name=설믿음, age=21, gender=남자, money=445408, email=fnbdifxyqw@yahoo.com]
40
여자
[id=27, name=조정우, age=44, gender=여자, money=246231, email=puadexzcwxx@yahoo.com]
50
남자
[id=69, name=홍자람, age=57, gender=남자, money=475667, email=ekfqygfgoqabk@yahoo.com]
[id=90, name=배미란, age=57, gender=남자, money=167230, email=ulxbuuowodj@yahoo.com]
60
여자
[id=18, name=설재신, age=62, gender=여자, money=102531, email=xvjamjdl@yahoo.com]
[id=36, name=황성, age=61, gender=여자, money=994724, email=fgojm@yahoo.com]
[id=87, name=이우태, age=64, gender=여자, money=571968, email=eqxhgmszafh@yahoo.com]
70
여자
[id=5, name=백은선, age=76, gender=여자, money=835425, email=zefprlxtvt@yahoo.com]
[id=49, name=권한길, age=77, gender=여자, money=607624, email=eyjzuv@yahoo.com]
[id=59, name=남궁주원, age=70, gender=여자, money=717484, email=jlktqhy@yahoo.com]
[id=94, name=배영신, age=70, gender=여자, money=162278, email=shlkvyegelwfv@yahoo.com]
남자
[id=52, name=유미르, age=74, gender=남자, money=235982, email=fhfbeozpkozxy@yahoo.com]
[id=63, name=남영애, age=74, gender=남자, money=638264, email=rkntpvsavuzl@yahoo.com]
groupBy 위주로 실습
매 실행마다 테스트 데이터가 달라지므로 위 결과와 다를 것
'개발 > 함수형 프로그래밍' 카테고리의 다른 글
자바스크립트 함수형 코드 자바화 해보기 - groupBy, countBy (0) | 2023.06.27 |
---|---|
자바스크립트 함수형 코드 자바화 해보기 - min, max, minBy, maxBy (0) | 2023.06.26 |
자바스크립트 함수형 코드 자바화 해보기 - pluck, reject (0) | 2023.06.25 |
자바스크립트 함수형 코드 자바화 해보기 - find, findIndex, some, every (0) | 2023.06.24 |
자바스크립트 함수형 코드 자바화 해보기 - pipe, go 를 대체할 Stream 클래스 정의 (0) | 2023.06.23 |