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

实际上我有几个问题。

我有一个类Dog与以下实例字段:

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

我还有一个类Archive ,它可以实例化Dog并将Dog对象放入ArrayList中。

我试图在Archive中编写一个方法,它接受一个整数作为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的对象。 其次,这似乎是一个非常耗时的过程。 有没有办法加快这个速度?

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

你没有一个区块,所以你的表情结束了表达式dog=al.get(i);

 while(dog.getId()!=id && i 

之后的一切都只发生过一次。

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

如果需要获取键的值,则应使用Map,而不是Array。

编辑:这是为什么?

OP的评论:

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

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

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

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

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

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

(实际上你不需要创建一个引用,因为你真正应该做的是返回一个Map从其get()函数返回的内容。如果Map没有在Dog上进行参数化,就像这样: Map ,那么你需要从get中转换返回,但是你不需要引用: return (Dog) map.get(id);或者如果参数化了Map,则return map.get(id) 。一行是你的整个函数,并且它比在大多数情况下迭代数组更快。)

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

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

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

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

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

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

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

 // Populate the map Map dogs = new HashMap(); for( Dog dog : /* dog source */ ) { dogs.put( dog.getId(), dog ); } // Perform a lookup Dog dog = dogs.get( id ); 

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

你必须遍历整个数组,没有改变。 但是,您可以更容易地做到这一点

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

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

我应该使用迭代器。 不幸的是, java.util.Iterator在get方法java.util.Iterator有副作用。 (由于其exception分支,我不喜欢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 )。

如果必须获取不是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()); } }); 

我用java 8 lambdas解决了这个问题

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