代码之家  ›  专栏  ›  技术社区  ›  Jonik

在使用番石榴转换集合时,是否有一种优雅的方法来删除空值?

  •  59
  • Jonik  · 技术社区  · 15 年前

    在使用Google Collections时,我有一个关于简化一些集合处理代码的问题( : 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(现在更改它不是一个选项),但在本例中,我希望从结果字符串集合中省略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;
        }
    });
    

    但对于这样一个简单的任务来说,这很难做到优雅,更不用说可读性了!事实上,普通的旧Java代码(根本没有花哨的谓词或函数内容)可以说要干净得多:

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

    你能用谷歌收藏以更短或更优雅的方式做同样的事情吗

    5 回复  |  直到 12 年前
        1
  •  80
  •   Grzegorz Rożniecki    9 年前

    中已经有一个谓词 Predicates 这对你有帮助-- Predicates.notNull() Iterables.filter() 事实上 Lists.newArrayList() 我可以坐飞机吗 Iterable

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

    如果你真的不需要 Collection ,只是一个 ,然后 电话也可以不打了,你又干净了一步!

    我想你可能会发现 Function

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

    这会更彻底地清理这个问题(并促进重用)。

        2
  •  35
  •   Grzegorz Rożniecki    9 年前

    FluentIterable (自番石榴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();
            }
        };
    

    请注意,返回的列表是一个 ImmutableList . 但是,您可以使用 copyInto() 方法将元素倒入任意集合中。

        3
  •  14
  •   Jeffrey Bosboom    10 年前

    花的时间比 @Jon Skeet expected ,但Java 8流确实使这一点变得简单:

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

    你也可以使用 .filter(x -> x != null) 如果你愿意; the difference is very minor .

        4
  •  5
  •   Jon Skeet    15 年前

    首先,我要在某处创建一个常量过滤器:

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

    许多 使用闭包和扩展方法在Java7中更出色:)

        5
  •  1
  •   BewdyM8    15 年前

    您可以这样编写自己的方法。这将为从apply方法返回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);