当前位置: 首页 > news >正文

青岛企业如何建网站/seo搜索是什么

青岛企业如何建网站,seo搜索是什么,穆棱seo,仿牌做独立网站可靠吗文章目录1 LinkedHashMap(jdk1.7之前)1.1 定义1.2 底层存储1.3 构造方法1.4 增加1.5 删除1.6 查找1.7 是否包含1.8 cache功能1 LinkedHashMap(jdk1.7之前) 我们知道Map其底层数据存储是一个hash表(数组单向链表)。接下来我们看一下另一个LinkedHashMap&…

文章目录

  • 1 LinkedHashMap(jdk1.7之前)
    • 1.1 定义
    • 1.2 底层存储
    • 1.3 构造方法
    • 1.4 增加
    • 1.5 删除
    • 1.6 查找
    • 1.7 是否包含
    • 1.8 cache功能

1 LinkedHashMap(jdk1.7之前)

我们知道Map其底层数据存储是一个hash表(数组+单向链表)。接下来我们看一下另一个LinkedHashMap,它是HashMap的一个子类,他在HashMap的基础上维持了一个双向链表(hash表+双向链表),在遍历的时候可以使用插入顺序(先进先出,类似于FIFO),或者是最近最少使用(LRU)的顺序。
来具体看下LinkedHashMap的实现。

1.1 定义

 public class LinkedHashMap<K,V>extends HashMap<K,V>implements Map<K,V>

从定义可以看到LinkedHashMap继承于HashMap,且实现了Map接口。这也就意味着HashMap的一些优秀因素可以被继承下来,比如hash寻址,使用链表解决hash冲突等实现的快速查找,对于HashMap中一些效率较低的内容,比如容器扩容过程,遍历方式,LinkedHashMap是否做了一些优化呢。继续看代码吧。

1.2 底层存储

LinkedHashMap是基于HashMap,并在其基础上维持了一个双向链表,也就是说LinkedHashMap是一个hash表(数组+单向链表) +双向链表的实现,到底实现方式是怎么样的,来看一下:

      /**       * The head of the doubly linked list.*/ private transient Entry<K,V> header ;/**       * The iteration ordering method for this linked hash map: <tt>true</tt>* for access -order, <tt> false</tt> for insertion -order.** @serial11      */   private final boolean accessOrder;

看到了一个无比熟悉的属性header,它在LinkedList中出现过,英文注释很明确,是双向链表的头结点对不对。
再看accessOrder这个属性,true表示最近较少使用顺序,false表示插入顺序。当然你说怎么没看到数组呢,别忘了LinkedHashMap继承于HashMap

再来看下Entry这个节点类和HashMap中的有什么不同。

      /**       * LinkedHashMap entry.*/ private static class Entry<K,V> extends HashMap.Entry<K,V> {// These fields comprise the doubly linked list used for iteration.// 双向链表的上一个节点before和下一个节点after Entry<K,V> before, after ;// 构造方法直接调用父类HashMap的构造方法(super)Entry( int hash, K key, V value, HashMap.Entry<K,V> next) {super(hash, key, value, next);}/**          * 从链表中删除当前节点的方法*/private void remove() {// 改变当前节点前后两个节点的引用关系,当前节点没有被引用后,gc可以回收// 将上一个节点的after指向下一个节点before.after = after;// 将下一个节点的before指向前一个节点after.before = before;}/**          * 在指定的节点前加入一个节点到链表中(也就是加入到链表尾部)*/private void addBefore(Entry<K,V> existingEntry) {// 下面改变自己对前后的指向// 将当前节点的after指向给定的节点(加入到existingEntry前面嘛)after  = existingEntry;// 将当前节点的before指向给定节点的上一个节点33             before = existingEntry.before ;// 下面改变前后最自己的指向// 上一个节点的after指向自己before.after = this;// 下一个几点的before指向自己after.before = this;}// 当向Map中获取查询元素或修改元素(put相同key)的时候调用这个方法void recordAccess(HashMap<K,V> m) {LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;// 如果accessOrder为true,也就是使用最近较少使用顺序if (lm.accessOrder ) {lm. modCount++;// 先删除,再添加,也就相当于移动了// 删除当前元素remove();// 将当前元素加入到header前(也就是链表尾部)addBefore(lm. header);}}// 当从Map中删除元素的时候调动这个方法void recordRemoval(HashMap<K,V> m) {remove();}
}

可以看到Entry继承了HashMap中的Entry,但是LinkedHashMap中的Entry多了两个属性指向上一个节点的before和指向下一个节点的after,也正是这两个属性组成了一个双向链表,Entry还有一个继承下来的next属性,这个next是单向链表中用来指向下一个节点的,怎么回事嘛,怎么又是单向链表又是双向链表呢,其实想的没错,这里的节点即是Hash表中的单向链表中的一个节点,它又是LinkedHashMap维护的双向链表中的一个节点,是不是瞬间觉得高大上了
在这里插入图片描述

注:黑色箭头指向表示单向链表的next指向,红色箭头指向表示双向链表的before指向,蓝色箭头指向表示双向链表的after指向。另外LinkedHashMap种还有一个header节点是不保存数据的,这里没有画出来。

从上图可以看出LinkedHashMap仍然是一个Hash表,底层由一个数组组成,而数组的每一项都是个单向链表,由next指向下一个节点。但是LinkedHashMap所不同的是,在节点中多了两个属性beforeafter,由这两个属性组成了一个双向循环链表,而由这个双向链表维持着Map容器中元素的顺序。看下Entry中的recordRemoval方法,该方法将在节点被删除时候调用,Hash表中链表节点被正常删除后,调用该方法修正由于节点被删除后双向链表的前后指向关系,从这一点来看,LinkedHashMap比HashMap的add、remove、set等操作要慢一些(因为要维护双向链表 )。

1.3 构造方法

  /**       * 构造一个指定初始容量和加载因子的LinkedHashMap,默认accessOrder为false*/ public LinkedHashMap( int initialCapacity, float loadFactor) {super(initialCapacity, loadFactor);accessOrder = false;}/**      * 构造一个指定初始容量的LinkedHashMap,默认accessOrder为false*/public LinkedHashMap( int initialCapacity) {super(initialCapacity);accessOrder = false;}/**      * 构造一个使用默认初始容量(16)和默认加载因子(0.75)的LinkedHashMap,默认accessOrder为false*/public LinkedHashMap() {super();accessOrder = false;}/**      * 构造一个指定map的LinkedHashMap,所创建LinkedHashMap使用默认加载因子(0.75)和足以容纳指定map的初始容量,默认accessOrder为false 。*/public LinkedHashMap(Map<? extends K, ? extends V> m) {super(m);accessOrder = false;}/**      * 构造一个指定初始容量、加载因子和accessOrder的LinkedHashMap*/public LinkedHashMap( int initialCapacity,float loadFactor,boolean accessOrder) {super(initialCapacity, loadFactor);
40         this.accessOrder = accessOrder;
41 }

构造方法很简单基本都是调用父类HashMap的构造方法(super),只有一个区别就是对于accessOrder的设定,上面的构造参数中多数都是将accessOrder默认设置为false,只有一个构造方法留了一个出口可以设置accessOrder参数。看完了构造方法,发现一个问题,头部节点header的初始化跑哪里去了
回忆一下,看看HashMap的构造方法:

  /**       * Constructs an empty <tt>HashMap</tt> with the specified initial* capacity and load factor.** @param  initialCapacity the initial capacity* @param  loadFactor      the load factor* @throws IllegalArgumentException if the initial capacity is negative*         or the load factor is nonpositive*/public HashMap( int initialCapacity, float loadFactor) {if (initialCapacity < 0)throw new IllegalArgumentException( "Illegal initial capacity: " +initialCapacity);if (initialCapacity > MAXIMUM_CAPACITY)initialCapacity = MAXIMUM_CAPACITY;if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException( "Illegal load factor: " +loadFactor);// Find a power of 2 >= initialCapacityint capacity = 1;while (capacity < initialCapacity)capacity <<= 1;this.loadFactor = loadFactor;threshold = (int)(capacity * loadFactor);table = new Entry[capacity];init();}/**      * Initialization hook for subclasses. This method is called* in all constructors and pseudo -constructors (clone, readObject)* after HashMap has been initialized but before any entries have* been inserted.  (In the absence of this method, readObject would* require explicit knowledge of subclasses.)*/void init() {

init()HashMap中是一个空方法,也就是给子类留的一个回调函数,我们来看下LinkedHashMapinit()方法的实现

      /**       * Called by superclass constructors and pseudoconstructors (clone,* readObject) before any entries are inserted into the map.  Initializes* the chain.*/ void init() {// 初始化话header,将hash设置为-1,key、value、next设置为null header = new Entry<K,V>(-1, null, null, null);// header的before和after都指向header自身header.before = header. after = header ;

1.4 增加

LinkedHashMap没有重写put方法,只是重写了HashMap中被put方法调用的addEntry

      /**       * This override alters behavior of superclass put method. It causes newly* allocated entry to get inserted at the end of the linked list and* removes the eldest entry if appropriate.*/ void addEntry( int hash, K key, V value, int bucketIndex) {// 调用createEntry方法创建一个新的节点 createEntry(hash, key, value, bucketIndex);// Remove eldest entry if instructed, else grow capacity if appropriate// 取出header后的第一个节点(因为header不保存数据,所以取header后的第一个节点)Entry<K,V> eldest = header.after ;// 判断是容量不够了是要删除第一个节点还是需要扩容if (removeEldestEntry(eldest)) {// 删除第一个节点(可实现FIFO、LRU策略的Cache)removeEntryForKey(eldest. key);} else {// 和HashMap一样进行扩容if (size >= threshold)resize(2 * table.length );}}/**      * This override differs from addEntry in that it doesn't resize the* table or remove the eldest entry.*/void createEntry( int hash, K key, V value, int bucketIndex) {// 下面三行代码的逻辑是,创建一个新节点放到单向链表的头部// 取出数组bucketIndex位置的旧节点HashMap.Entry<K,V> old = table[bucketIndex];// 创建一个新的节点,并将next指向旧节点Entry<K,V> e = new Entry<K,V>(hash, key, value, old);// 将新创建的节点放到数组的bucketIndex位置table[bucketIndex] = e;// 维护双向链表,将新节点添加在双向链表header前面(链表尾部)e.addBefore( header);// 计数器size加1size++;}/**      * 默认返回false,也就是不会进行元素删除了。如果想实现cache功能,只需重写该方法*/protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {return false;
}

可以看到,在添加方法上,比HashMap中多了两个逻辑,一个是当Map容量不足后判断是删除第一个元素,还是进行扩容,另一个是维护双向链表。而在判断是否删除元素的时候,我们发现removeEldestEntry这个方法竟然是永远返回false,原来想要实现Cache功能,需要自己继承LinkedHashMap然后重写removeEldestEntry方法,这里默认提供的是容器的功能。

1.5 删除

LinkedHashMap没有重写remove方法,只是在实现了Entry类的recordRemoval方法,该方法是HashMap的提供的一个回调方法,在HashMapremove方法进行回调,而LinkedHashMaprecordRemoval的主要当然是要维护双向链表了

1.6 查找

LinkedHashMap重写了get方法,但是确复用了HashMap中的getEntry方法,LinkedHashMap是在get方法中指加入了调用recoreAccess方法的逻辑,recoreAccess方法的目的当然也是维护双向链表了,具体逻辑返回上面去看下Entry类的recoreAccess方法吧

 public V get(Object key) {Entry<K,V> e = (Entry<K,V>)getEntry(key);if (e == null)return null;e.recordAccess( this);return e.value ;}

1.7 是否包含

  /**       * Returns <tt>true</tt> if this map maps one or more keys to the* specified value.** @param value value whose presence in this map is to be tested* @return <tt> true</tt> if this map maps one or more keys to the*         specified value*/      public boolean containsValue(Object value) {// Overridden to take advantage of faster iterator// 遍历双向链表,查找指定的valueif (value==null) { for (Entry e = header .after; e != header; e = e.after )if (e.value ==null)return true;} else {for (Entry e = header .after; e != header; e = e.after )if (value.equals(e.value ))return true;}return false;}

LinkedHashMapcontainsValue进行了重写,HashMapcontainsValue需要遍历整个hash表,这样是十分低效的。而LinkedHashMap中重写后,不再遍历hash表,而是遍历其维护的双向链表,这样在效率上难道就有所改善吗?我们分析下:hash表是由数组+单向链表组成,而由于使用hash算法,可能会导致散列不均匀,甚至数组的有些项是没有元素的(没有hash出对应的散列值),而LinkedHashMap的双向链表呢,是不存在空项的,所以LinkedHashMapcontainsValueHashMapcontainsValue效率要好一些。

1.8 cache功能

在最后,让我们简单基于LInkedHashMap实现一个Cache功能

  import java.util.LinkedHashMap;import java.util.Map;public class MyLocalCache extends LinkedHashMap<String, Object> {private static final long serialVersionUID = 7182816356402068265L;private static final int DEFAULT_MAX_CAPACITY = 1024;private static final float DEFAULT_LOAD_FACTOR = 0.75f;private int maxCapacity;public enum Policy {FIFO, LRU}public MyLocalCache(Policy policy) {super(DEFAULT_MAX_CAPACITY, DEFAULT_LOAD_FACTOR, Policy.LRU .equals(policy));this.maxCapacity = DEFAULT_MAX_CAPACITY;}public MyLocalCache(int capacity, Policy policy) {super(capacity, DEFAULT_LOAD_FACTOR, Policy. LRU.equals(policy));this.maxCapacity = capacity;}@Overrideprotected boolean removeEldestEntry(Map.Entry<String, Object> eldest) {return this.size() > maxCapacity;}public static void main(String[] args) {MyLocalCache cache = new MyLocalCache(5, Policy.LRU);cache.put( "k1", "v1" );cache.put( "k2", "v2" );cache.put( "k3", "v3" );cache.put( "k4", "v4" );cache.put( "k5", "v5" );cache.put( "k6", "v6" );System. out.println("size=" + cache.size());System. out.println("----------------------" );for (Map.Entry<String, Object> entry : cache.entrySet()) {   System. out.println(entry.getValue());   }System. out.println("----------------------" );System. out.println("k3=" + cache.get("k3"));System. out.println("----------------------" );for (Map.Entry<String, Object> entry : cache.entrySet()) {   System. out.println(entry.getValue());   }}}
http://www.lbrq.cn/news/1116127.html

相关文章:

  • 专做户外装备测评视频网站/seo sem是什么意思
  • 网站建设技术经费预算/全网网络营销
  • 做自动发货网站/长沙网络推广只选智投未来
  • 集团公司网站建设策划/google下载安卓版
  • 河南建设银行处理违章网站/搜索引擎推广与优化
  • 成品网站w灬源码1688网页版/阿里云建站费用
  • 广东广州电脑个人建站/看网站搜什么关键词
  • 清远专业网站建设/怎么在百度做宣传广告
  • 国内最大网站制作公司/百度seo 站长工具
  • 空间注册网站/百度关键词排名查询接口
  • 北京 个人网站 备案/品牌运营管理公司
  • 留住用户网站/什么是软文营销?
  • 天涯武汉论坛/网站seo教材
  • 邯郸小学网站建设/抖音搜索优化
  • 重庆网站设计定制/国外域名注册
  • 自己的网站是什么样子的/营销网站的宣传、推广与运作
  • 电子商务网站建设阶段/十大骗子教育培训机构
  • 青岛家乐福网上商城/seo在线教学
  • 做网站需要多少/网站seo百度百科
  • html5做图书馆网站/软件开发外包公司
  • 免费推广seo策略方法/seo外包如何
  • 服装logo创意设计/网址seo关键词
  • 有哪些网站可以做海报/厦门百度推广怎么做
  • 专业房地产网站建设/免费海报模板网站
  • 建一个公司需要多少钱?/seo优化技术
  • 设计了网站/怎么做网站?
  • 新疆做网站的公司有哪些/好网站
  • 哪个网站做长图免费转高清图片/东莞seo托管
  • 网站怎么做筛选/高端网站建设制作
  • 关键词网站建设推广/seo排名软件有用吗
  • 【推荐100个unity插件】使用C#或者unity实现爬虫爬取静态网页数据——Html Agility Pack (HAP)库和XPath 语法的使用
  • RAG优化秘籍:基于Tablestore的知识库答疑系统架构设计
  • Dify的默认端口怎么修改
  • 3.2数据库-关系代数-函数依赖-范式
  • Vue.js 动画与过渡:让你的界面“活”起来,提升用户体验的视觉魔法!
  • excel 通过openpyxl表格下载和插入图片