21

我实际上有几个问题。

我有一个带有以下实例字段的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的对象. 其次,这似乎是一个非常耗时的过程。有什么方法可以加快速度吗?

4

8 回答 8

44

假设您已经为 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();
}
于 2009-04-11T01:34:43.063 回答
16

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

你没有块,所以你的 while 以表达式结束dog=al.get(i);

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

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

没有理由养狗,因为你从来没有使用过你养的狗;您立即将数组中的 Dog 分配给您的狗引用。

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

编辑:这是 donwmodded 为什么?

来自OP的评论:

关于不必创建 Dog 的新实例的另一个问题。如果我只是从数组列表中取出对象的副本,那么如何在没有放入对象的情况下从数组列表中取出它?我也注意到我没有将while循环括起来。

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

结果是,Dog dog;或者Dog dog = null给你一个不指向任何对象的引用。new Dog() 创建一个可以指向的对象。

紧随其后的dog = al.get(i)是引用现在指向由返回的狗引用al.get(i)。理解,在 Java 中,永远不会返回对象,只会返回对对象的引用(这是对象在内存中的地址)。

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

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

(实际上,您不需要创建引用,因为您真正应该做的是返回 Map 从其 get() 函数返回的内容。如果 Map 未在 Dog 上参数化,例如:Map<Dog>,那么您' 将需要从 get 转换返回值,但您不需要引用:return (Dog) map.get(id);或者如果 Map 是参数化的,则return map.get(id). 一行就是您的整个函数,在大多数情况下它会比迭代数组更快。 )

于 2009-04-10T23:57:44.793 回答
16

为了提高操作的性能,如果您总是想通过某个唯一标识符查找对象,那么您可以考虑使用Map<Integer,Dog>. 这将通过键提供恒定时间查找。您仍然可以使用 map 遍历对象本身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 );

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

于 2009-04-11T00:21:37.357 回答
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);
  }
}
于 2009-04-11T00:00:10.747 回答
1

我很感兴趣地看到原始海报使用了一种避免提前退出的风格。单次入境;Single Exit (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)。

于 2009-04-11T00:51:54.383 回答
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());
}
});
于 2013-05-05T22:27:40.333 回答
0

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

int dogId = 2;

return dogList.stream().filter(dog-> dogId == dog.getId()).collect(Collectors.toList()).get(0);
于 2017-08-23T14:10:28.207 回答
0
List<YourClass> list = ArrayList<YourClass>();


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

输出:[“约翰”,“亚历克斯”]

于 2019-06-10T10:48:45.070 回答