
目录

1、V put(K key, V value) //设置 key 对应的 value
- map.put(1,"hello");
- map.put(2,"world");
-
- //结果:{1=hello, 2=world}
2、V get(Object key) //返回 key 对应的 value
- map.get(1);
-
- //结果:hello
3、V getOrDefault(Object key, V defaultValue) //返回 key 对应的 value,key 不存在,返回默认值
- String str=map.getOrDefault(2,"hi");
- String str1=map.getOrDefault(3,"hi");
-
- //结果:
- //str world
- //str1 hi
4、V remove(Object key) //删除 key 对应的映射关系
- map.remove(1);
- System.out.println(map);
-
- //结果:{2=world}
5、Set<K> keySet() 返回所有 key 的不重复集合
- map.put(1,"hi");
- map.put(2,"thanks");
- map.put(2,"what");
- map.put(3,"how");
- System.out.println(map.keySet());
-
- //结果:[1, 2, 3]
6、Set<Map.Entry<K, V>> entrySet() //返回所有的 key-value 映射关系
- map.put(1,"hi");
- map.put(2,"thanks");
- map.put(3,"how");
- for(Map.Entry<Integer, String> entry : map.entrySet()){
- System.out.println(entry.getKey() + "--->" + entry.getValue());
- }
- System.out.println();
-
- //结果:
- 1--->hi
- 2--->thanks
- 3--->how
7、boolean containsKey(Object key) //判断是否包含 key
- map.put(1,"hi");
- map.put(2,"thanks");
- map.put(3,"how");
- map.containsKey(1);//true
- map.containsKey(5);//false
- map.containsValue("hi");//true
- map.containsValue("hello");//false
- K getKey() //返回 entry 中的 key
- V getValue() //返回 entry 中的 value
- V setValue(V value) //将键值对中的value替换为指定value
注意:
2. Map中存放键值对的Key是唯一的,value是可以重复的 。


4. Map中的Key可以全部分离出来,存储到Set中来进行访问(因为Key不能重复)。
5. Map中的value可以全部分离出来,存储在Collection的任何一个子集合中(value可能有重复)。

1、boolean add(E e) //添加元素,但重复元素不会被添加成功
- Set<Integer> set=new HashSet<>();
- set.add(1);
- set.add(2);
- set.add(3);
- boolean flg=set.add(1);
- System.out.println(flg);
- System.out.println(set);
-
- //结果
- //false
- //[1, 2, 3]
2、boolean contains(Object o) //判断 o 是否在集合中
- boolean flg=set.contains(1);//true
- boolean flg1=set.contains(6);//false
3、Iterator<E> iterator() //返回迭代器
- set.add(1);
- set.add(2);
- set.add(3);
- Iterator it= set.iterator();
- while(it.hasNext()){
- System.out.print(it.next()+" ");
- }
-
- //结果:1 2 3
4、boolean remove(Object o) //删除集合中的 o
- set.add(1);
- set.add(2);
- set.add(3);
- boolean flg2=set.remove(1);
- System.out.println(flg2);//true
- System.out.println(set);//[2, 3]
5、int size() //返回set中元素的个数
- set.add(1);
- set.add(2);
- set.add(3);
- System.out.println(set.size());//3
6、boolean isEmpty() //检测set是否为空,空返回true,否则返回false
7、Object[] toArray() //将set中的元素转换为数组返回
- Set<Object> set1=new HashSet<>();
- Object[] array1=set1.toArray();
-
- Set<Integer> set2=new HashSet<>();//指定类型
- Integer[] array=set2.toArray(new Integer[0]);
-
5. 实现Set接口的常用类有TreeSet和HashSet,还有一个LinkedHashSet,LinkedHashSet是在HashSet的基础 上维护了一个双向链表来记录元素的插入次序。
1、在1_0000个数据里面找重复的数据
- public static Map<Integer,Integer> func(int[] array){
- //判断array中的元素是否在map中,如果不在就是1,如果在就在原来的基础上加1
- Map<Integer,Integer> map=new HashMap<>();
- for(int x:array){
- if(map.get(x)==null){
- map.put(x,1);
- }else{
- int val=map.get(x);
- map.put(x,val+1);
- }
- }
- return map;
- }
-
- public static void main(String[] args) {
- int[] array=new int[10000];
- Random random=new Random();
- for(int i=0;i< array.length;i++){
- array[i]=random.nextInt(1000);//生成的随机数在0-1000之间,一定有重复的元素
- }
- Map<Integer,Integer> map=func(array);
- System.out.println(map);
- String str="array";
- System.out.println(str.toCharArray());
- }
2、将10000个数据去重
- public static Set<Integer> func(int[] array){
- Set<Integer> set=new HashSet<>();
- for (int x:array) {
- set.add(x);
- }
- return set;
- }
-
- public static void main(String[] args) {
- int[] array=new int[10000];
- Random random=new Random();
- for (int i = 0; i < array.length; i++) {
- array[i]=random.nextInt(1000);
- }
- Set<Integer> set=func(array);
- System.out.println(set);
- int ret=func1(array);
- System.out.println(ret);
- }

🍌查找
在二叉搜索树中查找某个值key,就用key和根节点比较,key更大,在右子树查找,key更小,在左子树查找。

代码示例:
- public Node search(int key){
- Node cur=root;
- while(cur!=null){
- if(key>cur.val){
- cur=cur.right;
- }else if(key<cur.val){
- cur=cur.left;
- }else{
- return cur;
- }
- }
- return null;//没找到
- }
🍌插入
在二叉搜索树中插入某个结点,要不破坏它的性质,所以要先找到合适插入的位置,然后再插入
- public boolean insert(int val){
- //空树情况下
- if(root==null){
- root=new Node(val);
- return true;
- }
-
- //找到要插入的位置
- Node cur=root;
- Node parent=null;//记录父节点
- while(cur!=null) {
- if (val > cur.val) {
- parent = cur;
- cur = cur.right;
- } else if (val < cur.val) {
- parent = cur;
- cur = cur.left;
- } else {
- return false;//不能有相同数据
- }
- }
-
- //插入
- Node tmp=new Node(val);
- if(val<parent.val){
- parent.left=tmp;
- }else{
- parent.right=tmp;
- }
- return true;
- }
🍌删除
删除二叉搜索树中某个结点,先找到要删除的位置,在进行删除

代码示例:
- public void remove(int key){
- Node cur=root;
- Node parent=null;
- //找到要删除结点位置
- while(cur!=null){
- if(cur.val==key){
- removeNode(cur,parent);
- break;
- }else if(cur.val<key){
- parent=cur;
- cur=cur.right;
- }else{
- parent=cur;
- cur=cur.left;
- }
- }
- }
-
- //删除
- public void removeNode(Node cur,Node parent){
- if(cur.left==null){
- if(cur==root){
- root=cur.right;
- }else if(cur==parent.left){
- parent.left=cur.right;
- }else if(cur==parent.right){
- parent.right=cur.right;
- }
- }else if(cur.right==null){
- if(cur==root){
- root=root.left;
- }else if(cur==parent.left){
- parent.left=cur.left;
- }else if(cur==parent.right){
- parent.right=cur.left;
- }
- }else{
- //右数找最小值,左数找最大值
- Node targetParent=cur;
- Node target=cur.right;
- 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.left;
- }
- }
- }
该方式即为哈希( 散列 ) 方法, 哈希方法中使用的转换函数称为哈希 ( 散列 ) 函数,构造出来的结构称为哈希表(Hash Table)(或者称散列表)。
我们可以看到4和14通过相同哈希函数找到了相同的位置,发生了冲突。
此时不同的关键字通过相同的哈希函数有可能找到相同的位置。此时的情况就叫: 哈希冲突/哈希碰撞。
散列表的载荷因子定义为: a =填入表中的元素个数/散列表的长度。
a是散列表装满程度的标志因子。由于表长是定值,a与“填入表中的元素个数”成正比,所以,a越大,表明填入表中的元素越多,产生冲突的可能性就越大。反之,a越小,标明填入表中的元素越少,产生冲突的可能性就越小。实际上,散列表的平均查找长度是载荷因子a的函数,只是不同处理冲突的方法有不同的函数。对于开放定址法,荷载因子是特别重要因素,应严格限制在0. 7-0.8以下。超过0.8,查表时的CPU缓存不命中( cachemissing)按照指数曲线上升。因此,一些采用开放定址法的hash库,如Java的系统库限制了荷载因子为0.75,超过此值将resize散列表。
随着负载因子的增大,冲突率也在增大,所以当冲突率达到一个无法忍受的程度时,我们需要通过降低负载因子来变相的降低冲突率。已知哈希表中已有的关键字个数是不可变的,那我们能调整的就只有哈希表中的数组的大小。
🌵闭散列
但是线性探测有一个不好的地方,它依次向后探测,直到寻找到下一个空位置为止,如果空位置是连续的那么产生冲突的元素都放在一起了。
🌵开散列
💧实现
- public class HashBuck {
-
- static class Node{
- public int key;
- public int val;
- Node next;
-
- public Node(int key,int val){
- this.key=key;
- this.val=val;
- }
- }
- public Node[] array;
- public int usedSize;
- public static final double DEFAULT_LOAD_FACTOR = 0.75;//负载因子
- public HashBuck(){
- this.array=new Node[10];
- }
- /**
- * put函数
- * @param key
- * @param val
- */
- public void put(int key,int val){
- //1、确认key所在位置
- int index=key% array.length;
- Node cur=array[index];
- //2、遍历这个下标的链表,看是不是有相同的key,有的话要更新val值
- while(cur!=null){
- if(cur.key==key){
- cur.val=val;
- return;
- }
- cur=cur.next;
- }
- //3、没有key这个元素,用头插法插入
- Node node=new Node(key,val);
- node.next=array[index];
- array[index]=node;
- this.usedSize++;
- //4、插入元素成功之后,检查当前散列表的负载因子
- if(loadFactor()>=DEFAULT_LOAD_FACTOR){
- resize();//扩容
- }
- }
- private void resize(){
- Node[] newArray=new Node[array.length*2];
- for(int i=0;i< newArray.length;i++){
- Node cur=newArray[i];
- while(cur!=null){
- int index= cur.key% newArray.length;//获取新下标
- //就是把cur这个节点,以头插的形式 插入到新的数组对应下标的链表当中
- Node curNext=cur.next;
- cur.next=newArray[index];
- newArray[index]=cur;
- cur=curNext;
- }
- }
- array=newArray;
- }
- private double loadFactor() {
- return 1.0*usedSize/ array.length;
- }
-
- /**
- * 根据key获得val值
- * @param key
- * @return
- */
- public int get(int key){
- //1、确认key所在位置
- int index=key% array.length;
- Node cur=array[index];
- //2、遍历这个下标的链表,找到key,返回当前val值
- while(cur!=null){
- if(cur.key==key){
- return cur.val;
- }
- cur=cur.next;
- }
- return -1;
- }
- }
- class Person{
- public String ID;
- public Person(String ID){
- this.ID=ID;
- }
- //重写
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Person person = (Person) o;
- return Objects.equals(ID, person.ID);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(ID);
- }
-
- @Override
- public String toString() {
- return "Person{" +
- "ID='" + ID + '\'' +
- '}';
- }
- }
- public class HashBuck2<K,V> {
-
- static class Node<K,V>{
- public K key;
- public V val;
- public Node<K,V> next;
- public Node(K key,V val){
- this.key=key;
- this.val=val;
- }
- }
- public Node<K,V>[] array=( Node<K,V>[])new Node[10];
- int usedSize;
-
- /**
- * put函数
- * @param key
- * @param val
- */
- public void put(K key,V val){
- int hash=key.hashCode();
- int index=hash% array.length;
- Node<K,V> cur=array[index];
- while(cur!=null){
- //利用equals进行相等性比较
- if(cur.key.equals(key)){
- cur.val=val;
- return;
- }
- cur=cur.next;
- }
- Node<K,V> node=new Node<>(key,val);
- node.next=array[index];
- array[index]=node;
- this.usedSize++;
- }
-
- /**
- * 根据key获得val值
- * @param key
- * @return
- */
- public V get(K key){
- int hash=key.hashCode();
- int index=hash% array.length;
- Node<K,V> cur=array[index];
- while(cur!=null){
- if(cur.key.equals(key)){
- return cur.val;
- }
- cur=cur.next;
- }
- return null;
- }
常见问题:(看源码)
1.如果new HashMap(19), bucket数组多大?
>=19 &&最接近19的一个2次幂--32
2. HashMap什么时候开辟bucket数组占用内存?
第一次put的时候--16
3. hashMap何时扩容?
超过负载因子的时候扩容,且是2倍扩容的
4.当两个对象的hashcode相同会发生什么?
冲突
5.如果两个键的hashcode相同,你如何获取值对象?
遍历与hashCode值相等时相连的链表,直到相等(equals)或者null(没有找到)
6.你了解重新调整HashMap大小存在什么问题吗?
重新哈希原来的元素到新的链表当中
以上就是今天的内容了,有什么问题都可以在评论区留言哦✌✌✌
