JAVA数据结构-单链表和HashMap

java之单链表

单链表是一种物理存储单元上非连续、非顺序的存储结构。

单链表是由那几个部分组成的呢?
 是由N个节点组成的
       每一个节点分为两部分:
       1.数据域
       2.指针域

数据域用来存储数据,指针域用来链接各个节点。废话不多说,直接上代码!

节点的代码如下

public class Node<E> {
    private E e;// 数据域
    private Node<E> next;// 引用域
    public Node() {
    }
    public Node(E e) {
        this.e = e;
    }
    public E getE() {
        return e;
    }
    public void setE(E e) {
        this.e = e;
    }
    public Node<E> getNext() {
        return next;
    }
    public void setNext(Node<E> next) {
        this.next = next;
    }
单链表的代码如下

public class MyLinkedList<E> {

    //声明头节点
    private Node<E> root;
    private int size;//声明单链表中存储的节点数
    
    public MyLinkedList(){
        root = new Node<E>();//实例化头节点
    }
    
    /**
     * 向链表中添加元素的方法
     * @param e要添加的元素
     */
    public void add(E e){
        //根据e实例化了一个新的节点对象
        Node<E> node = new Node<E>(e);
        
        //获取root的下一个节点
        Node<E> tnode = root.getNext();
        
        root.setNext(node);//将新的节点作为root的下一个节点
        
        node.setNext(tnode);//将root原来的下一个节点作为新增加节点的下一个节点
        
        size++;//记录添加的节点数
    }
    
    /**
     * 删除指定索引位置的元素
     * @param index索引位置
     * @return 返回删除的元素
     */
    public E remove(int index){
        if(index <= 0 || index > size)
            return null;
        //获取要删除节点的前一个节点
        Node<E> node = select(index-1);
        //获取要删除的节点
        Node<E> dNode = node.getNext();
        //获取要删除节点的后一个节点
        Node<E> nNode = dNode.getNext();
        
        //先建立删除节点的前一个节点和删除节点的后一个节点的关系
        node.setNext(nNode);
        //清除dNode的下一个节点
        dNode.setNext(null);
        
        size--;//计数器减一
        
        return dNode.getE();//返回删除节点中的数据域
    }
    
    /**
     * 获取指定索引位置的元素
     * @param index索引位置
     * @return 返回节点中的数据域
     */
    public E get(int index){
        if(index <= 0 || index > size)
            return null;
        //查找指定索引位置的节点对象
        Node<E> node = select(index);
        //获取节点中的数据域元素并返回
        return node.getE();
    }
    
    /**
     * 获取单链表中存储的元素总数
     * @return 返回size属性
     */
    public int size(){
        return size;
    }
    
    /**
     * 获取指定索引位置的节点对象
     * @param index索引位置
     * @return 返回获取到的节点对象
     */
    private Node<E> select(int index){
        Node<E> node = root.getNext();//将头节点的下一个节点赋给node
        if(index==1)//如果index是1表示是头结点的下一个节点
            return node;//直接返回node
        for(int i=1;i<index;i++){
            node = node.getNext();//获取node的下一个节点
        }
        return node;
    }
 
}


java之HashMap


1 : HashMap概述
      HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
2 : HashMap的数据结构
      java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。
  
       从上图中可以看出,HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。
源码如下:
/** 
 * The table, resized as necessary. Length MUST Always be a power of two. 
 */  
transient Entry[] table;  
  
static class Entry<K,V> implements Map.Entry<K,V> {  
    final K key;  
    V value;  
    Entry<K,V> next;  
    final int hash;  
    ……  
}  

可以看出, Entry 就是数组中的元素,每个  Map.Entry  其实就是一个 key-value 对,它持有一个指向下一个元素的引用,这就构成了链表。
3.    HashMap的存取实现:
存储:
public V put(K key, V value) {  
    // HashMap允许存放null键和null值。  
    // 当key为null时,调用putForNullKey方法,将value放置在数组第一个位置。  
    if (key == null)  
        return putForNullKey(value);  
    // 根据key的keyCode重新计算hash值。  
    int hash = hash(key.hashCode());  
    // 搜索指定hash值在对应table中的索引。  
    int i = indexFor(hash, table.length);  
    // 如果 i 索引处的 Entry 不为 null,通过循环不断遍历 e 元素的下一个元素。  
    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;  
        }  
    }  
    // 如果i索引处的Entry为null,表明此处还没有Entry。  
    modCount++;  
    // 将key、value添加到i索引处。  
    addEntry(hash, key, value, i);  
    return null;  
}  
       从上面的源代码中可以看出:当我们往HashMapput元素的时候,先根据keyhashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。  
 2) 读取:
public V get(Object key) {  
    if (key == null)  
        return getForNullKey();  
    int hash = hash(key.hashCode());  
    for (Entry<K,V> e = table[indexFor(hash, table.length)];  
        e != null;  
        e = e.next) {  
        Object k;  
        if (e.hash == hash && ((k = e.key) == key || key.equals(k)))  
            return e.value;  
    }  
    return null;  
}  

       从上面的源代码中可以看出:从HashMapget元素时,首先计算keyhashCode,找到数组中对应位置的某一元素,然后通过keyequals方法在对应位置的链表中找到需要的元素。

归纳起来简单地说:HashMap 在底层将 key-value 当成一个整体进行处理,这个整体就是一个 Entry 对象。HashMap 底层采用一个 Entry[] 数组来保存所有的 key-value 对,当需要存储一个 Entry 对象时,会根据hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry时,也会根据hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该Entry







猜你喜欢

转载自blog.csdn.net/champion2009/article/details/71036568