【问题标题】:Java method: Finding object in array list given a known attribute valueJava方法:在给定已知属性值的数组列表中查找对象
【发布时间】:2010-10-18 20:38:07
【问题描述】:

其实我有几个问题。

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

【问题讨论】:

  • getSize() 和 al 是如何定义的?

标签: java arraylist


【解决方案1】:

假设您已经正确地为 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();
}

【讨论】:

    【解决方案2】:

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

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

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

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

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

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

    编辑:这是为什么不修改的??

    来自 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 创建 Dog。

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

    【讨论】:

    • 我认为这不是他正在寻找的答案。关于循环没有做它应该做的事情,你是对的,但这不是重点。而且我真的不明白你所说的“更新”(我假设你的意思是创建一个新实例)是什么意思,他甚至不想或不需要这样做。
    • 关于 Jorn 的 cmets..我想说这就是他正在寻找的答案。作者说不行,tpdi解释了为什么不行。您关于创建新狗实例的评论令人费解。 tpdi 正在解释,就像你说的那样没有必要
    • 关于不必创建 Dog 的新实例的另一个问题。如果我只是从数组列表中取出对象的副本,那么如何在没有放入对象的情况下从数组列表中取出它?我刚刚也注意到我没有将 while 循环括起来。
    • 正如 tpdi 的编辑所解释的(非常详细),你只需要一个 variable 来放入 Dog,你不需要一个 object . (只是想我会简洁地表达)
    【解决方案3】:

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

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

    【讨论】:

    • 我们可以使用 Collections.binarySearch()。 binarySearch 或 Map 按性能哪个更好?
    【解决方案4】:

    你必须遍历整个数组,没有改变。但是,您可以轻松一点

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

    【讨论】:

    • 你不需要遍历列表。
    • Jon 是对的,但无论如何 +1 是为了提供仍然使用列表(而不是地图)的最佳/最干净的解决方案。
    • 您仍然必须首先遍历列表才能创建地图。这是否更有效取决于几个因素,包括列表更改的频率以及您需要查找的频率。
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

      如果你必须得到一个不是 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());
      }
      });
      

      【讨论】:

        【解决方案7】:

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

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

        【讨论】:

          【解决方案8】:
          List<YourClass> list = ArrayList<YourClass>();
          
          
          List<String> userNames = list.stream().map(m -> m.getUserName()).collect(Collectors.toList());
          

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

          【讨论】:

            猜你喜欢
            • 2011-05-09
            • 1970-01-01
            • 2013-02-13
            • 2012-03-08
            • 1970-01-01
            • 2016-04-03
            • 2012-09-24
            • 1970-01-01
            • 2022-07-08
            相关资源
            最近更新 更多