development

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

big-blog 2020. 12. 7. 20:13
반응형

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


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

나는 많은 "컴퓨터"개체를 가지고 있고, 그들의 "리소스 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을 반환 할 수 있습니다 (그리고 지금은 옵션이 아닌 변경). 그러나이 경우 결과 String 컬렉션에서 null을 생략하고 싶습니다.

다음은 null을 필터링하는 한 가지 방법입니다.

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;
    }
});

그러나 이것은 그러한 간단한 작업에 대해 읽기는 말할 것도없고 우아하지도 않습니다! 사실, 평범한 오래된 자바 코드 (멋진 Predicate 나 Function이 전혀 없음)는 틀림없이 훨씬 더 깨끗할 것입니다.

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 = ...;
}

이를 훨씬 더 정리하고 재사용을 촉진합니다.


A bit "prettier" syntax with FluentIterable (since 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();
        }
    };

Note that the returned list is an ImmutableList. However, you can use copyInto() method to pour the elements into an arbitrary collection.


It took longer than @Jon Skeet expected, but Java 8 streams do make this simple:

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

You can also use .filter(x -> x != null) if you like; the difference is very minor.


Firstly, I'd create a constant filter somewhere:

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

Then you can use:

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));

You can use the same null filter everywhere in your code.

If you use the same computing function elsewhere, you can make that a constant too, leaving just:

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

It's certainly not as nice as the C# equivalent would be, but this is all going to get a lot nicer in Java 7 with closures and extension methods :)


You could write your own method like so. this will filter out nulls for any Function that returns null from the apply method.

   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());
    }

The method can then be called with the following code.

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);

참고URL : https://stackoverflow.com/questions/1802629/is-there-an-elegant-way-to-remove-nulls-while-transforming-a-collection-using-gu

반응형