Map是一个接口,其包含了多个实现类。Map是利用键值对的方式,来存储的。Key相当于扩大了索引的内容,不再局限于数组中的数字。
HashMap
HashMap的底层实现采用了Hash表,这是一种非常重要的数据结构。key的hashcode值用于分割其在Entry[]中的位置,并在后面存储数据。具有极快的访问速度,但是其遍历顺序却是不确定的(因为在Hashmap的散列里,我们利用的散列方法会导致其遍历顺序的变化)。HashMap最多只允许记录一条键为null的记录。同时HashMap非线程安全的。如果想满足线程安全,可以使用synchronizedMap或者ConcurrentHashMap。
HashMap在Java7中的实现是利用的一个数组+链表。数组中每个元素都是一个单向链表
而HashMap为了进一步提高效率,在Java8中,引入了红黑树。
HashMap在Java8中引入了红黑树,当链表的长度大于8之后,Java就会自动将链表转换成红黑树。将此前查找的时间复杂度由O(n)下降到了O(logn)
下面我们实现一种最基础的数组加链表形式的Map。分别要实现其put ,get 功能,并且要保证Key的唯一性。同时,我们重写toString方法,方便我们遍历整个Map。并且使用泛型操作。
package 集合.Map.手写HashMap; import java.util.Arrays; /** * @program:多线程和IO * @descripton: * @author:ZhengCheng * @create:2021/10/11-19:43 **/ public class HashMap_ <T,E>{ private Node[] Entry ; private int size ; public HashMap_() { init(); } private void init() { size = 16; Entry = new Node[size]; } //key对应的HashCode private int gethashCode(T key){ return key.hashCode(); } public void put(T key,E val){ int hash = gethashCode(key); int index = getPosition(hash); if (Entry[index] == null){ Entry[index] = new Node(hash,key,val); }else { //遍历列表判断是否冲突 Node check = checkHash(hash,index); if (check == null){ Node temp = Entry[index]; while (temp.next == null){ temp.next = new Node(hash,key,val); } }else { System.out.println("冲突,添加失败"); return; } } } private Node checkHash(int hash ,int index) { Node temp = Entry[index]; while (temp.next!=null && temp.getHash()!=hash){ temp = temp.next; } if (temp.next == null && temp.getHash() !=hash){ return null; } return temp; } private int getPosition(int hash){ return hash&(size-1); } public E get(T key){ int hash = gethashCode(key); //比较有无hash?如何快速比较呢?没有方法 int index = getPosition(hash); Node temp =checkHash(hash,index); if (temp != null){ E v = (E) temp.getVal(); return v; }else { System.out.println("无"); } return null; } //方便查看map里的键值对内容 遍历数组遍历链表 @Override public String toString() { String res = ""; for (int i = 0; i < Entry.length; i++) { if (Entry[i] != null){ //遍历链表 Node tmp = Entry[i]; while (tmp.next != null){ res += tmp.toString(); tmp = tmp.next; } res += tmp.toString(); } } return res; } } class Node <T,E>{ public int hash; private T key; private E val; public Node next ; public int getHash() { return hash; } public void setHash(int hash) { this.hash = hash; } public T getKey() { return key; } public void setKey(T key) { this.key = key; } public E getVal() { return val; } @Override public String toString() { return "Node{" + "key=" + key + ", val=" + val + '}'; } public void setVal(E val) { this.val = val; } public Node(int hash, T key, E val) { this.hash = hash; this.key = key; this.val = val; } }public class testDemo { public static void main(String[] args) { HashMap_<Integer, Integer> map = new HashMap_<>(); map.put(1,23); map.put(1,22); map.put(2,1); map.put(3,1); map.put(4,1); Integer i = map.get(1); System.out.println(i); System.out.println(map); } }冲突,添加失败
23
Node{key=1, val=23}Node{key=2, val=1}Node{key=3, val=1}Node{key=4, val=1}Process finished with exit code 0
TreeMap
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io.Serializableprivate final Comparator<? super K> comparator; private transient Entry<K,V> root; private transient int size = 0; private transient int modCount = 0;
TreeMap实现了SortedMap接口(实现了NavigatableMap ,NM是实现了Sorted的),能够把它保存的记录根据键的排序(默认Comparable升序或者自己使用Comparable重新排序)可以用Iterator遍历Treemap。
当我们需要排序的映射时,建议使用TreeMap。
注意:在使用TreeMap时,key必须要实现Comparable接口,或者在构造TreeMap时传入Comparator,否则会在运行中抛出ClassCastException。
后续的ConcurrentHashMap,HashTableLinkHashMap在提到了再深入了解。