[java] Java Collection을 필터링하는 가장 좋은 방법은 무엇입니까?

java.util.Collection조건 자를 기반으로 기준 을 필터링하고 싶습니다 .



답변

Java 8 ( 2014 )은 한 줄의 코드로 스트림과 람다를 사용하여이 문제를 해결합니다.

List<Person> beerDrinkers = persons.stream()
    .filter(p -> p.getAge() > 16).collect(Collectors.toList());

튜토리얼은 다음과 같습니다 .

Collection#removeIf컬렉션을 제자리에 수정하는 데 사용 합니다. (주의 사항 :이 경우 술어는 술어를 만족시키는 오브젝트를 제거합니다) :

persons.removeIf(p -> p.getAge() <= 16);

lambdaj를 사용하면 루프 나 내부 클래스를 작성하지 않고도 컬렉션을 필터링 할 수 있습니다.

List<Person> beerDrinkers = select(persons, having(on(Person.class).getAge(),
    greaterThan(16)));

더 읽기 쉬운 것을 상상할 수 있습니까?

면책 조항 : 나는 lambdaj에 기고자입니다


답변

Java 1.5 를 사용하고 있고 Google Collections를 추가 할 수 없다고 가정하면 Google 직원 과 매우 유사한 작업을 수행합니다. 이것은 Jon의 의견에 약간의 변형입니다.

먼저이 인터페이스를 코드베이스에 추가하십시오.

public interface IPredicate<T> { boolean apply(T type); }

구현자는 특정 술어가 특정 유형에 해당하면 응답 할 수 있습니다. 예 경우 T했다 UserAuthorizedUserPredicate<User>구현은 IPredicate<T>다음 AuthorizedUserPredicate#apply전달 여부를 반환 User권한이 부여됩니다.

그런 다음 일부 유틸리티 클래스에서는

public static <T> Collection<T> filter(Collection<T> target, IPredicate<T> predicate) {
    Collection<T> result = new ArrayList<T>();
    for (T element: target) {
        if (predicate.apply(element)) {
            result.add(element);
        }
    }
    return result;
}

따라서 위의 내용을 사용한다고 가정하면

Predicate<User> isAuthorized = new Predicate<User>() {
    public boolean apply(User user) {
        // binds a boolean method in User to a reference
        return user.isAuthorized();
    }
};
// allUsers is a Collection<User>
Collection<User> authorizedUsers = filter(allUsers, isAuthorized);

선형 검사의 성능이 중요하다면 대상 컬렉션이있는 도메인 개체를 원할 수 있습니다. 대상 컬렉션이있는 도메인 개체에는 대상 컬렉션을 초기화, 추가 및 설정하는 메서드에 대한 필터링 논리가 있습니다.

최신 정보:

유틸리티 클래스 (Predicate라고 함)에서 술어가 예상 값을 반환하지 않을 때 기본값 옵션과 함께 select 메소드를 추가했으며 새 IPredicate 내에서 매개 변수를 사용할 정적 속성도 추가했습니다.

public class Predicate {
    public static Object predicateParams;

    public static <T> Collection<T> filter(Collection<T> target, IPredicate<T> predicate) {
        Collection<T> result = new ArrayList<T>();
        for (T element : target) {
            if (predicate.apply(element)) {
                result.add(element);
            }
        }
        return result;
    }

    public static <T> T select(Collection<T> target, IPredicate<T> predicate) {
        T result = null;
        for (T element : target) {
            if (!predicate.apply(element))
                continue;
            result = element;
            break;
        }
        return result;
    }

    public static <T> T select(Collection<T> target, IPredicate<T> predicate, T defaultValue) {
        T result = defaultValue;
        for (T element : target) {
            if (!predicate.apply(element))
                continue;
            result = element;
            break;
        }
        return result;
    }
}

다음 예제는 컬렉션간에 누락 된 개체를 찾습니다.

List<MyTypeA> missingObjects = (List<MyTypeA>) Predicate.filter(myCollectionOfA,
    new IPredicate<MyTypeA>() {
        public boolean apply(MyTypeA objectOfA) {
            Predicate.predicateParams = objectOfA.getName();
            return Predicate.select(myCollectionB, new IPredicate<MyTypeB>() {
                public boolean apply(MyTypeB objectOfB) {
                    return objectOfB.getName().equals(Predicate.predicateParams.toString());
                }
            }) == null;
        }
    });

다음 예제는 컬렉션에서 인스턴스를 찾고 인스턴스를 찾을 수 없을 때 컬렉션의 첫 번째 요소를 기본값으로 반환합니다.

MyType myObject = Predicate.select(collectionOfMyType, new IPredicate<MyType>() {
public boolean apply(MyType objectOfMyType) {
    return objectOfMyType.isDefault();
}}, collectionOfMyType.get(0));

업데이트 (Java 8 릴리스 이후) :

I (Alan)이이 답변을 처음 게시 한 지 몇 년이 지났지 만 여전히이 답변에 대한 SO 포인트를 수집하고 있다고 믿을 수 없습니다. 어쨌든 Java 8이 언어에 대한 클로저를 도입 했으므로 이제는 대답이 상당히 달라지고 더 간단 해졌습니다. Java 8에서는 고유 한 정적 유틸리티 클래스가 필요하지 않습니다. 따라서 술어와 일치하는 첫 번째 요소를 찾으려면.

final UserService userService = ... // perhaps injected IoC
final Optional<UserModel> userOption = userCollection.stream().filter(u -> {
    boolean isAuthorized = userService.isAuthorized(u);
    return isAuthorized;
}).findFirst();

선택적 항목에 대한 JDK 8 API는 능력이있다 get(), isPresent(), orElse(defaultUser), orElseGet(userSupplier)orElseThrow(exceptionSupplier)뿐만 아니라 같은 다른 ‘모나드’기능 map, flatMapfilter.

술어와 일치하는 모든 사용자를 단순히 수집하려면를 사용 Collectors하여 원하는 콜렉션에서 스트림을 종료하십시오.

final UserService userService = ... // perhaps injected IoC
final List<UserModel> userOption = userCollection.stream().filter(u -> {
    boolean isAuthorized = userService.isAuthorized(u);
    return isAuthorized;
}).collect(Collectors.toList());

Java 8 스트림 작동 방식에 대한 자세한 예는 여기 를 참조 하십시오 .


답변

Apache Commons의 CollectionUtils.filter (Collection, Predicate)를 사용하십시오 .


답변

“최상의”방법은 요청이 너무 넓습니다. “가장 짧습니까?” “가장 빠르다”? “읽기 쉬운”? 제자리에서 또는 다른 컬렉션으로 필터링 하시겠습니까?

가장 단순하지만 읽기 쉬운 방법은 반복하고 Iterator.remove () 메서드를 사용하는 것입니다.

Iterator<Foo> it = col.iterator();
while( it.hasNext() ) {
  Foo foo = it.next();
  if( !condition(foo) ) it.remove();
}

이제 더 읽기 쉽게하기 위해 유틸리티 메소드로 랩핑 할 수 있습니다. 그런 다음 IPredicate 인터페이스를 발명하고 해당 인터페이스의 익명 구현을 작성하고 다음과 같은 작업을 수행하십시오.

CollectionUtils.filterInPlace(col,
  new IPredicate<Foo>(){
    public boolean keepIt(Foo foo) {
      return foo.isBar();
    }
  });

여기서 filterInPlace ()는 컬렉션을 반복하고 Predicate.keepIt ()을 호출하여 컬렉션에 유지할 인스턴스가 있는지 확인합니다.

나는이 작업을 위해 타사 라이브러리를 가져 오는 것에 대한 정당화를 실제로 보지 못합니다.


답변

제네릭을 지원하는 업데이트 된 컬렉션 프레임 워크에 대해서는 Google 컬렉션 을 고려하십시오 .

업데이트 : Google 컬렉션 라이브러리는 더 이상 사용되지 않습니다. 대신 최신 버전의 구아바 를 사용해야합니다 . 술어를 기반으로 필터링하는 메커니즘을 포함하여 콜렉션 프레임 워크에 대한 모든 확장이 여전히 있습니다.


답변

Java 8을 기다리십시오.

List<Person> olderThan30 =
  //Create a Stream from the personList
  personList.stream().
  //filter the element to select only those with age >= 30
  filter(p -> p.age >= 30).
  //put those filtered elements into a new List.
  collect(Collectors.toList());


답변

Java 8의 초기 릴리스 이후 다음과 같은 것을 시도해 볼 수 있습니다.

Collection<T> collection = ...;
Stream<T> stream = collection.stream().filter(...);

예를 들어, 정수 목록이 있고> 10보다 큰 숫자를 필터링 한 다음 해당 숫자를 콘솔에 인쇄하려면 다음과 같이 할 수 있습니다.

List<Integer> numbers = Arrays.asList(12, 74, 5, 8, 16);
numbers.stream().filter(n -> n > 10).forEach(System.out::println);