45일차 (3) - java (filter, map, stream)

Yohan·2024년 4월 24일
0

코딩기록

목록 보기
67/156
post-custom-banner

객체 데이터 변환 및 필터링

  • Map과 filter는 데이터를 변환하고 필터링하는 데 사용되는 메서드이다.
  • 컬렉션(배열, 리스트 등)의 요소를 처리하고 변환하는 데 유용하다.

filter

/**
     * @solution - try4: 여러 객체들을 필터링
     * @param basket
     * @param p
     * @return
     */
    public static <T> List<T> filter(List<T> basket, GenericPredicate<T> p) {

        List<T> filteredList = new ArrayList<>();

        for (T t : basket) {
            if (p.test(t)) {
                filteredList.add(t);
            }
        }
        return filteredList;
    }
    
    
    // GenericPredicate
    public interface GenericPredicate<T> {

    boolean test(T t);
}


	// main
    // 사과 바구니 생성
        List<Apple> appleBasket = List.of(
                new Apple(80, GREEN)
                , new Apple(155, GREEN)
                , new Apple(120, RED)
                , new Apple(97, RED)
                , new Apple(200, GREEN)
                , new Apple(50, RED)
                , new Apple(85, YELLOW)
                , new Apple(75, YELLOW)
        );
        
      
      	System.out.println("========= 녹색 사과 필터링 ==========");
        /* List<Apple> apple5 = filter(appleBasket, new GenericPredicate<Apple>() {
            @Override
            public boolean test(Apple apple) {
                return apple.getColor() == GREEN;
            }
        });
        */
        // 위와 같음
        List<Apple> apple5 = filter(appleBasket, apple -> apple.getColor() == GREEN);
        System.out.println("apple5 = " + apple5);

        List<Integer> numbers = List.of(1,2,3,4,5,6,7,8,9);
        System.out.println("========== 짝수만 필터링 =========");
        List<Integer> even = filter(numbers, n -> n % 2 == 0);
        System.out.println("even = " + even);
  • 위는 filter의 구성이다. 내가 원하는 조건으로 필터링하여 List배열을 리턴해준다.
  • <T> 에는 내가 필터링하고 싶은 객체가 들어간다.
  • GenericPredicate은 인터페이스로 test메서드를 추상메서드로 가진다. test를 원하는대로 오버라이딩해서 조건을 만들어낸다.
  • 조건을 람다로 나타내면 간단하게 나타낼 수 있다.

Map

	// X객체 리스트에서 Y객체 리스트를 반환
    public static <X, Y> List<Y> map(List<X> xList, GenericFuncction<X,Y> f) {

        List<Y> mappedList = new ArrayList<>();

        for (X x : xList) {
            Y y = f.apply(x);
            mappedList.add(y);
        }
        return mappedList;
    }
    
    
    // GenericFuncction
    @FunctionalInterface
public interface GenericFuncction<X,Y> {

    // X를 주면 Y를 추출해줄게
    Y apply(X x);
    
    
    // main
    System.out.println("======== 사과의 무게만 추출 =========");
        List<Integer> weightList = MappingApple.map(appleBasket, apple -> apple.getWeight());
        System.out.println("weightList = " + weightList);

        System.out.println("==== 숫자리스트에서 각 숫자들의 제곱읍 추출");
        List<Integer> pows = MappingApple.map(numbers, n -> n * n);
        System.out.println("pows = " + pows);
}
  • Map의 포인트는 파라미터의 객체와 반환 객체가 다르다는 것이다. Mapping을 하게되면 내용이 달라지는 것이기 때문에 다른 객체를 반환한다.
    -> 예를 들어 사과의 색상만 추출한다고 했을 때 X는 Apple 객체, Y는 Color 객체
  • GenericFuncction 인터페이스에 apply 메서드를 추상메서드로 등록하고 오버라이딩해서 구현한다. (람다로 표현했다)

Filtering 과 Mapping

  • 위에서는 filter와 map의 구조를 파악해보았다.
  • 여기서는 실질적으로 filter와 map이 어떻게 쓰이는지 예시를 알아본다.
  • Menu
public class Menu {

    public static final List<Dish> menuList;

    static {
        menuList = List.of(
                new Dish("pork", false, 800, Dish.Type.MEAT),
                new Dish("beef", false, 700, Dish.Type.MEAT),
                new Dish("chicken", false, 400, Dish.Type.MEAT),
                new Dish("french fries", true, 530, Dish.Type.OTHER),
                new Dish("rice", true, 350, Dish.Type.OTHER),
                new Dish("season fruit", true, 120, Dish.Type.OTHER),
                new Dish("pizza", true, 550, Dish.Type.OTHER),
                new Dish("prawns", false, 400, Dish.Type.FISH),
                new Dish("salmon", false, 450, Dish.Type.FISH)
        );
    }

}
  • Dish
public class Dish {

    private final String name; // 요리 이름
    private final boolean vegeterian; // 채식주의 여부
    private final int calories; // 칼로리
    private final Type type; // 요리 카테고리

    public enum Type {
        MEAT, FISH, OTHER
    }

    public Dish(String name, boolean vegeterian, int calories, Type type) {
        this.name = name;
        this.vegeterian = vegeterian;
        this.calories = calories;
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public boolean isVegeterian() {
        return vegeterian;
    }

    public int getCalories() {
        return calories;
    }

    public Type getType() {
        return type;
    }

    @Override
    public String toString() {
        return "Dish{" +
                "name='" + name + '\'' +
                ", vegeterian=" + vegeterian +
                ", calories=" + calories +
                ", type=" + type +
                '}';
    }
}
  • Filter (main)
public class Filtering {

    public static void main(String[] args) {

        // 요리 메뉴 중 채식주의자가 먹을 수 있는 요리만 필터링
        List<Dish> dishList = Menu.menuList
                .stream()
                .filter(dish -> dish.isVegeterian())
                .collect(toList());
        dishList.forEach(dish -> {
            System.out.println(dish.getName());
        });

        System.out.println("=============================");

        // 메뉴 목록중에 육류이면서 600칼로리 미만인 요리 필터링해서 출력
        Menu.menuList
                .stream()
                .filter(dish -> dish.getType() == Dish.Type.MEAT && dish.getCalories() < 600)
                .collect(toList())
                .forEach(dish -> System.out.println(dish));

        System.out.println("===============================");
        // 칼로리가 300칼로리보다 큰 요리 중 앞에서 3개만 필터링
        Menu.menuList
                .stream()
                .filter(d -> d.getCalories() > 300)
                .limit(3) // 앞에서부터 3개 추출
                .collect(toList())
                .forEach(d -> System.out.println(d));

        System.out.println("========================");

        // 칼로리가 300칼로리보다 큰 요리 중 처음 2개는 제끼고 필터링
        Menu.menuList
                .stream()
                .filter(d -> d.getCalories() > 300)
                .skip(2) // 맨 앞 2개 제외
                .collect(toList())
                .forEach(d -> System.out.println(d));

        System.out.println("========================");

        List<Integer> numbers = List.of(1, 2, 1, 3, 3, 2, 4, 4, 5, 6);

        // 리스트에서 짝수만 필터링
        List<Integer> intergerList = numbers.stream()
                .filter(n -> n % 2 ==0)
                .distinct() // 중복 제거
                .collect(toList());

        System.out.println(intergerList);
    }
}
  • filter하기 위해서는 .stream()을 통해 일단 펴줘야한다.
  • filter된 것을 바로 사용할 수 없고 collect()를 통해 다시 수집해줘야한다.
  • 앞에서부터 원하는 갯수만 출력하고 싶으면 .limit() 활용
  • 앞에서부터 원하는 갯수를 제외하고 싶으면 .skip() 활용
  • limit과 skip을 활용한다면 범위를 지정 할 수 있다.
  • Map (main)
// map: 리스트에서 원하는 데이터만 추출
// original     : [ {}, {}, {}, {}, {} ]
// filter       : [ {}, {}, {} ] - 내용은 같지만 갯수는 달라질 수 있음
// map          : [ "", "", "", "", "" ] - original과 갯수는 같지만 map에 의해 내용은 바뀔 수 있음
public class Mapping {

    public static void main(String[] args) {

        // 요리 이름만 추출
        List<String> nameList = menuList
                .stream()
                .map(dish -> dish.getName())
                .collect(Collectors.toList());

        System.out.println(nameList);

        System.out.println("==========================");

        List<String> words = List.of(
                "safari", "chrome", "ms edge"
                , "opera", "firefox"
        );

        // [6, 6, 7, 5, 7]
        List<Integer> collect = words.stream()
                .map(w -> w.length())
                .collect(Collectors.toList());
        System.out.println(collect);

        List<Character> characterList = words.stream()
                .map(w -> w.charAt(0)) // charAt(index): 문자열에서 해당위치 글자 추출
                .collect(Collectors.toList());
        System.out.println(characterList);

        System.out.println("======================");

        // 메뉴 목록에서 메뉴이름과 칼로리를 추출해서
        // 새로운 객체에 포장 싶음
        List<SimpleDish> simpleDishList = menuList
                .stream()
                .map(dish -> new SimpleDish(dish))
                .collect(Collectors.toList());

        simpleDishList.forEach(sd -> System.out.println(sd));
    }
}
  • 마지막 예시를 보면 내가 원하는 것만 추출해서 새로운 객체에 넣어서 출력하고 싶다. 그러면 새로운 객체를 생성해서 내가 원하는 것의 필드와 생성자를 생성한다.
public class SimpleDish {

    private final String name;
    private final int calories;

    public SimpleDish(Dish dish) {
        this.name = dish.getName();
        this.calories = dish.getCalories();
    }


    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "SimpleDish{" +
                "name='" + name + '\'' +
                ", calories=" + calories +
                '}';
    }
}
  • 한 가지 중요한 부분이 있다면 생성자다.
  • 생성자에 보통 원하는 파라미터를 집어 넣어야되는데 여기서는 dish를 통으로 집어넣었다. 이렇게 통으로 집어넣고 생성자 안에서 객체를 지정해줘도 된다.
profile
백엔드 개발자
post-custom-banner

0개의 댓글