【问题标题】:Getting a list of elements from an ArrayList从 ArrayList 中获取元素列表
【发布时间】:2010-06-24 17:37:01
【问题描述】:

假设我有一个如下所示的 bean。

class Customer{
  private String code;
  private String name;
  private Integer value;
  //getters setters omitted for brevity
}

然后从一个方法我得到一个List<Customer> 回来。现在假设我想从列表中获取所有成员“名称”的列表。显然,我可以自己遍历并构建元素“名称”的List<String>

但是,我想知道是否有任何人都知道的这种技术的捷径或更有效的方法。例如,如果我想获取 Map 对象中所有键的列表,我可以使用 map.keySet()。沿着这条线的东西是我试图找出的。

【问题讨论】:

  • 我认为你被卡住了,或者如果你喜欢冒险,你可以看看quaere。查看What is the Java equivalent for LINQ? 了解一些相关信息。
  • 我认为您不是在寻找更有效的方式,而是在寻找更有效的方式。默认的 for 循环已经足够高效了。
  • 嗯,不一定是效率方面的,但我试图了解其他人为像这样的日常任务做了什么。我了解到 Guava 和 Lambdaj 是一些流行的替代品。所以这很好!

标签: java arraylist


【解决方案1】:

Guava 有Lists.transform,可以使用提供的Function<F,T>(或者更确切地说,Function<? super F,? extends T>)将List<F> 转换为List<T>

来自文档:

public static <F,T>
   List<T> transform(
               List<F> fromList,
               Function<? super F,? extends T> function
           )

返回将function 应用于fromList 的每个元素的列表。返回的列表是fromList 的转换视图;对fromList 的更改将反映在返回的列表中,反之亦然。

function 被延迟应用,在需要时调用。

还提供了类似的实时视图转换,如下所示:

【讨论】:

  • 酷,很高兴看到一个库的链接,该库基本上符合我在帖子中的建议。
  • @Mark:最大的不同在于 Guava 的实现是按需应用函数。有点“懒惰的变换”,正如你所渴望的那样。
  • 啊,我很好奇他们为什么专门使用可迭代。这就解释了原因。我现在更喜欢它了(假设 Guava 中有一个工厂方法可以从可迭代对象中创建一个集合?)
  • @Mark:我环顾四周,发现Collections2.transform。但是,是的,从 IterableCollectionIterables.addAll
  • @Mark Peters 是的,Guava 可以从 Iterable 中创建任何类型的 Collection,使用 Lists 之类的工厂方法或所有不可变集合上的 copyOf(Iterable) 工厂方法类。
【解决方案2】:

看起来您正在寻找 Perl 的 map 函数的 Java 等效项。一旦(如果)Java 接收到闭包,这种东西可能会被添加到集合库中。在那之前,我认为这是你能做的最好的:

List<String> list = new ArrayList<String>(customers.size());
for ( Customer c : customers ) {
    list.add(c.getName());
}

您还可以编写一个map 函数,它使用一个简单的接口来提供映射函数。像这样的:

public interface Transform<I, O> {
    O transform(I in);
}
public <I, O> List<O> map(Collection<I> coll, Transform<? super I, ? extends O> xfrm) {
    List<O> list = new ArrayList<O>(coll.size());
    for ( I in : coll ) {
        list.add(xfrm.transform(in));
    }
    return list;
}

【讨论】:

  • “Perl 的”映射函数?呵呵!你不是说Java相当于Perl相当于Lisp的map函数吗?
【解决方案3】:

可以使用这样的东西: http://code.google.com/p/lambdaj/

【讨论】:

    【解决方案4】:

    我认为这是你必须自己编写代码的东西,在一个循环中。

    【讨论】:

    【解决方案5】:

    你可以使用LambdaJConverter接口,有如下一行:

    List<String> customerNames = convert(customerList, new Converter<Customer,String>() {
      public String convert(Customer customer) {
        return customer.getName();
      }
    });
    

    【讨论】:

    • 有趣。你知道它是否适用于 jdk 1.5 还是仅适用于 Java 6?
    【解决方案6】:

    您需要使用循环,但您要查找的函数在函数式语言中称为map。在 Java 中实现map 是可能的,尽管它往往相当不优雅;这是我多年前在“Java 应该有但由于某种原因没有”库中实现的版本:

    public interface MapFunction<T, U> {
        public U map(T source);
    }
    
    public static <T, U> U[] map(T[] objects, MapFunction<T, U> f) {
        if(objects.length == 0) {throw new IllegalArgumentException("Can't map onto an empty array");}
        @SuppressWarnings("unchecked") U[] rtn = (U[])Array.newInstance(f.map(objects[0]).getClass(), objects.length);
        for(int i = 0; i < objects.length; i++)
            rtn[i] = f.map(objects[i]);
        return rtn;
    }
    

    使用它,你可以这样做:

    List<Customer> list = yourFunction();
    List<String> names = Arrays.asList(map(list.toArray(new Customer[0]), new MapFunction<Customer, String>() {
        public String map(Customer c) {
            return c.getName();
        }
    }));
    

    您可以自然地更改 map 以获取集合而不是数组,这将消除对 Arrays.asListList.toArray 的需要

    【讨论】:

      【解决方案7】:

      使用Guava,您可以使用FunctionIterables.transformCollections2.transformLists.transform 分别创建IterableCollectionList

      Iterable<String> names = Iterables.transform(customers, 
          new Function<Customer, String>() {
            public String apply(Customer from) {
              return from.getName();
            }
          });
      

      返回的Iterable 是惰性的,并在您遍历它时将函数应用于底层列表。对于包含名称的List&lt;String&gt;,您可以使用:

      List<String> names = Lists.transform(...);
      

      ImmutableList<String> names = ImmutableList.copyOf(Iterables.transform(...));
      

      当然,每次你想写出匿名内部类 Function 实现是丑陋和冗长的,所以你可能想让 Function 成为 Customer 类中可用的常量,称为 @比如987654338@。

      然后转换看起来更好(尤其是静态导入):

      for (String name : transform(customers, Customer.NAME)) { ... }
      

      我还在我的博客here 上写了关于使用对象特定属性的接口(例如name 此处)来帮助整合这些功能。

      【讨论】:

      • 好吧,你和多基因都给出了类似的答案。如果我能接受两个正确的答案,那么我也会接受你的。谢谢!
      【解决方案8】:

      ....有没有捷径或更有效的方法

      那么,您是否正在寻找一种更有效的方法来做到这一点:

       List<String> names = new ArrayList<String>();
       for( Customer customer : yourCustomerList ) {
           names.add( customer.getName() );
       }
      

      ?!!!!

      或者只是一种不同的方式?

      所有先前的答案在运行时和编码方面都不是真的更有效。然而,毫无疑问,它们更灵活。

      另一种选择是在您的 Java 代码中包含 Scala Groovy 并使用它:

      list.map( _.name )

      list.collect { it.name }
      

      如果已编译,则可以从 Java 中使用 Groovy 类,或者您可以将它们作为脚本插入。

      这里是给定Customer 类的示例,它使用Groovy 作为脚本。

          List<Customer> customers = Arrays.asList( new Customer[]{
             new Customer("A","123",1),
             new Customer("B","456",2),
             new Customer("C","789",3),
             new Customer("D","012",4)
          });
      
          setVariable(customers, "list");
          evaluate("names = list.collect { it.name } ");
          List<String> names = (List<String>) getVariable("names");
          System.out.println("names = " + names);
      

      输出:

      names = [A, B, C, D]
      

      注意:我提取方法是为了便于阅读,但您可以在下面看到它们

      但是,这又是不同的,并不比常规的 for 循环更有效。

      这是complete source code。要运行它,您只需要类路径中的 Java1.6 和 Groovy。

      【讨论】:

      • 谢谢,我听说过 Groovy,但还没有搞砸。这看起来很整洁。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-04-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多