【问题标题】:How to get a list of specific fields values from objects stored in a list?如何从存储在列表中的对象中获取特定字段值的列表?
【发布时间】:2012-06-12 12:57:04
【问题描述】:

假设我有一个对象列表,其中包含两个字段 field1field2,均为 String 类型。

如果可能的话,我如何获得所有 field1 值的列表而无需遍历列表?

【问题讨论】:

  • 不,一般情况下没有。或者你有更多细节吗?
  • “无需遍历”,我猜完全没有。
  • without having to iterate through the list 你的意思是你不会遍历列表,甚至图书馆也不会?基本上,您只是想要一些易于输入的东西,还是真的需要 O(1) 访问基于条件的对象子集?这是两个完全不同的问题。

标签: java collections


【解决方案1】:

幸运的是,您可以使用 Java 8 - Streams

做到这一点

假设您有一个名为 YourEntity

的实体
public class YourEntity {

    private String field1;
    private String field2;

    public YourEntity(String field1, String field2) {
        this.field1 = field1;
        this.field2 = field2;
    }

    public void setField1(String field1) {
        this.field1 = field1;
    }

    public void setField2(String field2) {
        this.field2 = field2;
    }

    public String getField1() {
        return field1;
    }

    public String getField2() {
        return field2;
    }
}

使用以下方式声明您的 YourEntity 列表:

List<YourEntity> entities = Arrays.asList(new YourEntity("text1", "text2"), new YourEntity("text3", "text4"));

您可以通过这种方式一次性提取field1的列表:

import java.util.stream.Collectors;

List<String> field1List = entities.stream().map(YourEntity::getField1).collect(Collectors.toList());

或者这样

import java.util.stream.Collectors;

List<String> field1List = entities.stream().map(urEntity -> urEntity.getField1()).collect(Collectors.toList());

您也可以使用 java 8 打印所有项目 :)

field1List.forEach(System.out::println);

输出

text1
text3

【讨论】:

  • 如何为实体类中的任意数量的字段实现这一点?比如说,我正在编写一种通用方法来在单个实体类中的任意数量的字段或我在方法中作为参数发送的任何实体类中实现此目的。
【解决方案2】:

试试这个:

List<Entity> entities = getEntities();
List<Integer> listIntegerEntities = Lambda.extract(entities, Lambda.on(Entity.class).getFielf1());

LambdaJ 允许在没有显式循环的情况下访问集合,因此您无需自己编写更多代码行来迭代列表,而是让 LambdaJ 来完成。

【讨论】:

  • 这个答案肯定应该用这里使用的东西的简短描述来更新(LambdaJ?)
【解决方案3】:

对象是对内存地址的引用。然后,此对象的字段是对其他内存地址的其他引用。 因此,对象列表是引用列表。因此,列表不可能直接访问对象字段(引用给出的引用)。简短的回答是否定的。

注意:不管怎样,你会找到一个 API 做你想做的事,它仍然在里面循环。

【讨论】:

  • 一定要注意。您可以实现一个集合,该集合在插入时将元素添加到列表中,您根据搜索条件对其进行索引,然后直接返回列表本身。这样,插入效率会有些低,但检索将允许执行 OP 想要的操作。
  • 确实如此,但这样的集合将非常特定于它所包含的对象类型,而不是通用的。
  • 部分,是的,为了不完全“固定”,您需要使用边界接口或反射,因此您需要对可以插入的类型施加轻微的刚性,或者对索引器的性能和复杂性影响。但这完全可行。事实上,如果已经实现为 Java 集合和复合对象提供查询功能的某些库和语言扩展使用类似于第二种方法的东西,我不会感到惊讶。
  • 我不同意这里;确实,从逻辑上讲,列表必须是引用列表,但列表只是一个接口——如果它返回正确的东西,它就不需要后备存储。请参阅下面我的答案,了解不循环且满足要求的版本。
【解决方案4】:

取决于...

...您的问题是否涉及avoiding iterating over the collection

  • 在呼叫点易于实施
  • 就算法复杂性而言

具体来说,你的意思是:

  • 您不想自己输入迭代构造(只需使用便利库),
  • 或者您实际上想要在 O(1) 中自动返回元素而不需要处理它们的东西(并且具有完美的访问权限)?

请参阅下面的解决方案和选项。


使用便利库

如果是第一个,请查看 Google Guava、LambdaJ、FunctionalJava 或其他实现基本功能结构的库,并允许您通过几个富有表现力的调用来做您想做的事情。但请记住,它们会按照罐头上的说明进行操作:它们将过滤、收集或转换集合,并将迭代其元素以执行此操作。

例如:

  • Google Guava:

    Set<String> strings = buildSetStrings();  
    Collection<String> filteredStrings =
        Collections2.filter(strings, Predicates.containsPattern("^J"));  
    
  • Functional Java:

    Array<Integer> a = array(97, 44, 67, 3, 22, 90, 1, 77, 98, 1078, 6, 64, 6, 79, 42);
    Array<Integer> b = a.filter(even);
    
  • LambdaJ:

    List<Integer> biggerThan3 = filter(greaterThan(3), asList(1, 2, 3, 4, 5));
    

完美访问

如果是第二个,这是不可能的除了如果你从一开始就构建了所有的东西,你的对象应该由一个自定义集合类,它将根据插入时的字段值索引您的对象

它将它们保存在由所述值索引的存储桶中,以便您随时可以将它们作为列表检索或按需设置。

正如 dounyy 回答下面的 cmets 所述,设计这样一个自定义集合可能会影响它接受的元素的 API(很可能通过定义用于元素类型的超级接口),或者需要一个如果您希望这个集合是通用的,那么动态解析成员的相当复杂的实现(很可能通过使用反射)。

【讨论】:

  • 完美访问也可以通过实现List&lt;String&gt;接口来推迟到底层List&lt;Entity&gt;接口。这不会引入不必要的迭代;访问List&lt;String&gt;ith 元素与访问List&lt;Entity&gt;ith 元素一样昂贵。也没有 API 影响。
  • @user295691:是的。不同的方法,但相同的答案:如果不编写自定义实现,OP 就无法做到。
  • 自定义实现,是的,但不是“从头开始建筑师[ing]一切”——如果你想在现场打电话给Collections.frequency()Collections.binarySearch(),这可以快速使用在对象内部。
【解决方案5】:

Java 作为一种语言和 JDK 库都不能满足您的需求。您可以使用 LambdaJ 或等待预计包含 lambda 表达式的 Java 8。

【讨论】:

  • 如何避免迭代? Lambda J 仍然会为你做这件事。否则,您已经可以为此使用 Google Guava、LambdaJ、FunctionalJava 或许多其他的了。但当然,它们都会遍历结构。
  • 你是对的,@haylem。显然有人必须遍历集合。没有办法避免。当闭包被引入java时,它们会在幕后做同样的事情。您提到的工具只是帮助我们编写更短的代码。
【解决方案6】:

古老的问题,但我在寻找是否可以改进类似解决方案时遇到了这个问题。

您可以在不创建充实的ArrayList&lt;String&gt; 的情况下实现List&lt;String&gt; 接口,因此无需遍历父对象。

final List<Entity> entities = getEntities()
final List<String> field1 = new AbstractList() {
    public String get(int index) {
        return entities.get(index).getField1();
    }
    public int size() {
        return entities.size();
    }
}

这会为您提供一个列表,而无需遍历父对象。

对派生的List&lt;String&gt; 的随机访问将与对底层List&lt;Entity&gt; 的随机访问一样昂贵;如果您正在使用不提供快速随机访问的List&lt;Entity&gt; 的实现,您可能需要跳过几个环节(即实现更多List&lt;String&gt; 的方法。但这应该适用于 99% 的情况你需要一个轻便的适配器。

【讨论】:

  • 如果底层列表实现确实允许随机访问(例如 ArrayList),那就可以了。请注意,OP 的问题不是只访问一个值。
  • @haylem 如果原始列表不支持随机访问(例如LinkedList&lt;Entity&gt;),那么您将改为扩展AbstractSequentialList&lt;String&gt; - 这在我的回答中有所说明(间接地,但我认为很清楚) .这里的核心概念是您可以在O(1) 时间创建请求列表并使用O(1) 空间。您能否澄清“仅访问一个值”的含义?您可以访问派生列表中的所有值;它不会比在基础列表上调用get() 更昂贵。
【解决方案7】:

让对象属于以下类别。

public class Bike {

    String bikeModel;
    Int price;
}

现在有一个名为bikeList的自行车列表,类型为List

所以现在我们想要上面列表中所有自行车的自行车型号列表。

bikeList.map{ Bike b -> b.bikeModel }.toCollection(arrayListOf())

返回bikeList中所有自行车对象的第一个字段的数组列表

【讨论】:

    【解决方案8】:

    是的!这是可能的。 但是你必须做一些歪曲的工作。

    1. 创建一个包含所需变量的内部类。 例如:这里将使用 2 个变量。(TNAME 和 TABTYPE)。

       public class storeTables {
              private String tablename = "";
              private String tabletype = "";
              public storeTables(String a, String b)
              {
                  this.setTablename(a);
                  this.setTabletype(b);
              }
              public void setTablename(String tablename) {
                  this.tablename = tablename;
              }
              public String getTablename() {
                  return tablename;
              }
              public void setTabletype(String tabletype) {
                  this.tabletype = tabletype;
              }
              public String getTabletype() {
                  return tabletype;
              }}
      
    2. 创建上面创建的内部类的List,不要忘记封装。

      private List<storeTables> objlist= new ArrayList<storeTables>();
      
    3. 获取存储到列表中的值作为内部类对象。

      String Query="SELECT * FROM TAB";
      while(rs.next())
      {
      String tname=rs.getString("TNAME");
      String tabtype=rs.getString("TABTYPE");
      getObjlist().add(new storeTables(tname,tabtype));
              }
      
    4. 创建一个 DATAMODEL 并将列表推送到数据模型

      private DataModel selectableItems= new ListDataModel(objlist);
      
    5. 将扭曲的数据放到另一个列表中。

      List<storeTables> items= (List<storeTables>)selectableItems.getWrappedData();
      
    6. 终于!!!打印数据。

      for(storeTables item:items){
      System.out.println(item.getTablename());
          }
      

    多田!!!!它只会打印表名而不是标签类型;) 相信java中没有不可能的事 有什么疑问!!

    【讨论】:

      【解决方案9】:

      不,你不能。您必须遍历整个列表并从每个对象中获取每个“field1”值。

      【讨论】:

        【解决方案10】:

        也许你可以用这种方式创建一个 hashmap:

        HashMap<String, ArrayList<Object>> map;
        

        关键是字段。 这样做时,当您要求 HashMap 检索与您想要的字段对应的对象时,映射将返回一个 ArrayList,其中包含具有该字段的所有元素。

        【讨论】:

          【解决方案11】:

          没有迭代这是不可能的。你可以以某种方式减少迭代,但这是必须的。

          【讨论】:

            【解决方案12】:

            在不同列表中添加记录的值,并使用一两个迭代器简单地打印 单独的值。如下所示:

            rs=st.executeQuery("select * from stu");
            List data=new ArrayList();
            List data1=new ArrayList();
            while(rs.next())
            {
               data.add(rs.getString(1));
               data1.add(rs.getString(2));
            }
            Iterator it=data.iterator();
            Iterator it1=data1.iterator();
            while(it1.hasNext())
            {
               System.out.println(" "+it.next()+"  "+it1.next());
            }
            

            【讨论】:

            • 为什么你的解决方案里面有SQL??
            猜你喜欢
            • 2021-02-18
            • 1970-01-01
            • 2022-08-15
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-05-31
            相关资源
            最近更新 更多