hanker

[Java] filter() 메서드 (조건에 맞는 데이터만 출력) 본문

JAVA

[Java] filter() 메서드 (조건에 맞는 데이터만 출력)

hanker 2025. 6. 29. 14:39
반응형

Java 8에서 도입된 Java Stream의 filter 기능에 대해서 알아보자.

 

filter() 메서드는 주어진 조건을 만족하는 요소들만 통과시키는 중간 연산자이다.

 


1. filter() 기본 사용법
public class Main {
    public static void main(String[] args) throws Exception {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        // 짝수만 필터링
        List<Integer> evenNumbers = numbers.stream()
                .filter(n -> n % 2 == 0)
                .collect(Collectors.toList());

        System.out.println("원본 리스트: " + numbers);
        System.out.println("짝수만: " + evenNumbers);
    }
}

 

  • numbers.stream(): 컬렉션을 스트림으로 변환
  • filter(n -> n % 2 == 0): 람다 표현식으로 짝수 조건 정의
  • collect(Collectors.toList()): 필터링된 결과를 새로운 List로 수집
  • 원본 리스트는 변경되지 않고, 새로운 리스트가 생성됨

 

filter() 메서드 결과값

 


2. 다양한 데이터 타입 필터링

 

2-1. 문자열 필터링

public class Main {
    public static void main(String[] args) throws Exception {
        List<String> names = Arrays.asList("Ada", "C++", "Java", "C#", "Python");

        // 길이가 4 이상인 이름만 필터링
        List<String> longNames = names.stream()
                .filter(name -> name.length() >= 4)    // 문자열 길이가 4 이상인지 체크
                .collect(Collectors.toList());

        System.out.println("길이 4 이상: " + longNames);

        // 'A'로 시작하는 이름만 필터링
        List<String> aNames = names.stream()
                .filter(name -> name.startsWith("A"))  // 'A'로 시작하는지 체크
                .collect(Collectors.toList());

        System.out.println("A로 시작: " + aNames);

        // 대소문자 무시하고 'a'가 포함된 이름 필터링
        List<String> containsA = names.stream()
                .filter(name -> name.toLowerCase().contains("a"))  // 소문자로 변환 후 'a' 포함 여부 체크
                .collect(Collectors.toList());

        System.out.println("'a' 포함: " + containsA);
    }
}

 

  • name.length() >= 4: 문자열의 길이를 반환하여 4 이상인지 확인
  • name.startsWith("A"): 문자열이 특정 문자로 시작하는지 확인
  • name.toLowerCase().contains("a"): 대소문자를 통일한 후 특정 문자 포함 여부 확인
  • 각 filter는 boolean 값을 반환하는 조건식을 사용

 

문자열 필터링 결과값

 

 

 

2-2. 객체 필터링

class Employee {
    private String name;
    private int age;
    private String department;
    private double salary;

    public Employee(String name, int age, String department, double salary) {
        this.name = name;
        this.age = age;
        this.department = department;
        this.salary = salary;
    }

    // getter 메서드들 - 객체의 필드에 접근하기 위해 필요
    public String getName() { return name; }
    public int getAge() { return age; }
    public String getDepartment() { return department; }
    public double getSalary() { return salary; }

    @Override
    public String toString() {
        return String.format("%s(%d세, %s, %.0f만원)", name, age, department, salary);
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        // Employee 객체들의 리스트 생성
        List<Employee> employees = Arrays.asList(
                new Employee("김철수", 28, "개발팀", 4500),
                new Employee("이영희", 32, "마케팅팀", 4000),
                new Employee("박민수", 35, "개발팀", 5500),
                new Employee("최지은", 29, "디자인팀", 4200),
                new Employee("정우진", 40, "개발팀", 6000)
        );

        // 개발팀 직원만 필터링
        List<Employee> developers = employees.stream()
                .filter(emp -> emp.getDepartment().equals("개발팀"))  // 부서명이 "개발팀"인지 확인
                .collect(Collectors.toList());

        System.out.println("개발팀 직원들:");
        developers.forEach(System.out::println);  // 메서드 참조로 각 요소 출력

        // 30세 이상이면서 연봉 5000만원 이상인 직원 (AND 조건)
        List<Employee> seniorHighPaid = employees.stream()
                .filter(emp -> emp.getAge() >= 30 && emp.getSalary() >= 5000)  // 두 조건을 모두 만족
                .collect(Collectors.toList());

        System.out.println("\n30세 이상 & 연봉 5000만원 이상:");
        seniorHighPaid.forEach(System.out::println);
    }
}

 

  • Employee 클래스: 직원 정보를 담는 사용자 정의 객체
  • emp.getDepartment().equals("개발팀"): 객체의 getter 메서드를 통해 필드 값 비교
  • emp.getAge() >= 30 && emp.getSalary() >= 5000: AND 연산자로 여러 조건 결합
  • forEach(System.out::println): 메서드 참조를 사용한 간결한 출력 방식

객체 필터링 결과값

 

 

 


3. 복합 조건 필터링
public class Main {
    public static void main(String[] args) throws Exception {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);

        // 5보다 크고 10보다 작은 수 (두 조건을 모두 만족해야 함)
        List<Integer> filtered = numbers.stream()
                .filter(n -> n > 5 && n < 10)  // && 연산자로 두 조건 결합
                .collect(Collectors.toList());

        System.out.println("5 < n < 10: " + filtered);

        // 3의 배수이거나 5의 배수인 수 (둘 중 하나만 만족해도 됨)
        List<Integer> multipleOf3Or5 = numbers.stream()
                .filter(n -> n % 3 == 0 || n % 5 == 0)  // || 연산자로 두 조건 중 하나만 만족하면 통과
                .collect(Collectors.toList());

        System.out.println("3의 배수 또는 5의 배수: " + multipleOf3Or5);
    }
}

 

  • n > 5 && n < 10: 논리 AND 연산자로 두 조건을 모두 만족하는 경우만 통과
  • 5보다 크면서 동시에 10보다 작은 수만 필터링됨
  • n % 3 == 0 || n % 5 == 0: 논리 OR 연산자로 둘 중 하나의 조건만 만족해도 통과
  • n % 3 == 0: 3으로 나눈 나머지가 0 (3의 배수)
  • n % 5 == 0: 5로 나눈 나머지가 0 (5의 배수)

복합 조건 필터링

 

 


4. Predicate를 변수로 분리
class Employee {
    private String name;
    private int age;
    private String department;
    private double salary;

    public Employee(String name, int age, String department, double salary) {
        this.name = name;
        this.age = age;
        this.department = department;
        this.salary = salary;
    }

    // getter 메서드들 - 객체의 필드에 접근하기 위해 필요
    public String getName() { return name; }
    public int getAge() { return age; }
    public String getDepartment() { return department; }
    public double getSalary() { return salary; }

    @Override
    public String toString() {
        return String.format("%s(%d세, %s, %.0f만원)", name, age, department, salary);
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        // Employee 객체들의 리스트 생성
        List<Employee> employees = Arrays.asList(
                new Employee("김철수", 28, "개발팀", 4500),
                new Employee("이영희", 32, "마케팅팀", 4000),
                new Employee("박민수", 35, "개발팀", 5500),
                new Employee("최지은", 29, "디자인팀", 4200),
                new Employee("정우진", 40, "개발팀", 6000)
        );

        // Predicate를 변수로 정의 - 재사용 가능한 조건들
        Predicate<Employee> isDeveloper = emp -> emp.getDepartment().equals("개발팀");
        Predicate<Employee> isHighPaid = emp -> emp.getSalary() >= 5000;
        Predicate<Employee> isYoung = emp -> emp.getAge() < 35;

        // Predicate 조합 사용
        List<Employee> youngDevelopers = employees.stream()
                .filter(isDeveloper.and(isYoung))  // AND 조합: 개발팀이면서 젊은 직원
                .collect(Collectors.toList());

        List<Employee> highPaidOrDeveloper = employees.stream()
                .filter(isHighPaid.or(isDeveloper))  // OR 조합: 고연봉자이거나 개발자
                .collect(Collectors.toList());

        List<Employee> notDeveloper = employees.stream()
                .filter(isDeveloper.negate())  // NOT 조합: 개발자가 아닌 직원
                .collect(Collectors.toList());

        System.out.println("젊은 개발자: " + youngDevelopers);
        System.out.println("고연봉자 또는 개발자: " + highPaidOrDeveloper);
        System.out.println("개발자가 아닌 직원: " + notDeveloper);
    }
}

 

  • Employee 클래스: 직원 정보를 담는 사용자 정의 객체
  • emp.getDepartment().equals("개발팀"): 객체의 getter 메서드를 통해 필드 값 비교
  • emp.getAge() >= 30 && emp.getSalary() >= 5000: AND 연산자로 여러 조건 결합
  • forEach(System.out::println): 메서드 참조를 사용한 간결한 출력 방식

 


정리

 

Java Stream의 filter() 메서드는 단순한 조건부터 복잡한 비즈니스 로직까지 다양한 상황에서 활용할 수 있으며, 코드의 가독성과 유지보수성을 크게 향상시켜준다.

 

 

  • 간단한 조건은 람다 표현식으로, 복잡한 조건은 Predicate 변수로 분리
  • 성능을 위해 간단한 조건을 먼저 필터링
  • 대용량 데이터는 parallel stream 고려
  • 다른 Stream 연산과 조합하여 강력한 데이터 처리 파이프라인 구축

 

 

반응형