首页 > 编程知识 正文

java中排序的四种方式,四叉树编码的优缺点

时间:2023-05-06 00:36:42 阅读:162259 作者:777

前言如您所知,HashMap使用的数据结构在JDK8之前是数组单链表。 JDK8中使用的是数组单链表红黑树。

这里闲话不多说,为什么JDK8点引入了红黑树?

)1)因为数组的每个元素都是一个Entry,每个Entry都是一个单链接列表。

)2)链表长度过长时,需要花时间调查链表的要素,所以引入了红黑树。

)3)首先,红黑树是二叉树,而且是二叉树中比较特殊的二叉树搜索树。 红黑树具有从某个节点到该节点的子孙节点的所有路径中包含相同数量的黑节点的特性。 该特性确保没有路径比其他路径延伸两倍,因此红黑树是接近平衡的二叉树。 由此,红黑树的时间复杂度大幅降低为o(logn )。

)4)因此,用红黑树代替单链表会降低对集合中元素的访问速度。 (JDK8规定,如果链表的长度大于8,则从单链表转换为红色黑色树。 如果链表的长度小于6,则从红黑树转换为单链表。)。

回到正题,在HashMap中插入元素是什么流程呢? 是怎么把元素插入链表和红黑树的呢?

的插入查看HashMap的源代码,可以了解put操作。

静态散列(对象密钥)//自定义散列方法inth; //首先判断密钥是否为null,如果为null则为0,否则将key的hashCode和key的hashCode的值无符号地向右偏移16位的异或运算return(key==null )? 0:(h=key.hashcode () ) ) ) ) ) ) ) ) )。 }publicvput(kkey,V value ) (/向集合添加元素操作returnputval ) hash ) key )、key、value、false、true ); }finalvputval(inthash,K key,V value,boolean onlyIfAbsent,boolean evict ) { NodeK,V[] tab; NodeK,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 ) /当数组中对应索引处的元素为空时,重新设置链表k; if(p.hash==hash ) (k=p.key )==key||(key!=nullkey.equals(k ) ) ) /如果要插入的key与链表中第一个节点的key相同,则第一个节点被指定给e e=p。 elseif(pinstanceoftreenode ) /节点为红黑树e=((treenodek,v ) p ).puttreeval ) this,tab,hash,key,value ); else{for(intbincount=0; bincount(//链表中的节点if((e=p.next ) null ) ) /如果找不到与插入链表的节点相同的节点,则将链表的末尾p.next=newnode ) ) 如果插入后的链表的长度大于8,则返回红黑树if (bincount=tree ify _ threshold-1 )//-1for1STtreeifybin(tab,hash ); 黑; (if ) e.hash==hash((k=e.key )==key|)!=null ke

y.equals(k))))//如果找到了这个节点,那么跳出循环 break; p = e; } } //判断找到的相同key处的节点,并覆盖这个节点的value 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; }

首先,根据key,经过自定义的hash算法计算,得到一个hash值,再通过这个hash值&(table.length-1)得到在数组中的index。

然后通过索引index获取到数组中对应索引处的链表。拿到链表遍历里面的节点,如果没找到与要插入的节点具有相同key的节点,那么直接在链表中插入一个新的节点。如果找到了与要插入的节点具有相同key的节点,那么就把原有的value进行覆盖。

好了,了解了插入元素的流程,我们来说一下,插入元素的方式。

插入元素的方式

借用某位大神一张图哈:

在JDK8中:

p.next = newNode(hash, key, value, null);

可以看出新建的节点,放在了当前节点的next(即下一个位置),说明在当前节点的尾部。插入到当前节点的尾部,那当然是尾插法了。

再看JDK6中:

public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; //如果发现key已经在链表中存在,则修改并返回旧的值 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } //如果遍历链表没发现这个key,则会调用以下代码 modCount++; addEntry(hash, key, value, i); return null;}

重点关注插入的代码:addEntry(hash, key, value, i);

void addEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<K,V>(hash, key, value, e); if (size++ >= threshold) resize(2 * table.length);} Entry( int h, K k, V v, Entry<K,V> n) { value = v; next = n; key = k; hash = h;}

这里的next=n,说明了一切。意为: 新建节点的next,指向了n。n即为key对应索引处的链表。把之前的链表放到了新建节点的next的位置。说明是在以前链表的头部插入了新节点。故为头插法。

总结:

由上可以看出,

在JDK1.8中采用的是尾插法。

在JDK1.6中采用的是头插法。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。