Map集合
Map继承结构图
Map接口常用方法
Map接口的迭代方法
方案一:货物Map集合的所有key,然后遍历每个key,通过key货物value。
因为Set的父接口是Collection。
方案二:这种方式效率较高建议使用
--------------------------------------------------------------------------------------
HashMap集合底层原理图
存储的是Node节点,每个节点有key值,hash值,value值和下一个节点的内存地址。每次调用put()方法添加元素时,底层会new一个节点,并将K,V赋值给node对象。
1.不可重复性
内置类:字符串等调用底层的方法能保证不可重复性
public class HashTest { public static void main(String[] args) { HashMap<String,String> map = new HashMap<>(); map.put("11111111","张三"); map.put("11111111","张三");//如果key值相同,value会覆盖 //遍历 Set<Map.Entry<String, String>> entries = map.entrySet(); //entrySet()方法返回的是一个Set集合,而Set集合中存储的是Entry对象。 for(Map.Entry<String, String> entry :entries){ String idCard = entry.getKey(); String name = entry.getValue(); System.out.println(idCard + "=" + name);//字符串时内置的类,所以会覆盖 } } }
如果不是内置类,重写equals方法和hashcode方法
main类
/*** * HashMap集合:如果euqals方法返回的true,那么hashCode()方法放回的结果就必须相同 */ public class UserhashTest { public static void main(String[] args) { //User是自定义的类 HashMap<User,Integer> map = new HashMap<>(); User user1 = new User("张三", 15); User user2 = new User("李四", 15); User user3 = new User("王五", 15); User user4 = new User("赵六", 15); User user5 = new User("张三", 15); map.put(user1,110); map.put(user2,111); map.put(user3,112); map.put(user4,113); map.put(user5,114); //遍历 //获取Map集合的所有key,然后遍历每个Key, //通过key获取value。因为KeySet()方法是Set接口的方法, //Set接口又继承了Collection,所以用迭代器遍历。 Set<User> users = map.keySet(); for (User user: users){ Integer value = map.get(user); System.out.println(user + "===>" + value); } } }
User类
public class User { private String name; private int age; public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{"+ "name='" + name +'\'' + ",age=" + age + "}'"; } @Override public boolean equals(Object object) { if (this == object) return true; if (object == null || getClass() != object.getClass()) return false; User user = (User) object; return age == user.age && Objects.equals(name, user.name); } @Override public int hashCode() { return name.hashCode()+this.age; } }
原理
在自定义类型中要重写erqual和hash函数,因为如果不重写hash函数的话,是根据user实例的内存地址来计算的。
2.HashMap集合的key可以是null吗?
固定规则:如果key为null,固定存在数组索引为0处
//证明key可以为null,但只能有1个 public class HashMapNUll { public static void main(String[] args) { HashMap<Object, Object> map = new HashMap<>(); map.put(null,"zhangsan"); System.out.println(map.size());//输出结果为1 } }
3.手撕hashmap
package com.powernode.MyhashMap; /* 手写HashMap的put和get方法 */ public class HashMapDemo<K,V> { /**哈希表*/ private Node<K,V>[] table; /** 节点个数*/ private int size; public HashMapDemo(){ this.table = new Node[16]; } /**节点的返回个数*/ public int getSize(){ return size; } /**向集合中添加一个键值对 * */ public V put(K key, V value){ if( key == null){ return putForNullKey(value); } //key不是null //获取hash值 int hash = key.hashCode(); //将hash值转为数组下标 int index = Math.abs(hash % table.length); Node<K,V> node = table[index]; if(null == node){ table[index] = new Node<>(hash,key,value,null); size++; return value; } //如果有单项链表,遍历单项链表为尾插法 Node<K,V> prev = null; while (node != null){ if(node.key.equals(key)){ V oldvalue = node.value; node.value = value; return oldvalue; } prev = node; node = node.next; } prev.next = new Node<>(hash,key,value,null); return value; } private V putForNullKey(V value) { Node<K,V> node = table[0]; if(node == null){ table[0] = new Node<>(0,null,value,null); size++; return value; }else{ Node<K,V> prev = null; while (node != null){ if(node.key == null){ V oldvalue = node.value; node.value = value; return oldvalue; } prev = node; node = node.next; } prev.next = new Node<>(0,null,value,null); size++; return value; } } public V get(K key){ if(null == key){ Node<K,V> node = table[0]; if(null == node){ return null; } while (node !=null){ if(null == key){ V indexvalue = node.value; return indexvalue; } node = node.next; } } //key不为空 int hash = key.hashCode(); int index = Math.abs(hash % table.length); Node<K,V> node = table[index]; if(node.key == key){ return node.value; } while (null != node){ if(node.key == key){ return node.value; } node = node.next; } return null; } /** * 重写toString方法,直接输出Map集合时会调用 */ @Override public String toString() { //字符串拼接 StringBuilder sb = new StringBuilder(); for (int i = 0;i<table.length;i++){ Node<K,V> node = table[i]; while (null !=node){ sb.append(node); sb.append("\n"); node = node.next; } } return sb.toString(); } /**节点类*/ static class Node<K,V>{ /** * key的hashCode()方法的返回值 * 哈希值,哈希码 */ int hash; K key; V value; //下一个节点的内存地址 Node<K,V> next; /** * * @param hash 哈希值 * @param key 健 * @param value 值 * @param next 下一个节点的地址 */ public Node(int hash, K key, V value, Node<K, V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } @Override public String toString() { return "["+hash+","+key+","+value+"]"; } } }
4.HashMap在java8前后变化
5.HashMap的长度永远是2次幂