在 ArrayList 中搜索对象

Posted

技术标签:

【中文标题】在 ArrayList 中搜索对象【英文标题】:Searching For Object In ArrayList 【发布时间】:2012-02-28 12:25:59 【问题描述】:

我是 Java 的相对新手,正在为我的编程课程构建一个应用程序,用户可以在其中输入、删除、搜索或编辑学生列表(一个由整数 ID 号、字符串姓氏组成的对象,和双倍GPA)。到目前为止,我已经成功地允许用户输入、删除或编辑条目。现在,100 多名学生的列表在一个 ArrayList 中(每个学生都是一个带有三个参数的学生对象)。

我的问题是我不知道一种有效的方法来允许用户根据 ID 号、姓氏或 GPA(以及将来可能的 GPA 范围)搜索特定学生。我一直在研究 Big-O 表示法并想使用二分搜索,因为如果学生列表继续增长,这将是一个很好的实践,也是一个更好的选择。到目前为止,对于二分搜索,我们正在使用带有 while 循环的 high/low/mid 方法(如果这表明我当前的技能水平)。

所以这是我的问题:

根据此条件搜索特定学生的有效方法是什么?我不是在寻找代码或解决我的困境的答案。我真的在寻找 Java 程序员用来做这种事情的技术或技巧的名称,特别是如果这些技术可以与其他面向对象的编程语言一起使用。

【问题讨论】:

您是根据完全匹配还是类似匹配(即:'Gar' 匹配 'Garcia')进行搜索? 最好是准确地搜索名称(忽略大小写)。对 ID 号的搜索必须准确。对于 GPA,我希望它返回所有适用的内容,但前提是它对我的技能水平来说并不过分复杂。 【参考方案1】:

我建议查看HashMap 数据结构。它允许您将元素存储在映射到某个键的集合中,以便以后可以通过相同的键检索它们。在您的示例中,您可以使用不同的哈希映射来搜索不同的字段。哈希映射的键是字段(即:ID、姓氏或 GPA),值是相应的学生。对于不区分大小写的搜索,请确保在保存对象和检索对象之前将键(姓氏)转换为小写。

存储ID:

Map<String, Student> idToStudent;

键:“23213233”,值:“某位学生”;

要考虑重复名称或 gpa,然后使用以下类型的映射:

Map<String, List<Student>> lastNameToStudents;

键:“smith”,值:[“John Smith”、“Bob Smith”等]

Map<Double, List<Student>> gpaToStudents:

键:“2.4”,值:[“学生 1”、“学生 2”等];

注意,为了简洁起见,我使用了学生姓名的字符串表示,但实际上这些表示Student 实例。

【讨论】:

很好的信息。绝对是我会调查的。谢谢。 投票。但是重名和GPA会是个问题。 @ggreiner 没有重复的问题,我不能给你的答案投票两次:) 顺便说一句:Guava Multimap 是为这样的结构设计的:'Multimap mMap = ArrayListMultimap.create();'跨度> 【参考方案2】:

对于二分搜索,您需要按照搜索条件对列表进行排序。

您可以维护三个不同的列表,按您需要的三个不同标准排序:

ArrayList<Student> sortedByName;
ArrayList<Student> sortedByGpa;
ArrayList<Student> sortedById;

public List<Student> getStudentByName(String name)
    return binarySearchOnName(sortedByName, name);

public List<Student> getStudentByGpa(double gpa)
    return binarySearchOnGpa(sortedByGpa, gpa);

...

请注意,当您在 ArrayList 中插入一个新 Student 时,您还应该将该 Student 插入到另一个已排序的 ArrayList 中的正确位置。 (当然,您可以使用二分搜索来执行此操作。)

【讨论】:

【参考方案3】:

您需要重复还是任意排序?如果没有,请考虑使用 TreeSet 或 HashSet。 TreeSet 提供 O(log(n)) 访问/插入/删除并自动对其元素进行排序(因此迭代它会按排序顺序获取元素。HashSet 提供分期 O(1) 访问/插入/删除,但迭代发生在一些随机顺序(您无法控制)。其中一种听起来是实现目标的最简单方法。

如果您需要按成员搜索,您可能需要一个 HashMap/TreeMap,只要您不介意只获得完全匹配。如果您需要按成员搜索多个成员,您可能需要多个映射,每个要搜索的键一个映射。如果您要使用 ArrayLists 并且想要比 O(n) 查找更好,那么无论如何您都需要多个列表(每个查找参数 1 个列表,按该参数排序)。

最后,O(n) 查找将是迄今为止最简单的。只要列表不会变得太大(想想 10000 个元素而不是 100 个元素),性能就会很好,并且这种方法可以最大限度地减少存储开销。您不希望过早优化——在性能成为问题之前,最好的技术就是遍历列表。

【讨论】:

其实我还没有研究过HashSets或者TreeSets,但是谢谢你的建议。我一定会调查这些。谢谢!【参考方案4】:

所以我看到一个普遍问题的三个问题。一般问题是您希望能够根据某些标准进行搜索,这三个问题是每个问题的具体情况。因此,唯一保证唯一的字段是学生 ID 字段。这允许我们覆盖 equals + hashcode 方法。我们希望这是我们用来确定相等性的值(至少在这个非常简单的示例中)。通过这样做,您将通过以下调用公开一种无需排序即可在列表中进行搜索的机制:

list.contains(Student);

现在就名称而言,由于不能保证这个值是唯一的,我们想做类似的事情:

 matchedStudents = new ArrayList<Student>();
    for(Student currentStudent : studentList)  
      
        if(currentStudent.getName().equalsIgnoreCase(searchString)  
        
              matchedStudents.add(currentStudent);  
        
      
      return matchedStudents;

就 GPA 而言,我们基本上会重复上面的循环,只是使用 where GPA == searchCriteria 调用。

话虽如此,应该可以编写这样的函数:

function searchStudent(String searchString, String type)  
    
    //type is one of: GPA, name, ID  
    switch(type)  
      
       case gpa:  
       case id:  
       case name:
      
  

这应该让你开始。

【讨论】:

以上是关于在 ArrayList 中搜索对象的主要内容,如果未能解决你的问题,请参考以下文章

在Java中怎么修改ArrayList()中元素的值?

Dom4j解析语音数据XML文档(注意ArrayList多次添加对象,会导致覆盖之前的对象)

源码面经Java源码系列-ArrayList与LinkedList

在 ArrayList 中搜索对象

将 ArrayList 转换为 Array - 不确定如何使用它 [重复]

JDK1.8源码学习-ArrayList