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

泰安网站建设介绍网推是什么

泰安网站建设介绍,网推是什么,html5手机端开发软件,软件app下载大全#x1f495;活着是为了活着本身而活着#x1f495; 作者#xff1a;Mylvzi 文章主要内容#xff1a;数据结构之Map/Set讲解硬核源码剖析 一.搜索树 1.概念 二叉搜索树又叫二叉排序树#xff0c;他或者是一颗空树#xff0c;或者是具有以下性质的树 若它…  活着是为了活着本身而活着 作者Mylvzi   文章主要内容数据结构之Map/Set讲解硬核源码剖析    一.搜索树 1.概念 二叉搜索树又叫二叉排序树他或者是一颗空树或者是具有以下性质的树 若它的左子树不为空则左子树上所有节点的值都小于根节点的值若它的右子树不为空则右子树上所有节点的值都大于根节点的值它的左右子树也分别为二叉搜索树 简单来说二叉搜索树上存储结点的值满足以下条件: left root right  注意二叉搜索树中不能存在两个相同的值 2.二叉搜索树的操作及其实现 前提准备 static class TreeNode {int val;TreeNode left;TreeNode right;public TreeNode(int val) {this.val val;}}private TreeNode root null; 1.查询操作(search) 根据二叉搜索树的性质很容易实现查询的操作 代码实现 // searchpublic boolean search(int val) {TreeNode cur root;while(cur ! null) {if(cur.val val) {cur cur.right;} else if (cur.val val) {cur cur.left;}else {return true;}}return false;} 2.插入操作(insert) 让cur走到合适的位置再去判断parent.val 与 val的关系进行插入 画图分析 代码实现 // insertpublic boolean insert(int val) {TreeNode newNode new TreeNode(val);// 空树直接插入if (root null) {root newNode;return true;}// 保留cur的根节点TreeNode parent null;TreeNode cur root;while(cur ! null) {parent cur;if(cur.val val) {cur cur.right;} else if(cur.val val){cur cur.left;}else {// 二叉搜索树中不能存在两个相同的数字return false;}}// 此时cur就是要插入的位置if(parent.val val) {parent.right newNode;}else {parent.left newNode;}return true;} 3.删除操作(remove) 如果你要删除的cur的结点只有一个子节点此时删除十分容易 但是如果你想要删除的结点有两个子节点处理稍微麻烦因为在你删除cur结点之后还要保证剩下的结点也满足二叉搜索树的性质这里采用“替罪羊”法解决删除拥有两个子节点的结点 思路分析 我现在想要删除90这个结点但是删除之后谁来替代这个结点呢 根据二叉搜索树的性质90看作根节点则他一定比左树的所有结点的值大比右树所有结点的值小 我们需要找一个合理的结点去替换90替换之后仍要满足二叉搜索树的性质这个合理的结点可以通过两个方法实现 找左树的最大值找右树的最小值 现在以找右树的最小值为例演示 代码实现 public void remove(int val) {TreeNode parent null;TreeNode cur root;while(cur ! null) {parent cur;if (cur.val val) {cur cur.right;} else if (cur.val val) {cur cur.left;}else {removeNode(parent,cur);return;}}}private void removeNode(TreeNode parent, TreeNode cur) {// 注意此时cur就是我要删除的数据// cur的左树为空if(cur.left null) {if (cur root) {root cur.right;} else if (cur parent.left) {parent.left cur.right;}else {parent.right cur.right;}} else if (cur.right null) {// cur的右树为空if (cur root) {root cur.left;} else if (cur parent.left) {parent.left cur.left;}else {parent.right cur.left;}}else {// 都不等于Null// 找右树的最小值// 使用替罪羊法 /* TreeNode targetParent cur;TreeNode target cur.right;// 找右树的最小值 直到某个结点的left为null 则此节点就是右树最左边的结点 也就是右树的最小值while (target.left ! null) {targetParent target;target target.left;}cur.val target.val;if (target targetParent.left) {targetParent.left target.right;}else {targetParent.right target.right;}*/// 求左树的最大值TreeNode tp cur;TreeNode t cur.left;while (t.right ! null) {tp t;t t.right;}// 此时t就是左树的最大值cur.val t.val;// 重写链接if (t tp.left) {tp.left t.left;}else {tp.right t.left;}}} 3.性能分析 对于二叉搜索树来说进行插入/删除操作都要先进行查询所以二叉搜索树的性能取决于查询的效率  最好情况是二叉树是一颗完全二叉树  最坏情况则是二叉树是一颗单分支的二叉树 问题如果是单分支的情况二叉搜索树的效率会变得很低如何解决这种问题呢 4.和 java 类集的关系 TreeMap 和 TreeSet 即 java 中利用搜索树实现的 Map 和 Set实际上用的是红黑树而红黑树是一棵近似平衡的 二叉搜索树即在二叉搜索树的基础之上 颜色以及红黑树性质验证关于红黑树的内容后序再进行讲解。 二.搜索 1.概念和场景 Map和Set是一种专门用来进行“搜索”的数据结构/容器其搜索效率取决于其具体实现的子类 我们之前其实也学过搜索 1.直接遍历  最粗暴的搜索方法  时间复杂度0(N) 2.二分查找  需要序列是有序的  时间复杂度O(logN) 上述查找适合静态的查找即在查找的过程中不会进行插入和删除的操作但是现实中的很多查找都需要动态的进行插入和删除比如 1.根据学号查成绩 2.根据通讯录名字查找电话号 3.根据身高找女朋友(bushi) ...... 在查找的过程中可能会出现插入和删除的操作Map和Set就是用于动态的插入和删除的搜索容器 2.搜索的模型 搜索的方式其实有两种一种是直接在一大堆数据中寻找另一种是根据对应关系进行查找比如你需要先找到通讯录的名字才能根据通讯录的名字找到你要寻找的手机号我们一般把搜索的数据称为关键字Keykey对应的称为值Value将其称之为Key-value的键值对 1. 纯 key 模型(找有没有) 在词典中找单词 2. Key-Value 模型(找对应关系) 梁山好汉的江湖绰号每个好汉都有自己的江湖绰号学号对应着你的名字 而Map中存储的是Key-Value 模型Set中存储的是纯 key 模型 三.Map的使用 先放一张Java中和数据结构有关的类的图 1.概念 Map是一个接口类不能直接实例化对象如果要使用需要根据实现他的类来实例化具体的对象比如TreeMap(底层是红黑树)和HashMap(底层是哈希表) 2.常用方法 1.put  在Map中添加key与value的映射关系 // 根据实现Map的类TreeMap来实例化一个对象MapString,Integer map new TreeMap();// Put方法// 设置 单词--出现的次数 这样的一个key与value的映射map.put(apple,14);map.put(bank,15);map.put(cat,17); 注意 1.我们知道TreeMap的底层是一颗红黑树存储的时候是使用结点来存储元素的它实际上存储的是Key-Value的映射关系实际上Map中存在一个内部类Map.Entry,用于表示映射关系 2.既然TreeMap是一种红黑树那他在存入数据的时候必然要排序排序的根据是Key 2.get方法 存在两个get方法  但都是为了返回key对应的value值 System.out.println(map.get(apple));// 输出14System.out.println(map.get(bank));// 输出15System.out.println(map.get(cat));// 输出17// get时进行判断 如果Map中含有传入的key就返回其value 没有则返回其默认值(解某些题很有用)System.out.println(map.getOrDefault(apple, 100));// 输出14 因为Map中含有appleSystem.out.println(map.getOrDefault(Dog, 100));// 输出100 因为Map中不含有Dog get和getOrDefault的源码 // get也可以用来判断是否包含相应的keypublic V get(Object key) {TreeMap.EntryK,V p getEntry(key);return (pnull ? null : p.value);}default V getOrDefault(Object key, V defaultValue) {V v; // 三目运算符 为真返回v 为假返回默认值return (((v get(key)) ! null) || containsKey(key))? v: defaultValue;} 3.remove 删除key对应的映射关系 源码注意remove存在返回值 // remove存在返回值 返回你要删除的key对应的valuepublic V remove(Object key) {TreeMap.EntryK,V p getEntry(key);if (p null)return null;V oldValue p.value;deleteEntry(p);return oldValue;} 验证 System.out.println(map.remove(cat));// 输出17System.out.println(map.remove(Dog));// 输出null 4.contains   包含两个contains方法一个是判断是否存在key一个判断是否存在value System.out.println(map.containsKey(cat));// 输出trueSystem.out.println(map.containsKey(Dog));// 输出falseSystem.out.println(map.containsValue(15));// 输出trueSystem.out.println(map.containsValue(100));// 输出false 源码 public boolean containsKey(Object key) {return getEntry(key) ! null;}public boolean containsValue(Object value) {for (EntryK,V e getFirstEntry(); e ! null; e successor(e))if (valEquals(value, e.value))return true;return false;}5.keySet方法 返回不重复的key的集合  就是将Map中所有的key值存放到Set内部存的时候会进行排序 // 此处Set里面存放的类型要和key一致SetString set map.keySet();for (String s:set) {System.out.print(s );// 输出apple bank cat} 源码 public SetK keySet() {return navigableKeySet();}/*** since 1.6*/public NavigableSetK navigableKeySet() {KeySetK nks navigableKeySet;return (nks ! null) ? nks : (navigableKeySet new KeySet(this));} 6.values 返回所有的value的可重复集合  和上一个方法类似  此方法是拿到所有的value将其存放到Collection里面 CollectionInteger collection map.values();for (int val:collection) {System.out.print(val );// 输出14 15 17 } 源码 public CollectionV values() {CollectionV vs values;if (vs null) {vs new Values();values vs;}return vs;} 7.entrySet方法 返回所有的key--value的映射关系  可以理解为keySet和values的集合版本 SetMap.EntryString,Integer set map.entrySet();for (Map.EntryString,Integer entry:set) {System.out.println(key entry.getKey() value entry.getValue());}// 也可以直接打印System.out.println(set); // 输出[apple14, bank15, cat17] 注意getKey和getValue是Map的内部类Entry中的方法  用于返回映射中的key和value所以entrySet方法就相当于创建的Set集合中存储是一个一个Entry 注意 1.Map是一个接口只能实例化实现他的类如TreeMap和HashMap区别在于前者的底层是二叉搜索树后者的底层是哈希表 2.Map中存放的key是唯一的二叉搜索树中不能存放相同的值哈希表中也不能存放相同的值 3.在TreeMap中插入键值对时key不能为空否则就会抛NullPointerException异常因为每次put一个元素都需要进行一次比较而null不能直接比较value可以为空。但 是HashMap的key和value都可以为空 4.利用keySet方法可以将所有的key分离出来(存放到set里)利用values方法可以将所有的value分离出来(存放到Collection内部) 5.Map中的key不能直接修改value可以修改(会直接被替换为新值在二叉搜索树的put代码中就有) 四.set的使用 1.概念 Set是一种纯key模型的数据集合容器 Set是继承自Collection的接口类也就是Set中不存在values 2.常用方法 3.注意事项 1.TreeSet的底层其实是TreeMap所以TreeSet无法存放相同的key值 2.Set实现了toString方法可以直接打印 SetString set new TreeSet();set.add(lvzi);set.add(biandu);set.add(zhizi);System.out.println(set);// 输出[biandu, lvzi, zhizi] 3.Set最大的功能就是对集合中的元素进行去重 4.实现Set接口的常用类有TreeSet和HashSet还有一个LinkedHashSetLinkedHashSet是在HashSet的基础 上维护了一个双向链表来记录元素的插入次序。 5.TreeSet中不能插入null的keyHashSet可以 五.哈希表 1.概念 1.什么是哈希表 在顺序结构和平衡二叉树的存储结构中如果想要搜索某个元素必须要进行关键码(key)的多次比较顺序结构的搜索效率是O(N),平衡二叉树的搜索效率是O(logN)搜索的效率取决于比较的次数其实之所以需要比较是因为关键码(key)和值val之间没有一一对应的关系如果建立一种关键码与存储位置一一对应的映射那么我们就能不进行比较就拿到我们需要的数据。比如对于一个正比例函数x与y之间是一一对应的可以说y与x建立了一一对应的映射关系。 在计算机中也有这样的一种数据结构叫做哈希表实现了关键码和存储位置的一一映射 这种映射关系通过哈希函数(hashFunc)建立就像正比例函数建立x与y的映射关系一样 比如哈希函数设置为hash(key) key % capacity; capacity为存储元素底层空间总的大小。 2.哈希冲突 如上图如果我们再存储一个1414%10 4,他的索引应该是4但是4下标对应关键字key不能再对应14了此时就发生了哈希冲突 3.负载因子 α 填入表中的元素 / 哈希表的长度 负载因子α用于定性描述冲突率研究表明负载因子越大发生哈希冲突的概率就越大负载因子越小发生哈希冲突的概率就越小  2.常见的哈希函数 1.直接定制法 利用关键字的特性建立线性函数实现关键字和存储位置的一一对应 适用于数据简单连续的情况 相关面试题 387. 字符串中的第一个唯一字符 - 力扣LeetCode 思路分析 代码实现 // 1.使用计数数组 class Solution {public int firstUniqChar(String s) {int[] cnt new int[26];for (int i 0; i s.length(); i) {cnt[s.charAt(i) - a];}for (int i 0; i s.length(); i) {if(cnt[s.charAt(i) - a] 1) {return i;}}return -1;} }2.// 使用哈希表MapCharacter,Integer map new HashMap();for (int i 0; i s.length(); i) {char ch s.charAt(i);// 先获取ch的val 无论是否存在 是要val1 不存在 设置默认值为0 更新后变为1map.put(ch,map.getOrDefault(ch,0)1);}for (int i 0; i s.length(); i) {if(map.get(s.charAt(i)) 1) {return i;}}return -1;2.其他 当然还有其他方法这里仅作了解即可  2. 除留余数法--(常用) 设散列表中允许的地址数为m取一个不大于m但最接近或者等于m的质数p作为除数按照哈希函数 Hash(key) key% p(pm),将关键码转换成哈希地址 3. 平方取中法--(了解) 假设关键字为1234对它平方就是1522756抽取中间的3位227作为哈希地址 再比如关键字为4321对 它平方就是18671041抽取中间的3位671(或710)作为哈希地址 平方取中法比较适合不知道关键字的分 布而位数又不是很大的情况 4. 折叠法--(了解)  折叠法是将关键字从左到右分割成位数相等的几部分(最后一部分位数可以短些)然后将这几部分叠加求和 并按散列表表长取后几位作为散列地址。 折叠法适合事先不需要知道关键字的分布适合关键字位数比较多的情况 5. 随机数法--(了解) 选择一个随机函数取关键字的随机函数值为它的哈希地址即H(key) random(key),其中random为随机数 函数。 通常应用于关键字长度不等时采用此法 6. 数学分析法--(了解) 设有n个d位数每一位可能有r种不同的符号这r种不同的符号在各位上出现的频率不一定相同可能在某 些位上分布比较均匀每种符号出现的机会均等在某些位上分布不均匀只有某几种符号经常出现。可根据 散列表的大小选择其中各种符号分布均匀的若干位作为散列地址。例如 假设要存储某家公司员工登记表如果用手机号作为关键字那么极有可能前7位都是 相同的那么我们可以 选择后面的四位作为散列地址如果这样的抽取工作还容易出现 冲突还可以对抽取出来的数字进行反转(如 1234改成4321)、右环位移(如1234改成4123)、左环移位、前两数与后两数叠加(如1234改成123446)等方 法。 数字分析法通常适合处理关键字位数比较大的情况如果事先知道关键字的分布且关键字的若干位分布较均 匀的情况 哈希函数设置的越巧妙越能减少哈希冲突但无法避免哈希冲突  那如何解决哈希冲突呢常用的方法有两种开散列和闭散列 3.哈希冲突的解决 1.闭散列 也叫开放定址法当遇到哈希冲突时如果此时哈希表未满则证明一定有下标未填充数据可以将发生哈希冲突的数据填入到空位所以闭散列的关键是寻找空位寻找空位有两种方式线性探测和二次探测 1.线性探测 所谓的线性探测就是从发生冲突的位置开始依次向后寻找空位 先利用哈希函数获得元素的插入位置如果为空直接插入不为空进行线性探测 2.二次探测  线性探测是从发生冲突的位置依次向后寻找空位可能会导致数据过于集中为了避免这种情况可以采用二次探测来寻找空位 研究表明当表的长度为质数且表装载因子a不超过0.5时新的表项一定能够插入而且任何一个位置都不 会被探查两次。因此只要表中有一半的空位置就不会存在表满的问题。在搜索时可以不考虑表装满的情 况但在插入时必须确保表的装载因子a不超过0.5如果超出必须考虑增容 无论是二次探测还是线性探测都需要开辟大量的空间来解决哈希冲突所以通过闭散列的方式来解决哈希冲突会导致空间利用率不高更合理的解决哈希冲突的方式是开散列也是Java中JDK的解决方式 2.开散列(重点) 1.概念 开散列是通过哈希桶的方式来解决哈希冲突的所谓的哈希桶就是一个特殊的数组数组的每个元素都是链表 这样就算发生了哈希冲突即产生了相同的下标不需要再去探测空位置而是在当前位置插入形成一个链表搜索数据时需要先定位到下标再去遍历下标位置对应的整个链表知道找到要搜索的数据 注意 如果同时满足数组的长度 64 链表的长度 8 此时性能就会下降可以通过红黑树来提高性能 2.哈希桶的模拟实现 1.当存储的数据是整数(Key的数据类型为Integer) 前期准备 // 哈希表实际上一个数组 数组的元素是Nodestatic class Node {public int key;public int val;public Node next;public Node(int key, int val) {this.key key;this.val val;}}public Node[] arr;public int usedSize;public HashBuck() {arr new Node[5];} 1.search 寻找哈希表中是否存在某个元素存在返回true不存在返回false public boolean search(int key) {int index key % arr.length;Node cur arr[index];while (cur ! null) {if (cur.key key) {return true;}}return false;} 2.put 向哈希表中插入数据  需要先判断是否已经存在要插入的元素  如果存在更新val不存在直接插入(哈希表中不能存放两个相同的key) 同时随着哈希表中的数据增多要去判断冲突因子的是否合理当同时满足数组的长度 64 链表的长度 8时需要重新更新哈希表的长度来降低负载因子的大小降低哈希冲突更新长度需要重新哈希因为可能发生数据的移动 // put方法public void put(int key,int val) {int index key % arr.length;Node cur arr[index];// 遍历整个链表 看是否已经存在相同的key值while (cur ! null) {if (cur.key key) {cur.val val;return;}cur cur.next;}// 头插法 arr[index]其实就是链表的头节点Node newNode new Node(key,val);newNode.next arr[index];arr[index] newNode;usedSize;// 判断负载因子此时是否合理if(loadFactor() 0.75) {resize();}}private double loadFactor() {return usedSize*1.0 / arr.length;}private void resize() {// 扩大到原来的两倍Node[] tmpArr new Node[arr.length*2];// 扩容之后 要进行重新哈希for (int i 0; i arr.length ; i) {Node cur arr[i];while (cur ! null) {Node curNext cur.next;int newIndex cur.key % tmpArr.length;// 头插cur.next tmpArr[newIndex];tmpArr[newIndex] cur;cur curNext;}}//数组是对象arr tmpArr;} 3.get 返回关键码Key对应的value  如果不存在返回-1 // getpublic int get(int key) {int index key % arr.length;Node cur arr[index];while (cur ! null) {if(cur.key key) {return cur.val;}cur cur.next;}return -1;} 2.数据类型是引用类型 1.前提准备 创建person类 /*** 数据类型是引用类型 即Key是引用* 建立引用类Person 与 与 id的映射关系*/ class Person {public String id;public Person(String id) {this.id id;}// 存储的对象是一个一个人 key是一个引用类型 想要将类型转化为整数 并比较大小 重写方法Overridepublic boolean equals(Object object) {if (this object) return true;if (object null || getClass() ! object.getClass()) return false;Person person (Person) object;return id person.id;}Overridepublic int hashCode() {return Objects.hash(id);} } // K,V都是泛型 一定要在类的声明中添加static class NodeK,V {public K key;public V val;public NodeK,V next;public Node(K key, V val) {this.key key;this.val val;}}public NodeK,V[] arr;public int usedSize;public HashBuck2() {arr (NodeK, V[]) new Node[5];} 2.put 对于Person类来说不能直接让他%length来获取他的下标只能通过hashCode来获取其hash值再让hash值取%length从而获取他的下标 // put在hash中插入数据public void put(K key, V val) {// 先获取对应的hash值int hash key.hashCode();int index hash % arr.length;NodeK,V cur arr[index];while (cur ! null) {if(cur.key.equals(key)) {cur.val val;return;}cur cur.next;}NodeK,V newNode new Node(key,val);newNode.next arr[index];arr[index] newNode;usedSize;// 判断负载因子此时是否合理if(loadFactor() 0.75) {resize();}}private double loadFactor() {return usedSize*1.0 / arr.length;}private void resize() {// 扩大到原来的两倍NodeK,V[] tmpArr new Node[arr.length*2];// 扩容之后 要进行重新哈希for (int i 0; i arr.length ; i) {Node cur arr[i];while (cur ! null) {Node curNext cur.next;int newIndex cur.key.hashCode() % tmpArr.length;// 头插cur.next tmpArr[newIndex];tmpArr[newIndex] cur;cur curNext;}}//数组是对象arr tmpArr;} 3.get // get 根据key获得他的valpublic V get(K key) {int hash key.hashCode();int index hash % arr.length;NodeK,V cur arr[index];while(cur ! null) {if(cur.key.equals(key)) {return cur.val;}cur cur.next;}return null;}4.测试 public static void main(String[] args) {Person person1 new Person(123);Person person2 new Person(123);HashBuck2Person,String hashBuck2 new HashBuck2();hashBuck2.put(person1,lvzi);// 输出相同的结果 因为Person中重写了equals和hashcode方法 只要内容相同 就认为他们是相同的且具有相同的hashcode值// get方法就是根据hashcode方法得到hashcodeSystem.out.println(hashBuck2.get(person1));// 输出lvziSystem.out.println(hashBuck2.get(person2));// 输出lvzi} 注意 1.在链表中插入数据时有两种方法头插或尾插jdk1.8之前采用的是头插1.8之后采用的是尾插 2.哈希表的所有方法的时间复杂度都是O(1),获取index遍历链表链表的长度一定是常数的因为有loadFactor的调节链表长度过长会使用红黑树来调整 3.对于引用类型来说要重写equals和hashCode方法重写equals方法是因为在比较的时候需要通过内容来进行比较重写hashCode方法便于利用数字定位类也便于我们进行存储 4.equals方法和hashCode方法往往是要同时重写的这是因为在使用哈希表等数据结构时要保证搜索的一致性 如果两个对象通过equals比较返回true则他们的hashCode值应该相同如果两个对象通过equals比较返回false则他们的hashCode值不必须一定不同但为了检索的效率一般设置为不同 equals方法和hashCode方法的区别 六.hashMap的一些源码讲解 1.成员变量讲解 // 默认容量是16static final int DEFAULT_INITIAL_CAPACITY 1 4; // aka 16// 最大容量static final int MAXIMUM_CAPACITY 1 30;// 负载因子static final float DEFAULT_LOAD_FACTOR 0.75f; 以上三个都很好理解下面看几个较为重要但又陌生的成员变量 // 树化条件 每个链表中的结点8static final int TREEIFY_THRESHOLD 8;// 解树化条件static final int UNTREEIFY_THRESHOLD 6;// 最小的树华条件 哈希表的容量最小是64static final int MIN_TREEIFY_CAPACITY 64; 2.构造方法 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);} 为什么哈希表的容量一定要是2的次幂呢主要有以下两方面原因: 效率性能方面在建立映射的过程中最重要的一步在于获取到要插入数据的下标在上述的模拟实现中我们采用的是 hash % arr.length()的方法但还有另一种更快的方法就是使用位运算当哈希表的容量n为2的次幂时 hash % n hash (n-1),这两种求下标的方式是等价的使用运算可以大大提高运算速度哈希函数与桶的关系使用hash (n-1)这种方式来获取下标还可以使元素分布更加均匀降低哈希碰撞的可能性 当n为2的次幂时(4,16,32,64......)时n-1就是一个每个二进制位都为1的特殊二进制数(15--1111),再通过hash (n-1)实际上是对hash的每一位都进行了操作这种运算等价于hash % n但是运算效率更高为什么等价呢下面附上证明: 抹除高位只保留低位就相当于减去高位中所有的2^n的倍数(字丑见谅)  public HashMap() {this.loadFactor DEFAULT_LOAD_FACTOR; // all other fields defaulted} 注意看这是我们常用的构造方法但是这里面只设置了负载因子并没有给容量但是我们却能够直接使用这是为什么呢答案在put方法的源码之中 public V put(K key, V value) {// 调用putVal方法return putVal(hash(key), key, value, false, true);}
http://www.pierceye.com/news/420048/

相关文章:

  • 品牌网站升级wordpress 4.9中文
  • 网站搭建软件广告标识标牌制作公司
  • 做造价在哪个网站查价格微信小程序是什么语言开发的
  • 网站建设平台接单做电子商务平台网站需要多少钱
  • 甘肃网站seo技术厂家企业简介内容
  • 视觉中国设计网站做音乐网站
  • 金坛区建设工程质量监督网站西宁百姓网免费发布信息
  • 运维 网站开发网站如何引入流量
  • 网站建设泉州效率网络西安网站设计公司哪家好
  • 青羊建站报价网上能注册公司吗怎么注册
  • 免费网站虚拟主机整站seo技术搜索引擎优化
  • 青岛网站建设订做油画风网站
  • 网站备案名称的影响吗六安哪里有做推广网站
  • 网站建设策划书网站发布与推广长沙公司网站费用
  • 设计网页英语口碑优化seo
  • 试客那个网站做的好北京做软件开发的公司
  • 网站多套系统如何调用网页大图素材
  • 沧州网站建设方案咨询wordpress需要 伪静态
  • 安徽省住房和城乡建设部网站郑州百姓网免费发布信息
  • 电子商务网站开发是指聊城市城乡建设部网站查询
  • 聊天室网站模板国内市场调研公司
  • 网站做不做备案有什么区别网站媒体给房开做内容推广
  • 昆明专业网站建设模板蚌埠app制作公司
  • 平面ui设计网站网页布局设计类型
  • 东莞企石网站建设网站怎么留住用户
  • 公司网站虚假宣传但网站不是我做的wordpress自豪地采用修改
  • 山西大同网站建设哪家好宜宾seo快速排名
  • 网站为什么做黄词骗流量网站图标在哪里修改
  • 手机移动端网站建设青岛门户网站建设
  • 专业APP客户端做网站php完整电商网站开发源码