当前位置: 首页 > news >正文

手机网站封装小程序百度帐号个人中心

手机网站封装小程序,百度帐号个人中心,东莞市住房城乡建设网官网,企业管理十大系统Map接口 说一下 HashMap 的实现原理#xff1f; HashMap概述#xff1a; HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作#xff0c;并允许使用null值和null键。此类不保证映射的顺序#xff0c;特别是它不保证该顺序恒久不变。 HashMap的数据…Map接口 说一下 HashMap 的实现原理 HashMap概述 HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作并允许使用null值和null键。此类不保证映射的顺序特别是它不保证该顺序恒久不变。 HashMap的数据结构 在Java编程语言中最基本的结构就是两种一个是数组另外一个是模拟指针引用所有的数据结构都可以用这两个基本结构来构造的HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构即数组和链表的结合体。 HashMap 基于 Hash 算法实现的 当我们往Hashmap中put元素时利用key的hashCode重新hash计算出当前对象的元素在数组中的下标存储时如果出现hash值相同的key此时有两种情况。(1)如果key相同则覆盖原始值(2)如果key不同出现冲突则将当前的key-value放入链表中获取时直接找到hash值对应的下标在进一步判断key是否相同从而找到对应值。理解了以上过程就不难明白HashMap是如何解决hash冲突的问题核心就是使用了数组的存储方式然后将冲突的key的对象放入链表中一旦发现冲突就在链表中做进一步的对比。 需要注意Jdk 1.8中对HashMap的实现做了优化当链表中的节点数据超过八个之后该链表会转为红黑树来提高查询效率从原来的O(n)到O(logn) HashMap在JDK1.7和JDK1.8中有哪些不同 HashMap的底层实现 在Java中保存数据有两种比较简单的数据结构数组和链表。数组的特点是寻址容易插入和删除困难链表的特点是寻址困难但插入和删除容易所以我们将数组和链表结合在一起发挥两者各自的优势使用一种叫做拉链法的方式可以解决哈希冲突。 JDK1.8之前 JDK1.8之前采用的是拉链法。拉链法将链表和数组相结合。也就是说创建一个链表数组数组中每一格就是一个链表。若遇到哈希冲突则将冲突的值加到链表中即可。 JDK1.8之后 相比于之前的版本jdk1.8在解决哈希冲突时有了较大的变化当链表长度大于阈值默认为8时将链表转化为红黑树以减少搜索时间。 JDK1.7 VS JDK1.8 比较 DK1.8主要解决或优化了一下问题 resize 扩容优化引入了红黑树目的是避免单条链表过长而影响查询效率红黑树算法请参考解决了多线程死循环问题但仍是非线程安全的多线程时可能会造成数据丢失问题。 HashMap的put方法的具体流程 当我们put的时候首先计算 key的hash值这里调用了 hash方法hash方法实际是让key.hashCode()与key.hashCode()16进行异或操作高16bit补0一个数和0异或不变所以 hash 函数大概的作用就是高16bit不变低16bit和高16bit做了一个异或目的是减少碰撞。按照函数注释因为bucket数组大小是2的幂计算下标index (table.length - 1) hash如果不做 hash 处理相当于散列生效的只有几个低 bit 位为了减少散列的碰撞设计者综合考虑了速度、作用、质量之后使用高16bit和低16bit异或来简单处理减少碰撞而且JDK8中用了复杂度 Ologn的树结构来提升碰撞下的性能。 putVal方法执行流程图 1 public V put(K key, V value) { 2 return putVal(hash(key), key, value, false, true); 3 } 4 5 static final int hash(Object key) { 6 int h; 7 return (key null) ? 0 : (h key.hashCode()) ^ (h 16); 8 } 9 10 //实现Map.put和相关方法 11 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, 12 boolean evict) { 13 NodeK,V[] tab; NodeK,V p; int n, i; 14 // 步骤①tab为空则创建 15 // table未初始化或者长度为0进行扩容 16 if ((tab table) null || (n tab.length) 0) 17 n (tab resize()).length; 18 // 步骤②计算index并对null做处理 19 // (n ‐ 1) hash 确定元素存放在哪个桶中桶为空新生成结点放入桶中(此时这 个结点是放在数组中) 20 if ((p tab[i (n ‐ 1) hash]) null) 21 tab[i] newNode(hash, key, value, null); 22 // 桶中已经存在元素 23 else { 24 NodeK,V e; K k; 25 // 步骤③节点key存在直接覆盖value 26 // 比较桶中第一个元素(数组中的结点)的hash值相等key相等 27 if (p.hash hash 28 ((k p.key) key || (key ! null key.equals(k)))) 29 // 将第一个元素赋值给e用e来记录 30 e p; 31 // 步骤④判断该链为红黑树 32 // hash值不相等即key不相等为红黑树结点 33 // 如果当前元素类型为TreeNode表示为红黑树putTreeVal返回待存放的node, e可 能为null 34 else if (p instanceof TreeNode) 35 // 放入树中 36 e ((TreeNodeK,V)p).putTreeVal(this, tab, hash, key, value); 37 // 步骤⑤该链为链表 38 // 为链表结点 39 else { 40 // 在链表最末插入结点 41 for (int binCount 0; ; binCount) { 42 // 到达链表的尾部 43 44 //判断该链表尾部指针是不是空的 45 if ((e p.next) null) { 46 // 在尾部插入新结点 47 p.next newNode(hash, key, value, null); 48 //判断链表的长度是否达到转化红黑树的临界值临界值为8 49 if (binCount TREEIFY_THRESHOLD ‐ 1) // ‐1 for 1st 50 //链表结构转树形结构 51 treeifyBin(tab, hash); 52 // 跳出循环 53 break; 54 } 55 // 判断链表中结点的key值与插入的元素的key值是否相等 56 if (e.hash hash 57 ((k e.key) key || (key ! null key.equals(k)))) 58 // 相等跳出循环 59 break; 60 // 用于遍历桶中的链表与前面的e p.next组合可以遍历链表 61 p e; 62 } 63 } 64 //判断当前的key已经存在的情况下再来一个相同的hash值、key值时返回新来的val ue这个值 65 if (e ! null) { 66 // 记录e的value 67 V oldValue e.value; 68 // onlyIfAbsent为false或者旧值为null 69 if (!onlyIfAbsent || oldValue null) 70 //用新值替换旧值 71 e.value value; 72 // 访问后回调 73 afterNodeAccess(e); 74 // 返回旧值 75 return oldValue; 76 } 77 } 78 // 结构性修改 79 modCount; 80 // 步骤⑥超过最大容量就扩容 81 // 实际大小大于阈值则扩容 82 if (size threshold) 83 resize(); 84 // 插入后回调 85 afterNodeInsertion(evict); 86 return null; 87 }①.判断键值对数组table[i]是否为空或为null否则执行resize()进行扩容 ②.根据键值key计算hash值得到插入的数组索引i如果table[i]null直接新建节点添加转向⑥如果table[i]不为空转向③ ③.判断table[i]的首个元素是否和key一样如果相同直接覆盖value否则转向 ④这里的相同指的是hashCode以及equals ④.判断table[i] 是否为treeNode即table[i] 是否是红黑树如果是红黑树则直接在树中插入键值对否则转向⑤ ⑤.遍历table[i]判断链表长度是否大于8大于8的话把链表转换为红黑树在红黑树中执行插入操作否则进行链表的插入操作遍历过程中若发现key已经存在直接覆盖value即可 ⑥.插入成功后判断实际存在的键值对数量size是否超多了最大容量threshold如果超过进行扩容。 HashMap的扩容操作是怎么实现的 ①.在jdk1.8中resize方法是在hashmap中的键值对大于阀值时或者初始化时就调用resize方法进行扩容 ②.每次扩展的时候都是扩展2倍 ③.扩展后Node对象的位置要么在原位置要么移动到原偏移量两倍的位置。在putVal()中我们看到在这个函数里面使用到了2次resize()方法resize()方法表示的在进行第一次初始化时会对其进行扩容或者当该数组的实际大小大于其临界值值(第一次为12),这个时候在扩容的同时也会伴随的桶上面的元素进行重新分发这也是JDK1.8版本的一个优化的地方在1.7中扩容之后需要重新去计算其Hash值根据Hash值对其进行分发但在1.8版本中则是根据在同一个桶的位置中进行判断(e.hash oldCap)是否为0重新进行hash分配后该元素的位置要么停留在原始位置要么移动到原始位置增加的数组大小这个位置上 1 final NodeK,V[] resize() { 2 NodeK,V[] oldTab table;//oldTab指向hash桶数组 3 int oldCap (oldTab null) ? 0 : oldTab.length; 4 int oldThr threshold; 5 int newCap, newThr 0; 6 if (oldCap 0) {//如果oldCap不为空的话就是hash桶数组不为空 7 if (oldCap MAXIMUM_CAPACITY) {//如果大于最大容量了就赋值为整数最大的阀 值 8 threshold Integer.MAX_VALUE; 9 return oldTab;//返回 10 }//如果当前hash桶数组的长度在扩容后仍然小于最大容量 并且oldCap大于默认值16 11 else if ((newCap oldCap 1) MAXIMUM_CAPACITY 12 oldCap DEFAULT_INITIAL_CAPACITY) 13 newThr oldThr 1; // double threshold 双倍扩容阀值threshold 14 } 15 // 旧的容量为0但threshold大于零代表有参构造有cap传入threshold已经被初 始化成最小2的n次幂 16 // 直接将该值赋给新的容量 17 else if (oldThr 0) // initial capacity was placed in threshold 18 newCap oldThr; 19 // 无参构造创建的map给出默认容量和threshold 16, 16*0.75 20 else { // zero initial threshold signifies using defaults 21 newCap DEFAULT_INITIAL_CAPACITY; 22 newThr (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); 23 } 24 // 新的threshold 新的cap * 0.75 25 if (newThr 0) { 26 float ft (float)newCap * loadFactor; 27 newThr (newCap MAXIMUM_CAPACITY ft (float)MAXIMUM_CAPACITY ? 28 (int)ft : Integer.MAX_VALUE); 29 } 30 threshold newThr; 31 // 计算出新的数组长度后赋给当前成员变量table 32 SuppressWarnings({rawtypes,unchecked}) 33 NodeK,V[] newTab (NodeK,V[])new Node[newCap];//新建hash桶数组 34 table newTab;//将新数组的值复制给旧的hash桶数组 35 // 如果原先的数组没有初始化那么resize的初始化工作到此结束否则进入扩容元素 重排逻辑使其均匀的分散 36 if (oldTab ! null) { 37 // 遍历新数组的所有桶下标 38 for (int j 0; j oldCap; j) { 39 NodeK,V e; 40 if ((e oldTab[j]) ! null) { 41 // 旧数组的桶下标赋给临时变量e并且解除旧数组中的引用否则就数组无法被GC回收 42 oldTab[j] null; 43 // 如果e.nextnull代表桶中就一个元素不存在链表或者红黑树 44 if (e.next null) 45 // 用同样的hash映射算法把该元素加入新的数组 46 newTab[e.hash (newCap ‐ 1)] e; 47 // 如果e是TreeNode并且e.next!null那么处理树中元素的重排 48 else if (e instanceof TreeNode) 49 ((TreeNodeK,V)e).split(this, newTab, j, oldCap); 50 // e是链表的头并且e.next!null那么处理链表中元素重排 51 else { // preserve order 52 // loHead,loTail 代表扩容后不用变换下标见注1 53 NodeK,V loHead null, loTail null; 54 // hiHead,hiTail 代表扩容后变换下标见注1 55 NodeK,V hiHead null, hiTail null; 56 NodeK,V next; 57 // 遍历链表 58 do { 59 next e.next; 60 if ((e.hash oldCap) 0) { 61 if (loTail null) 62 // 初始化head指向链表当前元素ee不一定是链表的第一个元素初始化后loHead 63 // 代表下标保持不变的链表的头元素 64 loHead e; 65 else 66 // loTail.next指向当前e 67 loTail.next e; 68 // loTail指向当前的元素e 69 // 初始化后loTail和loHead指向相同的内存所以当loTail.next指向下一个元素 时 70 // 底层数组中的元素的next引用也相应发生变化造成lowHead.next.next..... 71 // 跟随loTail同步使得lowHead可以链接到所有属于该链表的元素。 72 loTail e; 73 } 74 else { 75 if (hiTail null) 76 // 初始化head指向链表当前元素e, 初始化后hiHead代表下标更改的链表头元素 77 hiHead e; 78 else 79 hiTail.next e; 80 hiTail e; 81 } 82 } while ((e next) ! null); 83 // 遍历结束, 将tail指向null并把链表头放入新数组的相应下标形成新的映射。 84 if (loTail ! null) { 85 loTail.next null; 86 newTab[j] loHead; 87 } 88 if (hiTail ! null) { 89 hiTail.next null; 90 newTab[j oldCap] hiHead; 91 } 92 } 93 } 94 } 95 } 96 return newTab; 97 }HashMap是怎么解决哈希冲突的 答在解决这个问题之前我们首先需要知道什么是哈希冲突而在了解哈希冲 突之前我们还要知道什么是哈希才行 什么是哈希 Hash一般翻译为“散列”也有直接音译为“哈希”的这就是把任意长度的输入通过散列算法变换成固定长度的输出该输出就是散列值哈希值 这种转换是一种压缩映射也就是散列值的空间通常远小于输入的空间不同的输入可能会散列成相同的输出所以不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。 所有散列函数都有如下一个基本特性**根据同一散列函数计算出的散列值如果 不同那么输入值肯定也不同。但是根据同一散列函数计算出的散列值如果相 同输入值不一定相同**。 什么是哈希冲突 当两个不同的输入值根据同一散列函数计算出相同的散列值的现象我们就把它叫做碰撞哈希碰撞。 HashMap的数据结构 在Java中保存数据有两种比较简单的数据结构数组和链表。数组的特点是寻址容易插入和删除困难链表的特点是寻址困难但插入和删除容易所以我们将数组和链表结合在一起发挥两者各自的优势使用一种叫做链地址法的方式可以解决哈希冲突 这样我们就可以将拥有相同哈希值的对象组织成一个链表放在hash值所对应的bucket下但相比于hashCode返回的int类型我们HashMap初始的容量大小DEFAULT_INITIAL_CAPACITY 1 4即2的四次方16要远小于int类型的范围所以我们如果只是单纯的用hashCode取余来获取对应的bucket这将会大大增加哈希碰撞的概率并且最坏情况下还会将HashMap变成一个单链表所以我们还需要对hashCode作一定的优化 hash()函数 上面提到的问题主要是因为如果使用hashCode取余那么相当于参与运算的只有hashCode的低位高位是没有起到任何作用的所以我们的思路就是让hashCode取值出的高位也参与运算进一步降低hash碰撞的概率使得数据分布更平均我们把这样的操作称为扰动在JDK 1.8中的hash()函数如下 1 static final int hash(Object key) { 2 int h; 3 return (key null) ? 0 : (h key.hashCode()) ^ (h 16);// 与自己右 移16位进行异或运算高低位异或 4 }这比在JDK 1.7中更为简洁相比在1.7中的4次位运算5次异或运算9次 扰动在1.8中只进行了1次位运算和1次异或运算2次扰动 JDK1.8新增红黑树 通过上面的链地址法使用散列表和扰动函数我们成功让我们的数据分布更平均哈希碰撞减少但是当我们的HashMap中存在大量数据时加入我们某个bucket下对应的链表有n个元素那么遍历时间复杂度就为O(n)为了针对这个问题JDK1.8在HashMap中新增了红黑树的数据结构进一步使得遍历复杂度降低至O(logn) 总结 简单总结一下HashMap是使用了哪些方法来有效解决哈希冲突的 使用链地址法使用散列表来链接拥有相同hash值的数据使用2次扰动函数hash函数来降低哈希冲突的概率使得数据分布更平均引入红黑树进一步降低遍历的时间复杂度使得遍历更快 能否使用任何类作为 Map 的 key 可以使用任何类作为 Map 的 key然而在使用之前需要考虑以下几点 如果类重写了 equals() 方法也应该重写 hashCode() 方法。类的所有实例需要遵循与 equals() 和hashCode() 相关的规则。如果一个类没有使用 equals()不应该在 hashCode() 中使用它。用户自定义 Key类最佳实践是使之为不可变的这样 hashCode() 值 可以被缓存起来拥有更好的性能。不可变的类也可以确保 hashCode()和 equals() 在未来不会改变这样就会解决与可变相关的问题了。 为什么HashMap中String、Integer这样的包装类适合作为K 答String、Integer等包装类的特性能够保证Hash值的不可更改性和计算准确性能够有效的减少Hash碰撞的几率 都是final类型即不可变性保证key的不可更改性不会存在获取hash值不同的情况内部已重写了equals()、hashCode()等方法遵守了HashMap内部的规范不清楚可以去上面看看putValue的过程不容易出现Hash值计算错误的情况 如果使用Object作为HashMap的Key应该怎么办呢 答重写hashCode()和equals()方法 重写hashCode()是因为需要计算存储数据的存储位置需要注意不要试图从散列码计算中排除掉一个对象的关键部分来提高性能这样虽然能更快但可能会导致更多的Hash碰撞重写equals()方法需要遵守自反性、对称性、传递性、一致性以及对于任何非null的引用值xx.equals(null)必须返回false的这几个特性目的是为了保证key在哈希表中的唯一性 HashMap为什么不直接使用hashCode()处理后的哈希值直接作为table的下标 答hashCode()方法返回的是int整数类型其范围为-(2 ^ 31)~(2 ^ 31 - 1)约有40亿个映射空间而HashMap的容量范围是在16初始化默认值~2 ^30HashMap通常情况下是取不到最大值的并且设备上也难以提供这么多的存储空间从而导致通过hashCode()计算出的哈希值可能不在数组大小范围内进而无法匹配存储位置 那怎么解决呢 HashMap自己实现了自己的hash()方法通过两次扰动使得它自己的哈希值高低位自行进行异或运算降低哈希碰撞概率也使得数据分布更平均在保证数组长度为2的幂次方的时候使用hash()运算之后的值与运算数组长度 - 1来获取数组下标的方式进行存储这样一来是比取余操作更加有效率二来也是因为只有当数组长度为2的幂次方时h(length-1)才等价于h%length三来解决了“哈希值与数组大小范围不匹配”的问题 HashMap 的长度为什么是2的幂次方 为了能让 HashMap 存取高效尽量较少碰撞也就是要尽量把数据分配均匀每个链表/红黑树长度大致相同。这个实现就是把数据存到哪个链表/红黑树中的算法。 这个算法应该如何设计呢 我们首先可能会想到采用%取余的操作来实现。但是重点来了“取余(%)操作中如果除数是2的幂次则等价于与其除数减一的与()操作也就是说hash%lengthhash(length-1)的前提是 length 是2的 n 次方。” 并且 采用二进制位操作 相对于%能够提高运算效率这就解释了 HashMap的长度为什么是2的幂次方。 那为什么是两次扰动呢 答这样就是加大哈希值低位的随机性使得分布更均匀从而提高对应数组存储下标位置的随机性均匀性最终减少Hash冲突两次就够了已经达到了高位低位同时参与运算的目的 HashMap 与 HashTable 有什么区别 线程安全 HashMap 是非线程安全的HashTable 是线程安全的HashTable 内部的方法基本都经过 synchronized 修饰。如果你要保证线程安全的话就使用 ConcurrentHashMap 吧效率 因为线程安全的问题HashMap 要比 HashTable 效率高一点。另外HashTable 基本被淘汰不要在代码中使用它对Null key 和Null value的支持 HashMap 中null 可以作为键这样的键只有一个可以有一个或多个键所对应的值为 null。但是在HashTable 中 put 进的键值只要有一个 null直接抛NullPointerException。 4.初始容量大小和每次扩充容量大小的不同 ①创建时如果不指定容量初始值Hashtable 默认的初始大小为11之后每次扩充容量变为原来的2n1。HashMap 默认的初始化大小为16。之后每次扩充容量变为原来的2倍。②创建时如果给定了容量初始值那么 Hashtable 会直接使用你给定的大小而 HashMap 会将其扩充为2的幂次方大小。也就是说 HashMap 总是使用2的幂作为哈希表的大小后面会介绍到为什么是2的幂次方。底层数据结构 JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化当链表长度大于阈值默认为8时将链表转化为红黑树以减少搜索时间。Hashtable 没有这样的机制。推荐使用在 Hashtable 的类注释可以看到Hashtable 是保留类不建议使用推荐在单线程环境下使用 HashMap 替代如果需要多线程使用则用 ConcurrentHashMap 替代。 如何决定使用 HashMap 还是TreeMap 对于在Map中插入、删除和定位元素这类操作HashMap是最好的选择。然而假如你需要对一个有序的key集合进行遍历TreeMap是更好的选择。基于你的collection的大小也许向HashMap中添加元素会更快将map换为TreeMap进行有序key的遍历。 HashMap 和 ConcurrentHashMap 的区别 ConcurrentHashMap对整个桶数组进行了分割分段(Segment)然后在每一个分段上都用lock锁进行保护相对于HashTable的synchronized锁的粒度更精细了一些并发性能更好而HashMap没有锁机制不是线程安全的。JDK1.8之后ConcurrentHashMap启用了一种全新的方式实现,利用CAS算法。HashMap的键值对允许有null但是ConCurrentHashMap都不允许。 ConcurrentHashMap 和 Hashtable 的区别 ConcurrentHashMap 和 Hashtable 的区别主要体现在实现线程安全的方式上不同。 底层数据结构 JDK1.7的 ConcurrentHashMap 底层采用 分段的数组链表 实现JDK1.8 采用的数据结构跟HashMap1.8的结构一样数组链表/红黑二叉树。Hashtable 和 JDK1.8 之前的 HashMap 的底层数据结构类似都是采用 数组链表 的形式数组是 HashMap 的主体链表则是主要为了解决哈希冲突而存在的实现线程安全的方式重要 ① 在JDK1.7的时候ConcurrentHashMap分段锁 对整个桶数组进行了分割分段(Segment)每一把锁只锁容器其中一部分数据多线程访问容器里不同数据段的数据就不会存在锁竞争提高并发访问率。默认分配16个Segment比Hashtable效率提高16倍。 到了 JDK1.8 的时候已经摒弃了Segment的概念而是直接用Node 数组链表红黑树的数据结构来实现并发控制使用synchronized 和 CAS 来操作。JDK1.6以后 对 synchronized锁做了很多优化 整个看起来就像是优化过且线程安全的 HashMap虽然在JDK1.8中还能看到 Segment 的数据结构但是已经简化了属性只是为了兼容旧版本②Hashtable(同一把锁) :使用 synchronized 来保证线程安全效率非常低下。当 一个线程访问同步方法时其他线程也访问同步方法可能会进入阻塞或轮询状态如使用 put 添加元素另一个线程不能使用 put 添加元素也不能使用 get竞争会越来越激烈效率越低。 ConcurrentHashMap 底层具体实现知道吗实现原理是什么 JDK1.7 首先将数据分为一段一段的存储然后给每一段数据配一把锁当一个线程占用锁访问其中一个段数据时其他段的数据也能被其他线程访问。在JDK1.7中ConcurrentHashMap采用Segment HashEntry的方式进行实 现结构如下 一个 ConcurrentHashMap 里包含一个 Segment 数组。Segment 的结构和HashMap类似是一种数组和链表结构一个 Segment 包含一个 HashEntry数组每个 HashEntry 是一个链表结构的元素每个 Segment 守护着一个HashEntry数组里的元素当对 HashEntry 数组的数据进行修改时必须首先获得对应的 Segment的锁。 该类包含两个静态内部类 HashEntry 和 Segment 前者用来封装映射表的键值对后者用来充当锁的角色Segment 是一种可重入的锁 ReentrantLock每个 Segment 守护一个HashEntry 数组里得元素当对 HashEntry 数组的数据进行修改时必须首先获得对应的 Segment 锁。 JDK1.8 在JDK1.8中放弃了Segment臃肿的设计取而代之的是采用Node CAS Synchronized来保证并发安全进行实现synchronized只锁定当前链表或红黑二叉树的首节点这样只要hash不冲突就不会产生并发效率又提升N倍。 辅助工具类 Array 和 ArrayList 有何区别 Array 可以存储基本数据类型和对象ArrayList 只能存储对象。Array 是指定固定大小的而 ArrayList大小是自动扩展的。Array 内置方法没有 ArrayList 多比如 addAll、removeAll、iteration 等方法 对于基本类型数据集合使用自动装箱来减少编码工作量。但是当处理固定大小的基本数据类型的时候这种方式相对比较慢。 如何实现 Array 和 List 之间的转换 Array 转 List Arrays. asList(array) List 转 ArrayList 的 toArray() 方法。 comparable 和 comparator的区别 comparable接口实际上是出自java.lang包它有一个 compareTo(Object obj)方法用来排序comparator接口实际上是出自 java.util 包它有一个compare(Object obj1,Object obj2)方法用来排序 一般我们需要对一个集合使用自定义排序时我们就要重写compareTo方法或compare方法当我们需要对某一个集合实现两种排序方式比如一个song对象中的歌名和歌手名分别采用一种排序方法的话我们可以重写compareTo方法和使用自制的Comparator方法或者以两个Comparator来实现歌名排序和歌星名排序第二种代表我们只能使用两个参数版的Collections.sort(). Collection 和 Collections 有什么区别 java.util.Collection 是一个集合接口集合类的一个顶级接口。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式其直 接继承接口有List与Set。Collections则是集合类的一个工具类/帮助类其中提供了一系列静态方法用于对集合中元素进行排序、搜索以及线程安全等各种操作。 TreeMap 和 TreeSet 在排序时如何比较元素Collections 工具类中的 sort()方法如何比较元素 TreeSet 要求存放的对象所属的类必须实现 Comparable 接口该接口提供了比较元素的 compareTo()方法当插入元素时会回调该方法比较元素的大小。TreeMap 要求存放的键值对映射的键必须实现 Comparable 接口从而根据键对 元素进 行排 序。 Collections 工具类的 sort 方法有两种重载的形式第一种要求传入的待排序容器中存放的对象比较实现 Comparable 接口以实现 元素的比较 第二种不强制性的要求容器中的元素必须可比较但是要求传入第二个参数参数是Comparator 接口的子类型需要重写 compare 方法实现元素的比较相当于一个临时定义的排序规则其实就是通过接口注入比较元素大小的算法也是对回调模式的应用Java 中对函数式编程的支持
http://www.yutouwan.com/news/62625/

相关文章:

  • 珠海建网站多少钱郴州百度seo
  • 网站seo检测南京每月做社保明细在哪个网站查
  • 哪个网站做超链接网站做百度收录的意义
  • 乡镇网站建设方案百图汇免费素材图库
  • 做网站公司做网站公司有哪些做陶瓷的公司网站
  • 网站建设服务范围优化方案电子版
  • 速橙科技有限公司网站建设开发工具怎么使用
  • 网站建设需要多少时间怎么找拉新推广平台
  • 石家庄做网站哪家公司好网站cms模板
  • 郑州企业网站设计微信推广怎么做
  • 上传文件到网站wordpress媒体库 替换
  • 常熟市维摩剑门绿茶网站建设目标百度seo自然优化
  • 一个月做网站学编程的人以后都干嘛呢
  • 黑龙江省住房和城乡建设网站网站建设太金手指六六六
  • 嘉定网站建设哪里便宜三维动画设计制作公司
  • 运动网站模板wordpress 画展主题
  • 番禺响应式网站开发上海网页设计公司哪儿济南兴田德润有活动吗
  • 做盗版电影网站犯法不企业常用的网络营销方法
  • 四川网站建设广元分公司河南项目信息网
  • wordpress多站点无法发布文章传播公司可以做门户网站吗
  • 富阳区建设局网站wordpress注册目录
  • dreamwear做网站基础网站建设
  • 做网站用什么软件tag() wordpress
  • 有服务器域名源码怎么做网站平台陕西省住房与城乡建设厅网站
  • 蚌埠网站制作哪家好百度网站建设多钱
  • 南京网站开发南京乐识赞生意宝做网站行吗
  • 微型营销网站制作哪家网络推广公司好
  • 分析网站的关键词推特登陆 网站建设
  • 搜索引擎网站推广wordpress远程后台设置
  • 做旅游网站的论文苏州保洁公司招聘保洁区域经理