Java方法:在给定已知属性值的数组列表中查找对象

时间:2009-04-10 23:54:46

标签: java arraylist

我实际上有几个问题。

我有一个 Dog 类,其中包含以下实例字段:

private int id;
private int id_mother;
private int id_father;
private String name="";
private String owner="";
private String bDate="";

我还有一个归档类可以实例化 Dog 并将Dog对象放入ArrayList。

我正在尝试在存档中编写一个方法,该方法将整数作为ID并查看ArrayList,并返回包含该ID的对象。

private Dog getDog(int id){
    Dog dog = new Dog();
    int length=getSize();
    int i=0;

    dog=al.get(i);
    i++;

    while(dog.getId()!=id && i<length)
        dog=al.get(i);
        i++;

    if(dog.getId()!=id)
        dog=null;
    return dog;
}//end getDog

此方法存在两个问题(我使用的其他方法)。首先它没有用,我看不出原因。我正在循环(可能)arraylist中的所有对象,因为在循环结束后,检查循环是否完成,因为它用完了要搜索的对象,或者因为它找到了具有给定ID的对象。其次,这似乎是一个非常耗时的过程。有没有办法加快这个速度?

8 个答案:

答案 0 :(得分:44)

假设您已经正确地为Dog编写了一个equals方法,根据Dog的id进行比较,返回列表中项目的最简单方法如下:

if (dogList.contains(dog)) {
   return dogList.get(dogList.indexOf(dog));
}

这比其他方法的性能密集程度要低。在这种情况下,您根本不需要循环。希望这会有所帮助。

P.S您可以使用Apache Commons Lang为Dog编写一个简单的equals方法,如下所示:

@Override
public boolean equals(Object obj) {     
   EqualsBuilder builder = new EqualsBuilder().append(this.getId(), obj.getId());               
   return builder.isEquals();
}

答案 1 :(得分:16)

while适用于while之后的表达式或块。

你没有阻止,所以你的时间以表达式dog=al.get(i);

结束
while(dog.getId()!=id && i<length)
                dog=al.get(i);

之后的所有事情只发生一次。

没有理由新建一只狗,因为你从来没有使用过新生的狗;你立即将数组中的狗分配给你的狗参考。

如果你需要获取一个键的值,你应该使用Map,而不是数组。

编辑:这是为什么?

OP的评论:

  

关于不必制作狗的新实例的另一个问题。如果我只是从数组列表中取出对象的副本,那么我怎么能从数组列表中取出它而没有我放置它的对象?我刚刚注意到我没有将while循环括起来。

Java引用及其引用的对象是不同的东西。它们非常像C ++引用和对象,尽管Java引用可以像C ++指针一样重新指向。

结果是Dog dog;Dog dog = null为您提供了指向无对象的引用。 new Dog() 创建可以指向的对象。

之后使用dog = al.get(i)表示引用现在指向al.get(i)返回的狗引用。理解,在Java中,永远不会返回对象,只引用对象(它们是内存中对象的地址)。

你新建的狗的指针/参考/地址现在已经丢失,因为没有代码引用它,因为所指对象被你从al.get()得到的指示物所取代。最终,Java垃圾收集器将销毁该对象;在C ++中,你已经“泄露”了记忆。

结果是你确实需要创建一个可以引用Dog的变量;你不需要用new创建一只狗。

(实际上你不需要创建引用,因为你真正应该做的是返回一个Map从其get()函数返回的内容。如果Map没有在Dog上进行参数化,就像这样: Map<Dog>,然后您需要从get转换回报,但您不需要引用:return (Dog) map.get(id);或者如果参数化了地图return map.get(id)。那一行是你的整个函数,对于大多数情况来说,它比迭代一个数组要快。)

答案 2 :(得分:16)

为了提高操作性能,如果您总是想要通过某个唯一标识符查找对象,那么您可以考虑使用Map<Integer,Dog>。这将按键提供恒定时间查找。您仍然可以使用地图values()迭代对象本身。

一个快速的代码片段,可以帮助您入门:

// Populate the map
Map<Integer,Dog> dogs = new HashMap<Integer,Dog>();
for( Dog dog : /* dog source */ ) {
    dogs.put( dog.getId(), dog );
}

// Perform a lookup
Dog dog = dogs.get( id );

如果您在列表中执行多个相同性质的查找,这将有助于加快速度。如果您只是进行一次查找,那么无论如何都会产生相同的循环开销。

答案 3 :(得分:13)

你必须遍历整个阵列,没有改变。但是,你可以做得更轻松

for (Dog dog : list) {
  if (dog.getId() == id) {
    return dog; //gotcha!
  }
}
return null; // dog not found.

或没有新的for循环

for (int i = 0; i < list.size(); i++) {
  if (list.get(i).getId() == id) {
    return list.get(i);
  }
}

答案 4 :(得分:1)

我有兴趣看到原始海报使用的风格避免了早期退出。单次入场;单出口(SESE)是一种有趣的风格,我还没有真正探索过。已经很晚了,我有一瓶苹果酒,所以我写了一个解决方案(没有经过测试),没有提前退出。

我应该使用迭代器。不幸的是java.util.Iterator在get方法中有副作用。 (由于其例外后果,我不喜欢Iterator设计。)

private Dog findDog(int id) {
    int i = 0;
    for (; i!=dogs.length() && dogs.get(i).getID()!=id; ++i) {
        ;
    }

    return i!=dogs.length() ? dogs.get(i) : null;
}

请注意i!=dogs.length()表达式的重复(可能已选择dogs.get(i).getID()!=id)。

答案 5 :(得分:0)

如果必须获取不是ID的属性。我会使用CollectionUtils

Dog someDog = new Dog();
Dog dog = CollectionUtils(dogList, new Predicate() {

@Override
public boolean evaluate(Object o)
{
    Dog d = (Dog)o;
    return someDog.getName().equals(d.getName());
}
});

答案 6 :(得分:0)

我使用java 8 lambdas

解决了这个问题
int dogId = 2;

return dogList.stream().filter(dog-> dogId == dog.getId()).collect(Collectors.toList()).get(0);

答案 7 :(得分:0)

List<YourClass> list = ArrayList<YourClass>();


List<String> userNames = list.stream().map(m -> m.getUserName()).collect(Collectors.toList());

输出: [“ John”,“ Alex”]