【问题标题】:Java - Get an object from a list?Java - 从列表中获取对象?
【发布时间】:2014-09-28 01:24:33
【问题描述】:

大家好,

在开发我的项目时,我实现了这样一个场景。我有一个 LinkedList,其中包含 100 Student 类对象。

我的情况是,

我需要从LinkedList 获取一个特定的Student 类对象,其中rollNo = 98。所以我使用了下面的代码。

我的 pojo 课是,

public class Student {

    private int rollNo ;
    private String name;

    // getters & setters        
}

我的主要课程是,

public class MainClass {

    List<Student> list = null;

    public MainClass(){
        list = new LinkedList<Student>();
    }

    public static void main(String... args){
        MainClass mainClass = new MainClass();
        mainClass.addStudentDetail();
        Student obj = mainClass.getStudentObject(98);

        // other Stuff with obj
    }

    private void addStudentDetail() {
        for(int i=1; i<=100; i++){
            Student obj = new Student();
            obj.setRollNo(i);
            obj.setName("Name"+i);
            list.add(obj);
        }

    }

    private Student getStudentObject(int rollNo) {
        int listLength = list.size();

        for(int i=0; i<listLength; i++){

            if(list.get(i).getRollNo() == rollNo)
                return list.get(i);

        }
        return null;
    }
}

获取 rollNo = 98 的 Student 对象,

这需要98 iterations

如果我的列表大小是 1,00,000 并且我需要 rollNo = 99,999Student 对象会发生什么?

有没有其他简单的方法可以使用where condition 获取一个特定对象?

注意:

抱歉,我不需要使用 HashMaprollNo 作为键。我的需要是只使用LinkedList。这是我的要求。

希望我能在这里得到一个好的解决方案。

【问题讨论】:

  • 如果卷号是唯一的,使用地图。
  • 你应该阅读Map
  • When to use LinkedList<> over ArrayList<>?。同样作为suggested by injecteer,如果将来您想根据元素的属性之一对元素下注,则可以使用 Map。
  • 如果您的列表是按 rollNo 排序的(就像代码中一样),您可以使用二进制搜索方法在 O(LogN) 时间内获取结果
  • @DownVoter 请不要犹豫写 cmets。

标签: java performance list collections


【解决方案1】:

使用MaprollNo 作为键,Student 实例作为值。这是“索引”此类集合以使查找尽可能高效的常用方法

更新:

如果你想快速搜索,你需要某种索引。看看 Java 的 HashMap 源代码,看看键是如何存储的,这可能会让你对你的情况有所了解

【讨论】:

  • 如果以后需要学生的集合,可以使用 Map.values()
  • 感谢您的回复。请查看我更新的问题。我不需要 Hashmap 的实现。
  • errrm,你为什么不能使用Map?你可以把它隐藏在你的班级深处,这样别人就看不到它了:)
  • @HumanBeing 在这种情况下你想要ArrayList。另外list.get(i).getRollNo() == rollNo) return list.get(i); 效率非常低,因为每次使用get(i) 时都是从头开始迭代。您应该改用Iterator,这样for-each loop 将改进您的原始代码。
  • 如果你想快速搜索,你需要某种index。看看 Java 的 HashMap 源代码,看看密钥是如何存储的,这可能会让您对自己的情况有所了解
【解决方案2】:

如果您使用 LinkedList,确实需要 98 次迭代才能找到学生。您可以使用地图,并通过键找到学生。键可以是rollNo,值可以是学生本身。

【讨论】:

  • 感谢您的回复。请查看我更新的问题。我不需要 Hashmap 的实现。
  • 如果rollNo始终是每个Student的索引,如果你调用“get”方法,并且使用ArrayList而不是LinkedList,则搜索速度更快,因为ArrayList中只是块的总和,并且与LinkedList 在内部迭代所有项目。
【解决方案3】:

由于 map 超出了等式并且您需要使用 list,因此您最终会得到这么多的迭代。或者,您可以对列表进行排序并实现类似于逻辑的二进制搜索以获得更好的性能。但是,如果您对 List 不是很讲究,可以使用 HashSet。它在大多数方面类似于列表,但只能包含唯一值。文档说这个类为基本操作(添加、删除、包含和大小)提供了恒定的时间性能。但是为此,您需要覆盖关于rollNo 的equals 和hashCode 方法。示例

public class Student {

    private int rollNo;
    private String name;

    //Getters & Setters.

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + rollNo;
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Student other = (Student) obj;
        if (rollNo != other.rollNo)
            return false;
        return true;
    }
}

【讨论】:

    【解决方案4】:

    好吧,如果您需要使用LinkedList,我唯一想到的为了提高性能,就是将列表的大小除以2,并检查您要查找的索引是否为在上限或下限。

    如果它在上半部分,则使用descendingIterator() 以相反的顺序遍历集合。如果在下半部分,则使用普通的iterator。它不会提高它的性能,但至少当你想要一个上半部分的对象时,你会节省不少迭代。毕竟是双链表。

    但是,我会使用Map 的实现,但由于您不能这样做,请尝试我解释的内容。

    【讨论】:

      【解决方案5】:

      看起来 roll no 是独一无二的。为什么不将它用作链表本身的索引呢?将 98 卷无对象放入链表的第 98 个索引中。

      顺便说一下hashmap也不错。虽然如果 roll no 是唯一的,那么上述方式会更快且内存成本更低。

      【讨论】:

        【解决方案6】:

        在java中有两种最常用的List,分别是ArrayListLinkedList。两者都保留数据插入列表的顺序。所以在这种情况下没有其他办法,你必须一个一个地迭代。

        替代解决方案是设置。常用的 Set 有 3 种,分别是 TreeSet、HashSet 和 LinkedHashSet。

        LinkedHashSet:保持数据插入集合的顺序。 (在你的情况下没有用)。搜索将在 O(n) 中进行。

        TreeSet :内部维护红黑树中的数据。所以搜索会很快。搜索时间为 O(log n)。

        HashSet:内部维护HashMap中的数据。所以搜索会比 TreeSet 快得多。搜索将在 O(1) 内完成。

        【讨论】:

          【解决方案7】:

          由于您目前通过一个简单的 for 循环添加学生,因此所有学生都很好且整齐地排序。所以你可以说,位置 x 的学生的卷号为 x+1。

          但现在想象一下,有些学生会离开,有些学生会来。一段时间后,“x+1”不再起作用。也许甚至连这个序列都不再是真的了。所以不再是 1、2、3,而是 2、3、1?

          对具有 >100 万个条目的集合进行迭代、排序等是无用的。

          您需要做的是:使用数据库! 如果你不能使用基于服务器的数据库,你也可以使用 SQLite 或类似的。

          然后,您可以通过简单的 SQL 语句访问卷号为 99(或 943,251)的学生记录,并将结果解析为 Student 实例。

          在我看来,这是保持代码相对简单和小巧的最快方式。

          【讨论】:

            【解决方案8】:

            如果您仅限于LinkedList,那么您可以通过不在循环中使用get(i) 来升级您的代码。这种方法效率非常低,因为如

            中所述

            Why would iterating over a List be faster than indexing through it?

            每个get(i) 从列表开始迭代直到i-th 元素,如

            get(0) - item0
            获取(1) - item0 -&gt; item1
            获取(2) - item0 -&gt; item1 -&gt; item2
            获取(3) - item0 -&gt; item1 -&gt; item2 -&gt; item3

            您应该使用Iterator 及其next 方法而不是这种方法来根据前一个元素获取下一个元素。类似代码

            Iterator<Student> it = list.iterator();
            while ( it.hasNext() ){
                Student s = i.next();
                //handle s here - test it or something
            }
            

            会这样迭代

             next       next       next       next 
              ->  item0  ->  item1  ->  item2  ->  item3
            

            而不是

            get(0)    get(1)            get(2)                    get(3)
              -> item0  -> (item0->item1) -> (item0->item1->item2) -> (item0->item1->item2->item3)
            

            上面的BTW代码也可以使用for-each looplike来编写

            for (Student s : list){
                //handle s here
            }
            

            所以改变你的

            for(int i=0; i<listLength; i++){
                if(list.get(i).getRollNo() == rollNo)
                    return list.get(i);
            

            for(String s : list)
                if (s.getRollNo() == rollNo)
                    return s;
            

            如果您确定列表是有序的,那么您可以通过使用ListIteratot 及其previous 方法从其末尾迭代来提高更接近列表大小的元素的搜索性能

            ListIterator<String> it = list.listIterator(list.size());
            while(it.hasPrevious())
                System.out.println(it.previous());
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2020-09-11
              • 1970-01-01
              • 1970-01-01
              • 2019-05-08
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多