Guava를 사용하여 컬렉션을 변환하면서 Nuls를 제거하는 우아한 방법이 있습니까?

StackOverflow https://stackoverflow.com/questions/1802629

문제

Google 컬렉션을 사용할 때 일부 컬렉션 처리 코드를 단순화하는 것에 대한 질문이 있습니다 (업데이트: 구아바).

나는 "컴퓨터"객체를 많이 가지고 있으며, "자원 ID"의 모음으로 끝나고 싶습니다. 이것은 그렇게 수행됩니다.

Collection<Computer> matchingComputers = findComputers();
Collection<String> resourceIds = 
    Lists.newArrayList(Iterables.transform(matchingComputers, new Function<Computer, String>() {
    public String apply(Computer from) {
        return from.getResourceId();
    }
}));

지금, getResourceId() NULL을 반환 할 수 있습니다 (그리고 현재 옵션이 아닌 변경). 그러나이 경우 결과 문자열 컬렉션에서 NULLS를 생략하고 싶습니다.

Nulls를 필터링하는 한 가지 방법은 다음과 같습니다.

Collections2.filter(resourceIds, new Predicate<String>() {
    @Override
    public boolean apply(String input) {
        return input != null;
    }
});

당신은 다음과 같은 모든 것을 함께 모을 수 있습니다.

Collection<String> resourceIds = Collections2.filter(
Lists.newArrayList(Iterables.transform(matchingComputers, new Function<Computer, String>() {
    public String apply(Computer from) {
        return from.getResourceId();
    }
})), new Predicate<String>() {
    @Override
    public boolean apply(String input) {
        return input != null;
    }
});

그러나 이것은 간단한 작업을 위해 읽을 수는 없지만 우아하지 않습니다! 실제로, 평범한 오래된 Java 코드 (멋진 술어 나 기능이 전혀 없음)는 훨씬 더 깨끗 할 것입니다.

Collection<String> resourceIds = Lists.newArrayList();
for (Computer computer : matchingComputers) {
    String resourceId = computer.getResourceId();
    if (resourceId != null) {
        resourceIds.add(resourceId);
    }
}

위의 것을 사용하는 것은 확실히 옵션이지만 호기심에서 (그리고 Google 컬렉션에 대한 더 많은 것을 배우고 싶어), Google 컬렉션을 사용하여 짧거나 더 우아한 방식으로 똑같은 일을 할 수 있습니까??

도움이 되었습니까?

해결책

이미 술어가 있습니다 Predicates 여기에서 도움이 될 것입니다. Predicates.notNull() - 그리고 당신은 사용할 수 있습니다 Iterables.filter() 그리고 사실 Lists.newArrayList() 취할 수 있습니다 Iterable 이것을 조금 더 청소하기 위해.

Collection<String> resourceIds = Lists.newArrayList(
  Iterables.filter(
     Iterables.transform(matchingComputers, yourFunction),
     Predicates.notNull()
  )
);

실제로 필요하지 않은 경우 Collection, 그냥 Iterable, 그런 다음 Lists.newArrayList() 전화도 사라질 수 있고 다시 한 걸음 더 깨끗합니다!

나는 당신이 그것을 찾을 수 있다고 생각합니다 Function 다시 편리하게 올 것이며 가장 유용 할 것입니다.

public class Computer {
    // ...
    public static Function<Computer, String> TO_ID = ...;
}

이것을 훨씬 더 청소합니다 (그리고 재사용을 촉진 할 것입니다).

다른 팁

약간 "더 예쁘다"구문 FluentIterable (Guava 12 이후) :

ImmutableList<String> resourceIds = FluentIterable.from(matchingComputers)
    .transform(getResourceId)
    .filter(Predicates.notNull())
    .toList();

static final Function<Computer, String> getResourceId =
    new Function<Computer, String>() {
        @Override
        public String apply(Computer computer) {
            return computer.getResourceId();
        }
    };

반환 된 목록은 an입니다 ImmutableList. 그러나 사용할 수 있습니다 copyInto() 메소드 요소를 임의의 컬렉션에 붓는 방법.

시간이 오래 걸렸습니다 @Jon Skeet이 예상되었습니다, 그러나 Java 8 스트림은 이것을 간단하게 만듭니다.

List<String> resourceIds = computers.stream()
    .map(Computer::getResourceId)
    .filter(Objects::nonNull)
    .collect(Collectors.toList());

당신은 또한 사용할 수 있습니다 .filter(x -> x != null) 당신이 좋아한다면; 차이는 매우 미미합니다.

먼저 어딘가에 일정한 필터를 만들었습니다.

public static final Predicate<Object> NULL_FILTER =  new Predicate<Object>() {
    @Override
    public boolean apply(Object input) {
            return input != null;
    }
}

그런 다음 사용할 수 있습니다.

Iterable<String> ids = Iterables.transform(matchingComputers,
    new Function<Computer, String>() {
        public String apply(Computer from) {
             return from.getResourceId();
        }
    }));
Collection<String> resourceIds = Lists.newArrayList(
    Iterables.filter(ids, NULL_FILTER));

코드의 모든 곳에서 동일한 널 필터를 사용할 수 있습니다.

다른 곳에서 동일한 컴퓨팅 함수를 사용하는 경우 일정하게 만들 수 있습니다.

Collection<String> resourceIds = Lists.newArrayList(
    Iterables.filter(
        Iterables.transform(matchingComputers, RESOURCE_ID_PROJECTION),
        NULL_FILTER));

C#에 해당하는만큼 좋지는 않지만 이것은 모두가 얻을 것입니다. 많은 폐쇄 및 확장 방법이있는 Java 7에서 더 좋습니다 :)

당신은 그렇게 자신의 방법을 쓸 수 있습니다. 이렇게하면 적용 메소드에서 NULL을 반환하는 함수에 대해 NULL을 걸러냅니다.

   public static <F, T> Collection<T> transformAndFilterNulls(List<F> fromList, Function<? super F, ? extends T> function) {
        return Collections2.filter(Lists.transform(fromList, function), Predicates.<T>notNull());
    }

그런 다음 다음 코드로 메소드를 호출 할 수 있습니다.

Collection c = transformAndFilterNulls(Lists.newArrayList("", "SD", "DDF"), new Function<String, Long>() {

    @Override
    public Long apply(String s) {
        return s.isEmpty() ? 20L : null;
    }
});
System.err.println(c);
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top