在服装类的ArrayList上使用Map有什么好处

时间:2019-05-11 20:01:00

标签: java hashmap maps

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

到目前为止,我学会了根据以下规则选择何时使用LinkedList,ArrayList或Array []:“如果真的很简单,则使用Array [];如果更复杂,则使用ArrayList(例如,保存某些类),并且如果列表是动态的,并且内部有很多对象,这些对象需要根据在中间删除或添加新对象或在列表内来回移动来更改顺序,则可以使用LinkedList。

但是我不知道何时选择HashMap或HashSet,如果您能向我解释一下,我将非常高兴。

4 个答案:

答案 0 :(得分:1)

让我在这里帮助您...

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

因此,像上面提到的那样,Hashed集合使用此数字将其用作数组索引以立即查找元素。但是显然太大了,无法将其用作可能的小型数组的数组索引。因此,他们需要减少该数量,使其适合数组大小的范围。 (HashMap和HashSet使用数组存储它们的元素)。

他们用来减少该数量的操作称为哈希,它类似于:Math.abs(-2079637766 % arrayLength);。 并非完全如此,它有点复杂,但这是为了简化。 假设arrayLength = 16; %运算符会将那个大数字减少为小于16的数字,以便可以将其放入数组中。

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

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

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

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

ArrayList和LinkedList之间的主要区别:

如果要在大小为1000的ArrayList中的第500位找到该元素,请执行:list.get(500);,它会一步完成,因为ArrayList是用数组实现的,因此500,它直接进入元素在数组中的位置。 但是,LinkedList不是用数组实现的,而是用指向彼此的对象实现的。这样,他们需要线性地从0开始一个接一个地计数直到到达500,这与ArrayList的单步执行相比效率并不高。 但是,当您需要在ArrayList中添加和删除元素时,有时需要重新创建Array,以便将更多元素放入其中,从而增加了开销。 但这对于LinkedList不会发生,因为不必重新创建数组,因此只需要重新引用对象(节点),只需一步即可完成。

因此,当您不打算在结构上删除或添加很多元素时,使用ArrayList很好,但是您将从其中了解很多内容。 如果要添加和删除很多元素,那么链接列表会更好,因为与这些操作无关。

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

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

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

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

答案 1 :(得分:0)

第一个HashSet。在HashSet中,您可以轻松获取它是否包含给定元素。让我们在班上有一组人,您想问一下班上是否有男生。您可以创建一个字符串数组列表。而且,如果您想询问某个人是否在班级中,则必须遍历整个列表,直到找到他为止,这对于较长的列表而言可能太慢了。如果改用HashSet,则操作要快得多。您可以计算搜索到的字符串的哈希值,然后直接进入哈希值,因此无需传递太多元素即可回答您的问题。好吧,您也可以通过一种变通方法来使ArrayList更快地为此目的而访问,但这已经准备就绪。

现在是HashMap。现在,假设您还想存储每个人的分数。因此,现在您可以使用HashMap。输入名称即可在短时间内得到他的分数,而无需遍历整个数据结构。

这有意义吗?

答案 2 :(得分:0)

关于您的问题:

  

”但是我不知道何时选择HashMap或HashSet,而我   如果能向我解释的话,我将非常高兴。”

HashMap实现了Map接口,可用于在恒定时间内将键(K)映射到值(V),并且顺序无关紧要,因此如果您现在就可以高效地放置和检索这些数据密钥。

并且HashSet实现Set接口,但是内部使用和HashMap,它的作用是用作Set,这意味着您不应该检索元素,只需检查是否在set中即可(通常)。

在HashMap中,您可以具有相同的值,而在Set中则不能(因为它是Set的属性)。

关于这个问题:

  

如果好处是此Hash对象只能包含1个同名对象,那么>在添加它之前让ArrayList检查该类型是否不是更容易吗?

在处理收集时,您可以根据数据表示形式来选择特定的数据,还可以基于想要访问和存储这些数据的方式,如何访问它?您需要对它们进行排序吗?因为每个实现可能具有不同的复杂性(https://en.wikipedia.org/wiki/Time_complexity),所以它变得很重要。

使用文档

对于ArrayList:

  

加法运算以固定的固定时间运行,即,添加n个元素需要O(n)时间。所有其他操作都是线性运行的(大致而言)。

对于HashMap:

  

该实现为基本操作(获取和放置)提供了恒定时间的性能,假设哈希函数将元素正确分散在存储桶中。集合视图上的迭代所需的时间与HashMap实例的“容量”(存储桶数)及其大小(键-值映射数)成正比。因此,如果迭代性能很重要,那么不要将初始容量设置得太高(或负载因子太低)是非常重要的。

这是关于时间的复杂性。

对于某些问题,您可以选择更多不典型的收藏:)。

答案 3 :(得分:0)

这与Java无关,并且选择主要取决于性能要求,但是必须强调一个基本差异。从概念上讲,列表是保持插入顺序并可能具有重复项的集合类型,集合更像是没有特定顺序且没有重复项的物品袋。当然,不同的实现可能会找到解决方法(例如TreeSet)。

首先,让我们检查ArrayList和LinkedList之间的区别。链表是一组节点,其中每个节点包含一个值以及到下一个和上一个节点的链接。这使得将元素插入到链表中成为了将节点追加到列表末尾的问题,这是快速的操作,因为只要节点保持对下一个节点的引用,内存就不必是连续的。另一方面,访问特定元素需要遍历整个列表,直到找到它为止。

顾名思义,数组列表包装一个数组。通过使用其索引访问数组中的元素是直接访问,但是插入元素意味着调整数组的大小以包括新元素,因此它所占用的内存是连续的,在这种情况下,写操作会更重。

HashMap就像字典一样,其中每个键都有一个值。插入的行为将主要取决于如何实现用作键的对象的hashCode和equals函数。如果两个键的hashCode相同,则存在哈希冲突,因此将使用equals来了解它是否是相同的键。如果equals是相同的,则它是相同的键,因此将替换该值。如果不是,则将新值添加到集合中。访问和写入值主要取决于计算键的哈希值,然后直接访问该值,这使得这两个操作都非常快,O(1)。

一个集合几乎就像一个哈希映射,没有“值”部分,因此,它遵循与实现附加值的hashCodeequals操作相同的规则。

研究一下Big-O表示法和算法的复杂性可能很方便。如果您从Java开始,我强烈推荐这本书Effective Java, by Joshua Bloch

希望它可以帮助您进一步挖掘。

相关问题