53

假设我有一个包含两个字段的对象列表,field1并且field2都是 String 类型。

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

4

12 回答 12

81

幸运的是,您可以使用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
于 2017-02-07T14:41:35.147 回答
9

尝试这个:

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

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

于 2012-06-12T13:01:24.363 回答
8

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

注意:无论如何,您都会找到一个可以执行您想要的操作的 API,它仍然在内部循环。

于 2012-06-12T13:02:01.743 回答
5

要看...

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

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

具体来说,你的意思是:

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

有关解决方案和选项,请参见下文。


使用便利库

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

例如:

  • 谷歌番石榴

    Set<String> strings = buildSetStrings();  
    Collection<String> filteredStrings =
        Collections2.filter(strings, Predicates.containsPattern("^J"));  
    
  • 函数式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);
    
  • 拉姆达J

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

完美访问

如果是第二个,这是不可能的除非您从一开始就构建所有内容,以便您的对象应由自定义集合类管理,该集合类将根据插入时的字段值索引您的对象

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

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

于 2012-06-12T13:07:11.307 回答
4

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

于 2012-06-12T13:02:11.807 回答
2

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

您可以在List<String>不创建充实的情况下实现接口ArrayList<String>,因此不会迭代父对象。

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<String>的随机访问将与对底层的随机访问一样昂贵List<Entity>;如果您使用的List<Entity>是不提供快速随机访问的实现,您可能需要跳过几个环节(即实现更多的方法List<String>。但这应该适用于您需要轻量级的 99% 的情况适配器。

于 2014-01-21T17:27:09.570 回答
2

让对象属于以下类。

public class Bike {

    String bikeModel;
    Int price;
}

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

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

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

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

于 2018-10-29T11:45:10.703 回答
1

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

  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中没有什么是不可能的任何疑问!

于 2013-04-05T12:50:49.197 回答
0

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

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());
}
于 2013-03-12T03:37:09.507 回答
0

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

于 2012-06-12T13:01:15.370 回答
0

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

于 2012-06-12T13:06:58.820 回答
0

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

HashMap<String, ArrayList<Object>> map;

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

于 2012-06-12T13:04:14.383 回答