java语言高级特性(二)数据结构相关类及实现原理

数据有逻辑结构和存储结构,逻辑结构又有四种:list,tree,set,graph。

存储结构有顺序印象和非顺序印象,因此有顺序存储结构和链式存储结构。

java JDK为常用的数据结构定义了一些Interface和Implementation。这些接口、实现类以及常用的排序、查找等算法成为Java Collection框架(Java Collection Framework)。

Collection接口定义了其上的3类操作:针对每个元素的基本操作、迭代器和Collection对象之间的批量操作。

接口Collection的子接口有Set和List。
集合(set)在Collection的基础之上增加了“不允许重复元素“的约束;
而List则在其上增加了”元素之间具有前弱、后继关系“的约束;除了第一个元素外,所有元素具有唯一的前驱;除了最有一个元素外,所有的元素具有唯一的后继。

java为集合接口提供了两个基本的实现:HashSet和Tree。

HashSet实现类按哈希算法来存储集合中的元素,查找性能较好但不能记忆元素之间的顺序,包括插入顺序。其子类LinkedHashSet也是根据元素hashCode(hashCode是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值)值来决定元素存储位置,但他同时使用链表维护元素的次序。

(set中不能存储相同的对象,这里相同的判断依据是equals方法,数据存入时,直接散列成哈希码,对象相同时,哈希码相同。)

TreeSet可以确保集合元素处于排序状态,根据实际值排序。TreeSet采用红黑树(较平衡)的数据结构排序,要求添加进TreeSet中的对象必须实现CompareTo接口。

List接口可以根据元素位置索引(index)来插入、替换、删除集合元素以及查找指定对象的位置。

***ArrayList实现类基于数组实现了 List接口,其内部封装了一个动态再分配的数组。每个数组对象都有capacity表示数组长度的自增长属性,默认为10。
LinkedList内部以链表来保存集合中的元素,因此随机访问容器性能较差,但在插入、删除元素时性能较好。*

Queue接口用于定义队列(先进后出)这种数据结构,直接继承了Collection接口。如果使用具有固定容量的队列,则应使用offer()和pool()存取元素,因为add()和remove()方法在因容器原因失败时抛出异常长。如果只是访问队首而不移出该元素,使用element()或者peek()方法。
LinkedList类实现了Queue接口,因此次我们可以把LinkedList当成了Queue来用。
PriorityQueue按大小记忆队列元素顺序,因此调用peek和pool方法直接取最小的元素。

Stack实现了List接口,提供了push和pop操作限制线性表中元素的插入和删除只能在线性表的同一端进行。
JDK 1.6 引入的 ArrayDequ 实现类被优先推荐作为栈使用。ArrayDeque 实现了 Deque 接 口。Deque 接口定义了双端队列,双端队列中的
元素可以从两端弹出,其限定定插入和删除操作在
表的两端进行。


知识点之间的衔接关系

  • 1 集合 接口 Set;实现类 HashSet、 LinkedHashSet 和 TreeSet
  • 2 线性表 接口
  • 3 队列 接口 Q ueue ;实现类 LinkedList
  • 4 优先队列 实现类 PriorityQueue .
  • 5 栈 实现类 Stack 和ArrayDequ .
  • 6 树 TreeMap < K,V> .
  • 7 查找与排序 java.util.Collections

Map 是一种典型名值对类型,它提供一种 Key-Value 对应保存的数据结构。客户程序通过 Key 值来访问对应的
Value,这个接口并没有继承 Collection 这接口;而其他的类或者接口,不管是 List、 Set、 Stack 等都继承或实现了
Collection。

TreeMap 和 HashMap算是 Java 集合类里面比较有难度的数据结构。

HashMap 元素存取的时间复杂度一般是 O(1).
TreeMap 内部对元素的操作复杂度为 O(logn)。

HashSet 的实现是基于 HashMap 的, 所 以 研 究HashSet 就要研究 HashMap。 TreeMap
记忆了顺序,TreeSet 内部的实现使用了 TreeMap。

HashMap可以看作三个视图:key的Set,value的Collection,Entry的Set。
(这里HashSet就是其实就是HashMap的一个视图。)

HashMap和HashSet研究

将对象放入到HashMap或HashSet中时,有两个方法需要特别关心:hashCode()和equals()。hashCode()方法决定了对象会被放到哪个bucket里,当多个对象的哈希值冲突时,equals()方法决定了这些对象是否是“同一个对象”。所以,如果要将自定义的对象放入到HashMap或HashSet中,需要@Override hashCode()和equals()方法。

//getEntry()方法
final Entry<K,V> getEntry(Object key) {
    ......
    int hash = (key == null) ? 0 : hash(key);
    for (Entry<K,V> e = table[hash&(table.length-1)];//得到冲突链表
         e != null; e = e.next) {//依次遍历冲突链表中的每个entry
        Object k;
        //依据equals()方法判断是否相等
        if (e.hash == hash &&
            ((k = e.key) == key || (key != null && key.equals(k))))
            return e;
    }
    return null;

前面已经说过HashSet是对HashMap的简单包装,对HashSet的函数调用都会转换成合适的HashMap方法,因此HashSet的实现非常简单,只有不到300行代码。这里不再赘述。

//HashSet是对HashMap的简单包装
public class HashSet<E>
{
    ......
    private transient HashMap<E,Object> map;//HashSet里面有一个HashMap
    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();
    public HashSet() {
        map = new HashMap<>();
    }
    ......
    public boolean add(E e) {//简单的方法转换
        return map.put(e, PRESENT)==null;
    }
    ......
}

treemap与tree实现类似,也使用了红黑树

static final class Entry<K,V> implements Map.Entry<K,V> {  
    K key;  
    V value;  
    Entry<K,V> left = null;  
    Entry<K,V> right = null;  
    Entry<K,V> parent;  
    boolean color = BLACK;  

    /** 
     * Make a new cell with given key, value, and parent, and with 
     * {@code null} child links, and BLACK color. 
     */  
    Entry(K key, V value, Entry<K,V> parent) {  
        this.key = key;  
        this.value = value;  
        this.parent = parent;  
    }  

    // ... Ignored  
}  

猜你喜欢

转载自blog.csdn.net/qq_37804737/article/details/78090350