객체 데이터 변환 및 필터링
- Map과 filter는 데이터를 변환하고 필터링하는 데 사용되는 메서드이다.
- 컬렉션(배열, 리스트 등)의 요소를 처리하고 변환하는 데 유용하다.
filter
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;
}
public interface GenericPredicate<T> {
boolean test(T t);
}
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, 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
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;
}
@FunctionalInterface
public interface GenericFuncction<X,Y> {
Y apply(X x);
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)
);
}
}
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 +
'}';
}
}
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("=============================");
Menu.menuList
.stream()
.filter(dish -> dish.getType() == Dish.Type.MEAT && dish.getCalories() < 600)
.collect(toList())
.forEach(dish -> System.out.println(dish));
System.out.println("===============================");
Menu.menuList
.stream()
.filter(d -> d.getCalories() > 300)
.limit(3)
.collect(toList())
.forEach(d -> System.out.println(d));
System.out.println("========================");
Menu.menuList
.stream()
.filter(d -> d.getCalories() > 300)
.skip(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)
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"
);
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))
.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를 통으로 집어넣었다. 이렇게 통으로 집어넣고 생성자 안에서 객체를 지정해줘도 된다.