一、使用Java模拟单向链表
/** * 节点 */ public class Node<E> { // 数据域 public E data; // 指针域 public Node<E> next; public Node(E value) { this.data = value; } /** * 显示方法 */ public void display() { System.out.print(data + " "); } @Override protected void finalize() throws Throwable { System.out.println("回收Node对象。。。"); super.finalize(); } }
/** * 模拟单向链表*/ public class MyLinkedList<E> { // 头节点 public Node<E> first; // 尾节点 public Node<E> last; // 链表的元素个数 public int size; public MyLinkedList() { } public int size() { return size; } public boolean isEmpty() { return size == 0; } /** * 在头节点前插入一个节点(头插) */ public void addFirst(E value) { Node<E> node = new Node<>(value); // if (first == null || last == null) { // 此时链表还没有节点 if (isEmpty()) { // 如果链表还没有节点 first = node; last = node; size++; return; } // 头插 node.next = first; first = node; size++; } /** * 在尾节点后插入一个节点(尾插) */ public void add(E value) { Node<E> node = new Node<>(value); // if (first == null || last == null) { // 此时链表还没有节点 if (isEmpty()) { // 如果链表还没有节点 first = node; last = node; size++; return; } // 尾插 last.next = node; last = node; size++; } /** * 删除头节点 */ public Node<E> deleteFirst() { Node<E> tmp = first; // if(size() == 1) { if (first.next == null) { last = null; } first = tmp.next; size--; return tmp; } /** * 查找方法 */ public Node<E> find(E value) { Node<E> current = first; while (!value.equals(current.data)) { if (current.next == null) { return null; } current = current.next; } return current; } /** * 显示方法 */ public void display() { Node<E> current = first; while (current != null) { current.display(); current = current.next; } System.out.println(); } }
测试:
public class Demo1 { public static void main(String[] args) { MyLinkedList<Integer> list = new MyLinkedList<>(); list.add(1); System.out.println("链表头节点:" + list.first.data); // 链表头节点:1 System.out.println("链表尾节点:" + list.last.data); // 链表头节点:1 System.out.print("list.dispaly(): "); list.display(); // list.dispaly(): 1 System.out.println("size: " + list.size()); // size: 1 list.deleteFirst(); // 删除头节点 System.gc(); // 被删除的头节点的内存被系统回收 try { Thread.sleep(1000 * 1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.print("list.dispaly(): "); list.display(); System.out.println("======= 分割线 ======="); list.add(10); list.add(20); System.out.println("链表头节点:" + list.first.data); // 链表头节点:10 System.out.println("链表尾节点:" + list.last.data); // 链表头节点:20 System.out.print("list.dispaly(): "); list.display(); // list.dispaly(): 10 20 System.out.println("size: " + list.size()); // size: 2 list.deleteFirst(); // 删除头节点 System.out.println("链表头节点:" + list.first.data); // 链表头节点:20 System.out.println("链表尾节点:" + list.last.data); // 链表头节点:20 System.out.println("size: " + list.size()); // size: 1 System.gc(); // 被删除的头节点的内存被系统回收 try { Thread.sleep(1000 * 1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("======= 分割线 ======="); list.add(30); list.addFirst(-10); list.addFirst(-30); list.display(); // list.dispaly(): -30 -10 20 30 Node<Integer> node = list.find(-10); System.out.println(node.data); // -10 Node<Integer> node2 = list.find(10); // node2 = null //System.out.println(node2.data); // NullPointerException } }
二、jdk中使用单向链表的源码--HashMap
我们知道jdk7的HashMap的底层实现是数组+单链表,jdk8的HashMap的底层实现是数组+单链表+红黑树。下面分析源码:
// jdk1.7.0_60
public class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, Serializable { public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null) return putForNullKey(value); int hash = hash(key); int i = indexFor(hash, table.length); for (Entry<K, V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; } static class Entry<K, V> implements Map.Entry<K, V> { final K key; V value; Entry<K, V> next; int hash; Entry(int h, K k, V v, Entry<K, V> n) { value = v; next = n; key = k; hash = h; } } void addEntry(int hash, K key, V value, int bucketIndex) { if ((size >= threshold) && (null != table[bucketIndex])) { resize(2 * table.length); hash = (null != key) ? hash(key) : 0; bucketIndex = indexFor(hash, table.length); } createEntry(hash, key, value, bucketIndex); } void createEntry(int hash, K key, V value, int bucketIndex) { Entry<K, V> e = table[bucketIndex]; // 新建一个Entry,指针域指向e,即指向原来的table[bucketIndex] // 所以,jdk7中单链表添加数据时使用头插 table[bucketIndex] = new Entry<>(hash, key, value, e); size++; } }
// jdk1.8.0_111
public class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, Serializable { public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K, V>[] tab; Node<K, V> p; int n, i; // 哈希表为空,第一次扩容 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; // 当前哈希桶为空,直接添加到当前哈希桶 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { // 当前哈希桶不为空,则与哈希桶存储的节点进行比较 Node<K, V> e; K k; // 哈希值相同,并且key也相同,覆盖 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) // 否则,添加到红黑树 e = ((TreeNode<K, V>) p).putTreeVal(this, tab, hash, key, value); else { // 或者,添加节点到单链表(还需要与链表中节点比较,相同就不添加) for (int binCount = 0;; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); // 尾插 if (binCount >= TREEIFY_THRESHOLD - 1) // 节点超过8个单链表转换成红黑树 treeifyBin(tab, hash); break; } // 如果与链表中某个节点相同,则不添加 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } // 哈希值相同,并且key也相同,覆盖 if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null; } }