Guava를 사용하여 컬렉션을 변환하면서 Nuls를 제거하는 우아한 방법이 있습니까?
-
05-07-2019 - |
문제
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);