首页 > 解决方案 > 使用 Map over ArrayList 的服装类有什么好处

问题描述

我现在正在学习 Java,并且正在学习不同类型的集合,到目前为止,我了解了 LinkedList、ArrayList 和 Array[]。现在我已经介绍了 Hash 类型的集合,HashSet 和 HashMap,我不太明白为什么会有用,因为它们支持的命令列表非常有限,而且它们是按随机顺序排序的,我需要重写 equal 和 HashKey 方法以使其与类正常工作。现在,我不明白使用这些类型而不是服装类的 ArrayList 的麻烦的好处。我的意思是,Map 所做的是将 2 个对象连接为 1,但是创建一个包含这 2 个对象作为参数的类不是更好吗?并有吸气剂来修改和使用它们?如果好处是这个 Hash 对象只能包含 1 个同名对象,那么在添加之前让 ArrayList 检查该类型是否已经存在不是更容易吗?

到目前为止,我学会了选择何时使用 LinkedList、ArrayList 或 Array[] 的规则“如果真的很简单,请使用 Array[],如果有点复杂,请使用 ArrayList(例如保存某个类的集合),如果列表是动态的,其中有很多对象需要根据在中间删除或添加新对象来更改顺序,或者在列表中来回移动,则使用 LinkedList。

但是我不明白什么时候更喜欢 HashMap 或 HashSet,如果你能给我解释一下,我会很高兴的。

标签: javahashmapmaps

解决方案


让我在这里帮你...

散列集合是添加、搜索和删除数据最有效的方法,因为它们散列键(在 HashMap 中)或元素(在 HashSet 中)以在单个步骤中找到它们所属的位置。哈希的概念非常简单。这是将对象表示为可以作为其 id 工作的数字的过程。例如,如果您在 Java 中有一个类似 的字符串String name = "Jeremy";,并打印它的 hashcode: System.out.println(name.hashCode());,您将在那里看到一个大数字(-2079637766),它是使用该字符串对象值创建的(在此字符串对象中,它是字符),即这样,该数字可以用作该对象的 Id。

所以像上面提到的哈希集合,使用这个数字将它用作数组索引以立即查找元素。但显然太大而不能将其用作可能的小数组的数组索引。所以他们需要减少这个数字,使其适合数组大小的范围。(HashMap 和 HashSet 使用数组来存储它们的元素)。

他们用来减少该数字的操作称为散列,类似于以下内容:Math.abs(-2079637766 % arrayLength);. 不完全是这样,它有点复杂,但这是为了简化。假设 arrayLength = 16; % 运算符会将这个大数字减少到小于 16 的数字,以便它可以放入数组中。

这就是为什么 Hashed 集合不允许重复的原因,因为如果您尝试添加相同的对象或等效对象(例如具有相同字符的 2 个字符串),它将产生相同的哈希码并覆盖结果索引中的任何值.

在您的问题中,您提到如果您担心 ArrayList 中的重复项目,我们可以在插入之前检查该项目是否存在,因此我们不需要使用 HashSet。但这不是一个好主意,因为如果您list.contains(elem);在 ArrayList 中调用该方法,则需要逐个比较元素以查看它是否存在。如果您在 ArrayList 中有 100 万个元素,并且您检查一个元素是否存在,但它不存在,那么 ArrayList 迭代了超过 100 万个元素,这是不好的。但是使用 HashSet,它只会对对象进行哈希处理,然后直接转到它应该在数组中的位置并检查,只需 1 步,而不是 100 万。因此,您会看到 HashSet 与 ArrayList 相比有多么高效。

同样的情况发生在大小为 100 万的 HashMap 上,它只需要 1 个步骤来检查一个键是否存在,而不是 100 万。当您需要添加、查找和删除一个元素时也会发生同样的事情,使用散列集合它会在一个步骤中完成所有这些(恒定时间,不取决于地图的大小),但对于其他结构。

这就是为什么它非常有效且被广泛使用的原因。

ArrayList 和 LinkedList 的主要区别:

如果你想在大小为 1000 的 ArrayList 中的位置 500 处找到元素,你可以这样做:list.get(500);它会一步完成,因为 ArrayList 是用数组实现的,所以使用 500,它直接去元素所在的位置是在数组中。但是 LinkedList 不是用数组来实现的,而是用相互指向的对象来实现的。这样,它们需要线性地从 0 开始,一个接一个地计数,直到它们到达 500,这与 ArrayList 的 1 单步相比效率并不高。但是当您需要在 ArrayList 中添加和删除元素时,有时需要重新创建 Array 以使更多元素适合其中,从而增加开销。但 LinkedList 不会发生这种情况,因为不必重新创建数组,只需重新引用对象(节点),这一步完成。

因此,当您不会在结构上删除或添加大量元素时,ArrayList 是很好的,但您会从中读取很多内容。如果您要添加和删除大量元素,那么最好使用链表,因为它与这些操作有关的工作较少。

为什么要在HashMaps中使用用户自定义类的equals()、hashCode()方法,而在TreeMaps中使用这些对象时需要实现Comparable接口?

根据我之前提到的 HashMaps,有可能 2 个不同的对象产生相同的哈希,如果发生这种情况,Java 不会覆盖或删除前一个,但会将它们保持在同一个索引中。这就是为什么你需要实现 hashCode(),所以你要确保你的对象不会有一个可以很容易复制的非常简单的 hashCode。而之所以推荐重写equals()方法的原因是,如果发生冲突(2个或多个对象在一个HashMap中共享同一个hash),那你怎么区分它们呢?好吧,询问这 2 个对象的 equals() 方法是否相同。因此,如果您询问 map 是否包含某个键,并且在该索引中找到 3 个元素,它会询问这些元素的 equals() 方法,如果其 equals() 与传递的键相同,则返回那个。如果您没有正确覆盖 equals() 方法并指定要检查相等性的内容(如属性名称、年龄等),那么 HashMap 中会发生一些不需要的覆盖,您将不会喜欢它。

如果您创建自己的类,例如 Person,并具有 name、age、lastName 和 email 等属性,您可以在 equals() 方法中使用这些属性,并且如果传递了 2 个不同的对象但在您选择的属性中具有相同的值如果相等,则返回 true 表示它们相同,否则返回 false。与 String 类一样,如果您执行s1.equals(s2);ifs1 = new String("John");s2 = new String("John");,即使它们在 Java 堆内存中是不同的对象,String.equals 方法的实现也会使用字符来确定对象是否相等,并且在此示例中返回 true。

要将TreeMap与用户定义的类一起使用,您需要实现Comparable 接口,因为 TreeMap 将根据某些属性对对象进行比较和排序,您需要指定对象将根据哪些属性进行排序。你的物品会按年龄分类吗?按名字?凭身份证?或通过您想要的任何其他属性。然后,当您实现 Comparable 接口并覆盖compareTo(UserDefinedClass o)方法时,您执行您的逻辑,如果当前对象大于传递的 o 对象,则返回正数,如果它们相同则返回 0,如果它们相同则返回负数当前对象较小。这样,TreeMap 将知道如何根据返回的数字对它们进行排序。


推荐阅读