赞
踩
集合由两个接口派生而来,Collection:存放单一元素;Map:存放键值对。Collection下三个子接口:List、Set、Queue
List:有序
,按对象进入顺序
保存对象,可重复
,允许多个null元素对象,可以使用Iterator取出所有元素逐一遍历,还可以使用get(int index)获取指定下表的对象
Object[]
存储,适用于频繁的查找工作,线程不安全Object[]
存储,线程安全。Set:无序
,不可重复
,最多允许一个null元素对象,只能用Iterator接口取得所有元素,再逐一遍历
Iterator
遍历HashSet
得到的结果不确定。HashSet
的查找效率,内部使用双向链表维护元素插入顺序(FIFO)。HashSet
,HashSet
时间复杂度为O(1),TreeSet
为O(logN)Queue:先后顺序
,有序
,可重复
Object[]
实现二叉堆Object[]
+双指针Map :键值对存储,key无序
、不可重复
,value无序
、可重复
。每个键最多一个值
HashMap
: JDK1.8 之前 HashMap
由数组+链表组成的,数组是 HashMap
的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。JDK1.8 以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间LinkedHashMap
: LinkedHashMap
继承自 HashMap
,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap
在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。详细可以查看:《LinkedHashMap 源码详细分析(JDK1.8)》 (opens new window)Hashtable
: 数组+链表组成的,数组是 Hashtable
的主体,链表则是主要为了解决哈希冲突而存在的TreeMap
: 红黑树(自平衡的排序二叉树)主要根据集合的特点来选用,比如我们需要根据键值获取到元素值时就选用 Map
接口下的集合,需要排序时选择 TreeMap
,不需要排序时就选择 HashMap
,需要保证线程安全就选用 ConcurrentHashMap
。
当我们只需要存放元素值时,就选择实现Collection
接口的集合,需要保证元素唯一时选择实现 Set
接口的集合比如 TreeSet
或 HashSet
,不需要就选择实现 List
接口的比如 ArrayList
或 LinkedList
,然后再根据实现这些接口的集合的特点来选用。
使用数组存储对象有一定的弊端。数组一旦声明长度不可变、类型不可变、有序、可重复,特点单一。集合提高了数据存储的灵活性,还可以保存具有映射关系的数据。
Queue 接口 | 抛出异常 | 返回特殊值 |
---|---|---|
插入队尾 | add(E e) | offer(E e) |
删除队首 | remove() | poll() |
查询队首元素 | element() | peek() |
Deque 接口 | 抛出异常 | 返回特殊值 |
---|---|---|
插入队首 | addFirst(E e) | offerFirst(E e) |
插入队尾 | addLast(E e) | offerLast(E e) |
删除队首 | removeFirst() | pollFirst() |
删除队尾 | removeLast() | pollLast() |
查询队首元素 | getFirst() | peekFirst() |
查询队尾元素 | getLast() | peekLast() |
都实现了Deque接口,都具有队列功能
1.5中被引入,与Queue区别在于元素出队顺序与优先级有关
NULL
和no-comparable
的对象Comparator
作为构造参数,从而定义元素优先级先后。HashMap
中的tableSizeFor()
方法保证)。即M总会使用2的幂作为哈希表大小HashMap
中带有初始容量的构造函数:
public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); this.loadFactor = loadFactor; this.threshold = tableSizeFor(initialCapacity); } public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } /** * Returns a power of two size for the given target capacity. */ static final int tableSizeFor(int cap) { int n = cap - 1; n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; }
看过 HashSet
源码的话就应该知道:HashSet
底层就是基于 HashMap
实现的。(HashSet
的源码非常非常少,因为除了 clone()
、writeObject()
、readObject()
是 HashSet
自己不得不实现之外,其他方法都是直接调用 HashMap
中的方法。
HashMap | HashSet |
---|---|
实现了 Map 接口 | 实现 Set 接口 |
存储键值对 | 仅存储对象 |
调用 put() 向 map 中添加元素 | 调用 add() 方法向 Set 中添加元素 |
HashMap 使用键(Key)计算 hashcode | HashSet 使用成员对象来计算hashcode 值,对于两个对象来说 hashcode 可能相同,所以equals() 方法用来判断对象的相等性 |
都继承自AbstractMap
,T还实现了NavigableMap
接口和SortedMap
接口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e8Jdmcdp-1646738917817)(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABAAAAAGnBAMAAADbEjhBAAAAMFBMVEUrKytQS0B1dnZXVVBnZmNKjzyltsY4QENGcTw4UzSVoq6KlJx/hos/jN81ZIwnjKPf/jDKAAAga0lEQVR42uzUUQ2AMAwA0S4oQAMYgc2/pklY0s/2PQ2XCwAAAAAAAAAAAAAATsafM4MSxrtSnqCE67tTHKAIATQngOYE0JwAmhPAZt+OVduGojCOP8NXySCQl7hFSyelmbypZEqzdLigrqacQfJLaBDctUOhGV0XDW0XtcGLJlPQEPxQvbq6xihWAlmPzv8ZfnwcyfLEEwATTwBMPAEw8QTAxHMAtFIaI3mLBP4iEQBs6wGUypTiPJ+2CGkjANhmAQTKVowBWGFJtQBgmwVQKls6BiBHJAAYZwC4AXATEOhiAIDiXVUDl02TAK/+XDb3AoBTRwBauwmYKTUA0L7J2hp+RZTHCLMd0V4AMKoDUCqV3t2VPQCFEqf8LPqeRzWWdHigPULKD7QSAIxyANSnb8oEoHwE4Cb/bABEK/jVBiH9Q7QWAIzqAKgOwFcHIEWpgwAun+a0NQC8GIi2CClBmMUCgE9DAAVQzpTptABXtDcA4C8Wuw5AdwckAoBPBkAwWADMnAS3ALiOo/4IJAeAZAEYdbYAGC4AAWhr3GRNU8kCMGwAIB1bACCq0f4Aoh7AnAQAo45PAe4x8IkF8Hb1EYAcgbw6AXh2AQwAzx6BF1jmAoBRZ6+Cx2+Adv3urT0C1+8reRHEqQ4AlOsLurTWAQYLsMGSfpI9AiuivwKAURbATNk+4izvAOD2Av5Ddn/7GmH2ofodCwBGWQBB/0FIgafzrmEKKb6K5ddAThkATkBa4NkcAPkegFc9AASBBgTABDMAXtD8lwBg1ssAIJZPwpgl/wuYeAJg4v1n545VG4aBMI4LSrH9dlkO6rfQEsiYxVTdvAj8QnmvumpwoVRXNISIu/+PoAyfx/P5bAtTAM5RAM5RAM5RAM698J1A3wa+FIofk5rBvLyqGaxbrmoG4yaZ1QzGjSJqBuOyyKpkDAHWLSInNYNpk4jM9YwhwLpRRN7UDKZl2a3VjCHAukXkJqd6xhBg2yRf5lrGEGDdKEUtYwiwLkuxVjKGAOsWKU5KxusAwybZ3UTmvzOGAOtGuatnvA6wbEwpSdqpGUw7r0rGBGgfO4KcowM4Rwdwjg7gHB3AOTqAc9pZvlEA9nGWO8d13jmtAIYA85gBnDtzG+gbzwGc4zmAc3QA5zIdwDc6gDVbarKoHWBKu7I97CNcyrod6/W+pt33gdRLD5bUJiq1FMdSAJeybr8K4D2k3XWgAPqSY4hDw+8/8VjvR5clHmv547lhP9jG69yDGzGfFexdCo/Fq4POpRie5JUC6EEOjdhbYksKDegA9jQNgXQAe+gAzjEEOscQ6NzzOsBEAfQgB7iWYoBnLR2AS4BBLR2AIfCTnTu4YRAGgijKMVd7KSCiA3AKi5JmKBeuIHxYBDby/OcCuIwGs7ZokKsB+AxsD4MgcYyCxdEA4hgFi2MULI5XgLiKl0KZQT4B18LFcRYgjrMAcWcagB+ONYSzAHEMgsQxCBJHA4hjDyCOG0Hi5q6WFwG43n9MvpU+yZzrm3mkd63s3GJ4mRWH+727jWkojgBkxdHJ3Pp9AKy0iQBkxXA72wcglEYAqgagJwAPRgOI+4UDrTVAJAA0ANgDgAaAZAOwB1jYOWPfpoEojDPCeLFBFUFIoVGGtMvVUYdQqXITZWhZGCKVNVhvOHti6YaEUCX4AxgYaaMbAktp5IGylKIMwD+BGLq1A3vL9y5O0iRumiIQIO6p9fle7Lwo36/f3dlJrQPYmLwKwD/jF+lRqqSKOppY8YWQaOW0DuAcHX07jxhzVn4sOylk7xDHAvATDrBZr9fXRVoUiZopEsX7IxkkykqKYmtKB3COT09Pv4vUaIXY3FVyGCOdm6D/asDbuInDmhaASwMA/RFpHuCSIvLHNdVjAOyIRcqJWE3pAMunHF9Fak0G4OYoADQJgFqDtzoUGQrsHOCyAMzUuyHG4yblV+jVFABoBmDH1Wo6B5AJADKlZpYolwKAngIAJbMUWAe4LACbCQD3BdMwbK2REJ0AO6Wqz1vf5ba0otvVCvexNY00DhBkSCVZPhbp8xwgnwAwO15SZENtAPB6T17FtrpCH7lfFegi3O6D6EnuFCLeiSmHF5FksZXVqgVgagCSWUC9/mTkL+teHm+hplCi24ypIZ2YECGsFVnwgH6THSBsZCOFbEwKCha2Yto6xwGcdgLAB9YfJYega8T7DEART25qqANYDiEitCtEbUgec1aIJaItKcrEkRNa7xQY1zWTdfU8DrIATI5H3RHg+csXdaPD9fXNs2qUITtHhxRtA4CQNI/0Gt2wm23iIEIAgIaqbSkoj2wDD7ZwrJ/uALcAwMnG0enX9xIlnw2VFIXtzjYDkJQkIiUdjZIMAMWozFk0EugZ4cuKSDMAxQA/wuXeLhqNUywAFzrAdf7jv7aZAPBk5v7QeNyWrCE1nTiCNuogQw/hyrpdqQhHN71OyFj4ZQYg0IWPyhxbVEzH7BKyqQ5wt718svF44/jrns8lrw9KMmyvagEA4JLgKA6cu+Rj1KEDlBRwhVV044ZcQ3OLdt04EA6GgFJJCj0ftXBuWeU461IkiyQtAJMBSEaAl/VUAFxNb6XAG+2zGUBqQPAwmQSa7C0FNR4iAQAeFqiqhHPHFxmSosPqbac7wFp7+fDo9OT0697+GAB4qsUGADAl0cMUFM+YTAId2ub1ANdwkOC1Yi3qTwLjPEWLgcjcEQKNCxfIkG8BmB6A+6xGQkISPJx2p+RZSF1o4L0OBgDkvAw7L2SOGYCaKilWySsp3wDQCdIdYG53+ZAvBBgANodLuuSXFQMgTdm4Kc+uAthTUBw1PCQWQ+xVBquAWR0scknPA0Mu7fOvBWAaANaHAMDOgAAeXG+GEnJLiM/2PQBgb+8dA7CTALDUdiFd6VNLkzTiF4J0B5hrL2+c9gCYGS7pksyohDn8Fuh1XvYBcFjQzznhzrVa2K1Fw8vA3Nw+A7DSamkLwOUc4GpfhBEAxAJtdwFQPguKGACASAAwQ4AQLF0BSXaAYIIDYAjoAQAChhwgS3ufyWcAzO8SETUHDgBBOVaRxe7iKABC3GDvR3QBcCwAF68Crg/mAGkAiE4ksgwAVKkNA6Dy+XxVDhwAMimRoXZpgfyJDsBzgGMzB8iNlUzWFIkDQL13muSQA3D9rVJJjzlAnDODf1lVKjXrAFM7wIwZAjZTAeB7PLUoUWPMAZDh0Lt9ADIKWSncyQ5gVgGPjw6PAcCYA9TCL1/0Tn8OgDAzOZfOOIDLGQ0HCLGfH3UAs4iwDnCpZeDzay9SAYCCDECWfAAgRx2ANYFEurcKgBoMgOivAjrNdAdYwnWAw43HuA7gp5bEeQaAUDpVv6uiM3AAA4B5DWWzCB0CYDEwmBoH2GFsLAAXASDYAeqb2AjEzJAahbBSMkvqtleIEkvniNtVH9mmtxZKUYjkEvUdAOsCZ5UdQFUWzrsO4PauBL4dLwkvwWoDAOw6YMihtlygHAPQrsrEAZA9EGvYvUWzeHlI3Iyqlb4DLEayFDMADW/VXge45KVg9DbX18/MyJRmJ43pHQW8BOyBYVaHHdqjBo/arzX1HcClqEgEACjUlLvgXsBBt+R6/VmvpMvmUuZRR72jpqmh4T28g27iACIO5wgHulrFBrIszzuTOQA6b2PFANBrCu2VwOnvBkKD0XA6RJHEoM1vMABoJvkloobZUo41ozDe7wEgioCjBQAiTY3z7gZ6594NZNPmSWc27HDJ/ipA3EVrfD/pvGG5VyGy5BpxD4BywK9HFQEA8KQdC8BFq4C+BTwQKeHMfzAiLXypcK8vmCdlP4vGd5IHPbT3PnLTCRZw6uU/D+BxHQ8/7vxs98nzsvuAJ7FJXtbKB4ld1OIa5gwvORX90vwsGkwC7320dwMvdoDEAuD7vzIAwKRPBN3mC4EnUvzGAAD28wBTAQACnq0/Q/NbAEh3APH0+GRMfwvA9PH3fyq4MMEB0sMCMEX8OwBUK3/4ewFOVVoA7PcCLAD228H2U8HWAa7Y+J+/G2gBsA5g4392gB/s3DFKxUAUheHbTVIEnlrYu4XnQmwOKLgJbaaaciB2aWZtVkfeUuyFQCI+T0jO14cUubnzV+MGmHd7vrrHnwNw/m/eAPPeH65tCxdFnsJm5Pu1blY/cpp55Vp3b78V9nc+QqV4k29A/xwqvITJdZhCI/EzTK7hKTQGfoXJVbyERiEdAXI9gNAgHQF6HSCKgEQ6AvQaIIqAgXQE6FVAFAGFdATI9cArEAqkI0CvA0QRkEhHgF4DRBEwkI4AvQqIIqCQjgC5HlBFAOkI0OsAUQQk0hGg1wBRBAykI0CvAqIIKKQjYANyznXKWfAVUs7lknOY2hgqxQW4BePy398DsEfeAAc3TrGUB2CPWizmAdgjHwEH5yPg4LwBDu6b/TqgAQAAQBDWvzVB+EMwdQPnjMA5BZhTgDkFmFOAOQWYU4A5BZhTgDkFmFOAOQWYU4A5BZhTgDkFmFOAOQWYU4A5BZhTgDkFmFOAOQWYU4A5BZhTgDkFmFOA2Ld63qahKMoGjLYDgiYDcasiQZbXog6BAYcqSJSFoRKsIXqD7QEh/gBDpbDBgAQjSeTBydIPZWhZGpCHND+Kc59f7Xy8JHzHYB81duzY79z7fN55106TcmQOkHJkDpByZA6QcmQOkHJkDpByZA6QcmQOkHJkDpByZA6QcmQOkHJkDpByZA6QcmQOkHJkDpByZA6Qcvw/DvDiwsXlLJYQyG+k/PBLsSRHAC/NZeHm3w/kxjilbi4LN5IjAP3BkrA9IQB9+84fx0S/b/xaBj8f8PbNBAlAWxKMSQdg2p9GblIA2pJgZAJYigCMTAAJFoCeOUC6BfBXHCAhuecyAWQ1QFIE8FL7AfzjU0BWA2QOkAkgzQLIaoA/LgCW6Ckgc4CfEYDBlH15wuIjNjc3z/da0+dvRgf9mgMYLGxm5nGLKX+PAPTPbA4l+ycF0NjbffpaU2Fj0F1V7N7i+3GH+L4fpl3mLW0ShY74LO/73cUCoEB2ZwRyRoHc8n1QLMDdbrjy/eZ3OEBjd0buj/t4KZKv8tNpypCpPE65gRYq/WQLQNwFXNsFlB1veJzbClE/GhFAgXPOwm7gn7RJ5MPTVzh3FkwB8wPpiUDWOa9ri1C2pUi5u6AGmEsZcPDVv08AVUd+xGtjw6RIrSS/BtgVeKpNI8+7A76vsMEhG3l/JpPMYe8kVkIBbAwHzgIHmBtIgXfOEMjjYe+7BVAYDtwFDjCXMsDlW28rpr8DhSOGyd0bBqMCuIqIe5wl2gFIAFd2AfUwKDvMWNzlepykygHkm0UCWBSI1ntOgwyLxQKQxuwurAH2ZlMG/BMJQA21AMA97gCtHP8HBIBOiIfBHpYxqjW8mgh54B9hc2d/xz+kYu8ERaDcGwmA9lq0ItBw8DsWOcBjvzkigA3fX1VPAQsDedSkN3U5wXcYlkeIhxq7RRzYFpRlUB5JAUjKotoB5lIGfl0IIHdMrRUo450jTafkAP24s9GlLhGUW+4ODpICiHK/ymu6YzMRnjj7kf8lgQKgWbBB1RfSx8be6GCouuLmjuZefkqXAWtL0+WsX8X6MBYAfUYrAhMHOQwO4OHUSAA5j/aqHYACeUqB7FIgT8dG5XpN3gdgVpZ1B2nT4dy2IA0KJNyLgMtEWYwEEFPemJP7NGXgu0IAgci9gNzgQVHFgx7p2EhPBmJzUEYC0CkQkoVTruHoMjbbOIWiZckUwJvL70MfvPL62pPRGRxZAYbnlrwaZd28jXktF876Bm+anhsJwCgN6WjDNM2BzdAn5i1+ihbcEq9HAsjzA7SgFMDcQCAAIHKAKu8HuPJbiIeufM+hQIhygBDKklIKYIUfIBClA0xSXhmlDLq2BQHovFny6kj2FK9PlLvNSOauSbIKHDPgMpBWJIAy71MgV+tetc0xibpmT1SwzVt8P2ECeCln3o+7yn7XuQ3DFvJ/5NCIQzbPKQXO8KFvaXftuAZghmfJy4W9t9oYe/u44kX4SCQAlMtoQT0FyEDmCkDWAKhLdGrYtqi1HD9FIEwbEOUpur+I4yMBrLvY21ILYI9MZwZl0PaKEEC+w5AAosbFJxfAkqFHiB2Ux4fYLmpbNur9eiQAsFPuW/Wgt2aznE8GwuAA6KFW8u4C4k54Qp2w97TRiJzQgNN9EZUcXhb6UgxB2QkaicBmcREoBYDtFjaoD1sYf4zuBKQA0DX0UjuAmI/VgUgByClADEXNO0TtRfGQPoUeCL19UQSWnUgAQf38LKUAYsq9MUoEv/5JFoErrmgiH051IhssCngRoDkEAsOPBNBr4dWGAKp8G+eIiK0w93rCHEDXJvtdvpHIBdymK2jCz5koxbZq0gGwfDwcxAIApACqomfuDIdeS2onFoBrmsE8AVweD0Q9BRhk6Wi86oh4Crxv3iYBbIBSCAARxwKomWavJhxAnfsl4QBTlEGrWocAaGoL0NAjVytjJcWfx1sIQFAiGBGIEwugCco6BFB2SC5GBXOmJcWfUAFcinIP30QwaNpa4QRL6NdgkQBuYee0A0jD1T18CgeYEAAHXOUUMKZEYKYAACmALVcKAIAACkQpHcBmkQA4UFvsAPKh0IgD5J3zItAVYx5BSAeIBHCPA3CAcQF4HKijBjA2SS5lEV6yBSA6oaEQACa/OpLHzc9nJntfjgIsukoHKNuWsAGlAzho6UjtAFciB2jMFsD681EHoCmJBHACQJ927AAjApCU6hogdgBgwgF0O2ij8c/kAFQABK3YARwhADQ+HE4LoEeUh3AAIReU0Il2gCtzBp4R3gquoHwxjPN7MOkAVBUoagAsxVE9GjzTDtDUGJt1G7jIAdgsB7At0ahHNwTTDtAGpfJB0GTu1yYEYHi8LR5B5V1K6wvENVoD4GVQheRNO8AhUcIBhFyoPDGSKoCXY7dC0/3eIgGIdIEJByjLUUBbsQNQES5kcCoEkJ8qAoHZdwFv3qoDqdIsXKM3I0Vg7ABy6ilGAlgZKwIBdQ0gKD+qKRF8j7cF4YorhCRpuND8F2MdBT7l6k3XAMIqzh2gwLGKisDk3QWQAJ59oE7QgOvjneDSNRM3PXp/ygEcRiNNZBc5ABa1cObYx1s4gLwNlDVzFec8XlU7gDqQ+Jk0hjINeHkbSPd+0gF0ejbRZ6S5HNUA3CJZiADD20BQqh1AUAoBaAoH0KrkADUhP1rYWuQAhmefccREHUOB2KJAksJD3YkCuRg5AD7MUw1AN9GthDnAxKNgYLzf6blNC1k4Zs+NHODeyTHHnFvgh/c80RvcOTmgh6S8c2Lp2Dg7szA73hlwcgD3sZCEjg+PtLt4YuK1lAKYG0iBd8MHSGUwY8TZ/R63zh3A8BAeAum5d45JAKDkdTqJKMMc9tU1AJGoKeU9bBuv1R3uCuZanDs9fDwBpVe70yMB8OYOf06aAyUm/5BSOkCOtzco2nV+kLwHQfrUFyLXr117pwnIUt625EPfyAHKnFCkr2j9cFzjKFxbwqkeruggmxzg/FGwEVAxbcgGld8FXFEHEn8dLCNqaURSI9cNLeERx06siHJfPgqWJ8EqiJIpHWAuZUA05DTI0iE5YYM4CMjg9teCw0TmogYId1J+8hyHhQKAVwbYIgHYyXwUrP5KVGLjuLNGg2fgH9DWaiiLUqnU7zOtctzdNoUrlkprWPSHJdMy8CFWWu7M/1rBau0eTiXo/f6aaBCNqB3g2vxAQvJKv1+Ep/tdCzvXwpiM2/4BE1GugVJfw5dB4Un9Pj6t+DhVWQPMpays4hHAKlEd3AEPbYgmKUsmv94CZWe1YiGQnZhSnNNBkDqFZoax45T12u1EfhmkaQ05CFTYPP9nJ+WHbPF/ZtFRigZVAtCuLQxETQ0uNkapOFbpALAAJeXiLA1ts+ospJQwWFgAG5uJ+zr4ZfRvUQ3t70E5BfzZQJQ1wC9Q6qgF6j90BgSQ1P8IAl6/1v4q1A4ANP5IIGoH+JXc71EV9KMCSOg/hHw//u3fBShqgJ+HfvL5wQ+ecvsoE0D207BMAMlxgB8TQPbLoH9eAJkDpPzXwbmk/DYwSQJITCdkDpAJQIWsBkiLALK7gCUJoAJsr9HfX8akA2CXjORPwZwUQKWSjNyXKYBX28vCw78fyP2k5P6NvTPIbRsGouhsDIq36iUGkIEcQhuj8abQRrC808awdKAeo+uu3DN0JKdwVNEIGQUgNfxv8RE4GnwQ+ZoR6Tj5lk4AVjFQAHCnNP9Y9ApKCgDu6gJguaN4FLm6pxOAA9cUj12Qu6K1pxOAC+/JG7jrCwBzxDFssnVPJgCW2X8Qwl1fAA7M/oMQ7voCcGH2H4TK3Jsgd50BMMw/4w1CcY/5EBC0dqUBsMwRB6G4v8RzL4LWrjQAO+aIg/CQr3sqAZAh7D8I4a4uAIaFl1iD0PCIr7uutScSAMvsPwjhri8AMgb9ByHc9QVAxqD/IFTm3gS6qwzAcD63Td/2A8Wg79uWz22bo3siAQj+F+rK3FvyR28AKgpDUwBC1q42ABSThkLQs/aEAnCkINT8CDACkqChUDACVNFTIEr6T0IByPU38/G5gOh3Qc6kFICKwMcgAG+oacIYAUkE4DIMHdnhfx0EeqaGXPwrPbqL7L105nTELiB+ALgpqS8FuojsxxeaMzVlyTU1Qm1ZtLMs371rVXS0xEqRXHm6l9pFaTEqiT6cGsZnA6MHYLxf5/e+U6v3t/GudgVgvyiityKnXkfFCIgegM9QuAJg9rQlEgpAT1tj5xwBCMAm3wtABwDB7Gp0gKxBB8j8GeAVHSDvXYDt0jrVQwAAAgAQgFWsHwEdbQkEALuAVNheAHAS+MUBeP8+ySjdUs2kjwvnehS5OkofRTMPX7+xaGUH+PEnUW7pBKCnYvqMdi+6p8ukjehp0tpMakftLI86XV4ti0Y9u4vuWt5L6fWJn6OoXHkSePuVKukEwBJVwkyNQLOvr5MaYXGheVpkFkVGcPnNL3y80q7sALff3xOFgA/tymeA2+YecMCMgA6AAGjE3QFqdIBcGAIOghAAgAAABEAVASPghAAoxL0LwENgNrh3AdgGZgM6QOY8OQlEB8gFnARmDk4C/7Z39i5OBGEYn7BCsMu460chwl2Cglpk3cGAimazgwEVrATrGKZYU6y4oJViIWhjba2yRbQR4Qo7UWz8K8TCziAWdurz7iZGzeqdX3CTfX8kO7nkUhzvb595ZzZ3V3FKBbjKCVAZyqcAvhpYGbaWP8nXAhjeCmZYgOXn7sZ7gNsswBLCq4CKw58JrDi8E1hxeCew4nACVBzuASrOTV4FVJuN7gOA0n2ACQtgIfXgKyooQUVBCaVP6vmzrII1OKvr0Fv9A1gAa3BW5L/nEAtgDSxAxWEBKs5/EWAbC2ANnAAVhwWoOCxAxeEeoOJwAlQcFqDiTAU4PnkXyBLcgPBlOYFPdxbAZgoB3n0Gb+Qiew1xX5azNpReNuQewGZyAY5/zvHlAi9/KUAWy21myAlgMySAOykE+CAXUMGZOFL+TwXwj7IAdkMC7EHx5xFw64c4jyVQoVoNMbqrGMCh1SYJYJ6dpCnA7dGzrg56LRbAMkiAEzMBXqN4+C/P8lt25wKcfLBmLsKP/cbcw3AsMzHemK093vsSAhwwZuRLz7wy5jkLYBckAEr/6cq1STEHnN91viQBTsYxTnfM+DRQazCm6M/2D7O9Q1Q+HpsnGLKxGbEAdgEBXAjw9trb67kAKP+5sgQwK152QR6N88HNHsi9MQQ4OLp4ciiPxKG7Rh4M5CnjswBWAQH2IACuQQIMhQBlCTDCkvBCPpwcyN0mzO9Zy4wOD+U2LSUGzzxGRKywAFZRCPARpz9BU8B2+h8jCwkwQI0HECAIIMFuEwWHUOqsmQ1ReSmVPjWAAM/ozgJYxVSAySwB5I2bJMBCAqDMx16gFRiPs4sQYDwekwArZ1YggHsmi00hgMsCWMZCAmAZABYSAALQYAAlAIhJAMp+ag1jM+IEsJJCgM/oASaUAOCnCUDDQGsdQYoWRn8mwOE4Cg5zAtjJD6sAsP1XCTCQBDWAYCYAvbiDewA7gQByMt8HWC8BLkrp+hAA7wq+EwBq0CrA41WAZZAAZ+c7geskwFHTUtj6c8294HQ8nwJGvspIgFHA+wC2QQJc++5awK5bYCEBiuz3aCfwMV0lHpvBLAGgxaPMkACGdwKt49urge9lCdseSnDqgQQYjXlEFS6uBTwNpTzyQLprJj59r7gW8IwFsAsSQN6ZBUAZ/vwIei0/16K4NigLXHzlUwPYa/LVQMvIBXCnnwj6OyAAfx7AOr79TCALUEEgwL/Cy1ZYAOv4hwK42mcBrIN/M6ji8C+GVBwWYOPU1/uxah0BnLb4gQTg7b7YhLAAG6YW6eDXP5jTF2BLV/yAp3XkdOqR2IRwD7BhnCjQ7T8SQPl+x+lXSIDlTAAnFGn/zwRIBNikAjSDf09vWQVAheuBLy4ndLskLtGU0LncFpfpgRNcLgTouL6odwR9j3BR+IZK6FiL8OaOwGEzNQPO6nrwH4qcC7AlFCrS7bQtVFvoulahwKGDO8zQWu8TII20btej4sRXDZF26QFNAfRSIjy9qaLgyv9ALCMQQHXrOnGiLRi69RB5oJNaryNSNHjzGWKLTtJ9eIUcQPWF11CdKwkJUI8SciepC8ZCHK1QfTrVnX5N92v9tEvF1AIlFih6d9YD9AXSXjXyr3BQiWq1uoUAeJHCg7ERR0c78xqrK6Hjh043RejTzC4UHuDcnjeBlPZdjHgTVX2WAA6+ry9S3RCMhTghVZcESKJ0Z5Q20igI8qBXAfq6uQBtJAAep1Rnigsx6wEcvGGnqKtN1QMwvyUA1VgnStEtbReLO5oCcJgnQC5AhLIDpdpfBaiFgqgv5ypp6SkEiKjkXlN4UVFv1Lpo/nBIw2kPkHcKTUGkrcZXAdBBCsLjOcBGIABQHQ+VxhzfRI07bj4F1PRON3F0oPcVq4Cd1OepvpiuCWYCQJzwUkcElzQngI0UAlzu6QQnOd1E2psu9rxehDJrtz/dB2gWC0BAkTEXoKZbXaFaoWAs4wvmoIEkCD+FgAAAAABJRU5ErkJggg==)]
NavigableMap让T有了对集合内元素搜索的能力
实现SortedMap接口让TreeMap有了对集合中的元素根据键排序的能力。默认key升序排序(也可以指定排序的比较器)
/** * @author shuang.kou * @createTime 2020年06月15日 17:02:00 */ public class Person { private Integer age; public Person(Integer age) { this.age = age; } public Integer getAge() { return age; } public static void main(String[] args) { TreeMap<Person, String> treeMap = new TreeMap<>(new Comparator<Person>() { @Override public int compare(Person person1, Person person2) { int num = person1.getAge() - person2.getAge(); return Integer.compare(num, 0); } }); treeMap.put(new Person(3), "person1"); treeMap.put(new Person(18), "person2"); treeMap.put(new Person(35), "person3"); treeMap.put(new Person(16), "person4"); treeMap.entrySet().stream().forEach(personStringEntry -> { System.out.println(personStringEntry.getValue()); }); } }
综上,相比于HashMap
来说 TreeMap
主要多了对集合中的元素根据键排序的能力以及对集合内元素的搜索的能力。
当你把对象加入HashSet
时,HashSet
会先计算对象的hashcode
值来判断对象加入的位置,同时也会与其他加入的对象的 hashcode
值作比较,如果没有相符的 hashcode
,HashSet
会假设对象没有重复出现。但是如果发现有相同 hashcode
值的对象,这时会调用equals()
方法来检查 hashcode
相等的对象是否真的相同。如果两者相同,HashSet
就不会让加入操作成功。
jdk8中,HashSet的add()方法只是简单调用了HashMap的put()方法,并且判断了一下返回值确保是否有重复元素。
// Returns: true if this set did not already contain the specified element
// 返回值:当set中没有包含add的元素时返回真
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
//HashMap的putVal()方法中也有说明
// Returns : previous value, or null if none
// 返回值:如果插入位置没有元素返回null,否则返回上一个元素
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
...
}
所以,无论HashSet中是否已经存在某元素,HashSet都会直接插入,只是会在add()方法返回值处告诉我们插入前是否存在相同元素。
JDK1.7:
根据对冲突处理方式不同,哈希表两种实现方式:1、开放地址方式;2、冲突链表方式。java7采用冲突链表方式。
有两个参数影响HashMap的性能:初始容量(inital capacity)和负载系数(load factor)。
初始容量:指定初始table的大小
负载系数:指定自动扩容的临界值
当entry数量超过capacity*load_factor时,容器自动扩容并重新哈希。对于插入元素较多的场景,将初始容量设大可以减少重新哈希次数。
将对象放入到HashMap或HashSet中时,有两个方法需要特别关心: hashCode()
和equals()
。hashCode()
方法决定了对象会被放到哪个bucket
里,当多个对象的哈希值冲突时,equals()
方法决定了这些对象是否是“同一个对象”。所以,如果要将自定义的对象放入到HashMap
或HashSet
中,需要*@Override*hashCode()
和equals()
方法。
get()
get(Object key)方法根据指定key返回对应的value,该方法调用了getEntry(Object key)得到相应的entry,然后返回entry.getValue()。因此,getEntry()
是算法的核心。算法思想是:首先通过Hash()函数得到对应bucket下标,然后依次遍历冲突链表,通过key.equals(k)
方法来判断是否是要找的那个entry
。
上图中hash(k)&(table.length-1)
等价于hash(k)%table.length
,原因是HashMap要求table.length
必须是2的指数,因此table.length-1
就是二进制低位全是1,跟hash(k)
相与会将哈希值的高位全抹掉,剩下的就是余数了。
//getEntry()方法
final Entry<K,V> getEntry(Object key) {
......
int hash = (key == null) ? 0 : hash(key);
for (Entry<K,V> e = table[hash&(table.length-1)];//得到冲突链表
e != null; e = e.next) {//依次遍历冲突链表中的每个entry
Object k;
//依据equals()方法判断是否相等
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
put()
put(K key, V value)
方法是将指定的key, value
对添加到map
里。该方法首先会对map
做一次查找,看是否包含该元组,如果已经包含则直接返回,查找过程类似于getEntry()
方法;如果没有找到,则会通过addEntry(int hash, K key, V value, int bucketIndex)
方法插入新的entry
,插入方式为头插法。
扩容机制:HM使用懒扩容,只会在put时才判断。注意,每次扩容后,都要重新计算原来的Entry在新数组中的位置。
//addEntry()
void addEntry(int hash, K key, V value, int bucketIndex) {
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);//自动扩容,并重新哈希
hash = (null != key) ? hash(key) : 0;
bucketIndex = hash & (table.length-1);//hash%table.length
}
//在冲突链表头部插入新的entry
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<>(hash, key, value, e);
size++;
}
remove()
//removeEntryForKey() final Entry<K,V> removeEntryForKey(Object key) { ...... int hash = (key == null) ? 0 : hash(key); int i = indexFor(hash, table.length);//hash&(table.length-1) Entry<K,V> prev = table[i];//得到冲突链表 Entry<K,V> e = prev; while (e != null) {//遍历冲突链表 Entry<K,V> next = e.next; Object k; if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) {//找到要删除的entry modCount++; size--; if (prev == e) table[i] = next;//删除的是冲突链表的第一个entry else prev.next = next; return e; } prev = e; e = next; } return e; }
JDK1.8
数组+ 链表+ 红黑树
java7中查找时根据hash值找到数组下标,但后面要顺着链表一个个比较下去才能找到,时间复杂度取决于链表长度,为O(n)。
为了降低这部分开销,java8中,当链表元素达到8个,会将链表转换为红黑树,在这里进行查找时间复杂度降为O(logN)。
java8使用Node代替entry,但只用于链表的情况,红黑树的情况使用TreeNode
put()
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } // 第四个参数 onlyIfAbsent 如果是 true,那么只有在不存在该 key 时才会进行 put 操作 // 第五个参数 evict 我们这里不关心 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; // 第一次 put 值的时候,会触发下面的 resize(),类似 java7 的第一次 put 也要初始化数组长度 // 第一次 resize 和后续的扩容有些不一样,因为这次是数组从 null 初始化到默认的 16 或自定义的初始容量 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; // 找到具体的数组下标,如果此位置没有值,那么直接初始化一下 Node 并放置在这个位置就可以了 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else {// 数组该位置有数据 Node<K,V> e; K k; // 首先,判断该位置的第一个数据和我们要插入的数据,key 是不是"相等",如果是,取出这个节点 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; // 如果该节点是代表红黑树的节点,调用红黑树的插值方法,本文不展开说红黑树 else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { // 到这里,说明数组该位置上是一个链表 for (int binCount = 0; ; ++binCount) { // 插入到链表的最后面(Java7 是插入到链表的最前面) if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); // TREEIFY_THRESHOLD 为 8,所以,如果新插入的值是链表中的第 8 个 // 会触发下面的 treeifyBin,也就是将链表转换为红黑树 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } // 如果在该链表中找到了"相等"的 key(== 或 equals) if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) // 此时 break,那么 e 为链表中[与要插入的新值的 key "相等"]的 node break; p = e; } } // e!=null 说明存在旧值的key与要插入的key"相等" // 对于我们分析的put操作,下面这个 if 其实就是进行 "值覆盖",然后返回旧值 if (e != null) { V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; // 如果 HashMap 由于新插入这个值导致 size 已经超过了阈值,需要进行扩容 if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }
相比起来,JDK1.7性能稍差,因为扰动了4次。
迭代器EntrySet
public class HashMapTest { public static void main(String[] args) { // 创建并赋值 HashMap Map<Integer, String> map = new HashMap(); map.put(1, "Java"); map.put(2, "JDK"); map.put(3, "Spring Framework"); map.put(4, "MyBatis framework"); map.put(5, "Java中文社群"); // 遍历 Iterator<Map.Entry<Integer, String>> iterator = map.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<Integer, String> entry = iterator.next(); System.out.println(entry.getKey()); System.out.println(entry.getValue()); } } }
1 Java 2 JDK 3 Spring Framework 4 MyBatis framework 5 Java中文社群
迭代器keySet
Iterator<Integer> iterator = map.keySet().iterator();
while (iterator.hasNext()) {
Integer key = iterator.next();
System.out.println(key);
System.out.println(map.get(key));
}
结果同上
ForEach EntrySet
for (Map.Entry<Integer, String> entry : map.entrySet()) {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
同上
ForEach KeySet
for(Integer key : map.keySet()){
System.out.println(key);
System.out.println(map.get(key));
}
Lambda
map.forEach((key, value)) -> {
System.out.println(key);
System.out.println(value);
});
Streams API 单线程
map.entrySet().stream().forEach(entry) -> {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
});
Streams API 多线程
map.entrySet().parallelStream().forEach(entry) -> {
//同上
});
结果:
4
MyBatis framework
5
Java中文社群
1
Java
2
JDK
3
Spring Framework
使用迭代器或是forEach循环的EntrySet
方法,性能是相同的。
使用…的keySet
方法,性能也相同。
但是两种EntrySet比keySet方法性能高,是因为KeySet
在循环时使用了 map.get(key)
,而 map.get(key)
相当于又遍历了一遍 Map 集合去查询 key
所对应的值。(为什么要用“又”这个词?那是因为在使用迭代器或者 for 循环时,其实已经遍历了一遍 Map 集合了,因此再使用 map.get(key)
查询时,相当于遍历了两遍。)
而 EntrySet
只遍历了一遍 Map 集合,之后通过代码“Entry<Integer, String> entry = iterator.next()”把对象的 key
和 value
值都放入到了 Entry
对象中,因此再获取 key
和 value
值时就无需再遍历 Map 集合,只需要从 Entry
对象中取值就可以了。
所以,EntrySet
的性能比 KeySet
的性能高出了一倍,因为 KeySet
相当于循环了两遍 Map 集合,而 EntrySet
只循环了一遍。
我们不能在遍历中使用集合 map.remove()
来删除数据,这是非安全的操作方式,但我们可以使用迭代器的 iterator.remove()
的方法来删除数据,这是安全的删除集合的方式。同样的我们也可以使用 Lambda 中的 removeIf
来提前删除数据,或者是使用 Stream 中的 filter
过滤掉要删除的数据进行循环,这样都是安全的,当然我们也可以在 for
循环前删除数据在遍历也是线程安全的。
底层数据结构:1.7采用分段的数组+链表,1.8采用和HashMap一样。
实现线程安全的方式(※):①在 JDK1.7 的时候,ConcurrentHashMap
(分段锁) 对整个桶数组进行了分割分段(Segment
),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。 到了 JDK1.8 的时候已经摒弃了 Segment
的概念,而是直接用 Node
数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized
和 CAS 来操作。(JDK1.6 以后 对 synchronized
锁做了很多优化) 整个看起来就像是优化过且线程安全的 HashMap
,虽然在 JDK1.8 中还能看到 Segment
的数据结构,但是已经简化了属性,只是为了兼容旧版本。② Hashtable
(同一把锁) :使用 synchronized
来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。
段(Segment
),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。 到了 JDK1.8 的时候已经摒弃了 Segment
的概念,而是直接用 Node
数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized
和 CAS 来操作。(JDK1.6 以后 对 synchronized
锁做了很多优化) 整个看起来就像是优化过且线程安全的 HashMap
,虽然在 JDK1.8 中还能看到 Segment
的数据结构,但是已经简化了属性,只是为了兼容旧版本。② Hashtable
(同一把锁) :使用 synchronized
来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。