精华内容
下载资源
问答
  • odomaince based Clonal Selection Clustering Algorithm,IDCSCA),该算法通过在经典的克隆选择算法框架中,引入基于免疫优势理论的免疫优势算子实现了在线自适应动态获得先验知识和个体间的信息共享.新算法首先通过对...
  • 同时,比较优势是一个动态演化的过程,文中正是基于以比较优势动态演化为核心内容的产品空间理论的视角,研究浙江省装备制造业7大产业的生产能力及各产业间的联系。通过产品空间相关模型识别其中优势产业,为产业升级...
  • 设计中,基于危险理论的应答模式,依据给定的危险半径,将进化种群划分为不同类型子群;各.子群中的个体动态获取样本大小,并依据不同的变异方式展开局部和全局搜索。比较性的数值.实验显示,此算法在寻优效率、搜索...
  • 这篇综述的主要目的是通过与其他流行的基于脑的亚稳态模型进行比较,从而展示默认空间理论中体现的认知,亚稳态意识模型在理论能力方面的优势,尽管该模型对人的认知本质提供了重要见解。意识,在他们的意识结构...
  • 本文将混沌理论和神经网络技术应用到农产品价格短期预测研究中, 充分利用相关技术优势, 设计了动态混沌神经网络时间序列预测模型. 在此基础上, 选取2008年1月21日-2012年7月1日的中国马铃薯日度价格为研究对象, 对...
  • 如何针对无标度网络的物理特性进行路由策略设计和优化是一个值得深入研究的问题。提出了一种参数可调的动态局部路由策略,该策略基于网络节点的转发...与经典的局部路由算法进行了仿真比较,结果显示该算法更有优势
  • 多角度动态光散射(MDLS)技术具有响应速度快、非接触式测量等优势,相较于单一散射角度测量技术,MDLS能获取更多的反映颗粒特征的散射光信息,可提供更准确的颗粒粒度分布(PSD),而合适的颗粒粒度反演算法能进一步...
  • 散列表整合了数组的快速索引还有链表的动态扩容的优势 4.什么是哈希? 核心理论:Hash也称散列、哈希,对应的英文都是Hash。基本原理就是把任意长度的输入,通过Hash算法变成==固定长度的输出。

    1.数组的优势/劣势

    特点:内存连续,并且空间都是一样的

    • 优势:索引速度比较快
    • 劣势:长度固定,不能扩容

    2.链表的优势/劣势

    特点:内存不连续,每一块内存都有一个引用去保存下一块内存的地址

    • 优势:扩容和删除方便
    • 劣势:查询比较慢,需要一个一个往下索引

    3.有没有一种方式整合两种数据结构的优势?散列表

    散列表整合了数组的快速索引还有链表的动态扩容的优势

    4.什么是哈希?

    核心理论:Hash也称散列、哈希,对应的英文都是Hash。基本原理就是把任意长度的输入,通过Hash算法变成==固定长度的输出。==

    这个映射的规则就是对应的Hash算法,而原始数据映射后的二进制串就是哈希值

    Hash的特点:

    1.从hash值不可以反向推导出原始的数据

    2.输入数据的微小变化会得到完全不同的hash值,相同的数据会得到相同的值

    3.哈希算法的执行效率要高效,长的文本也能快速地计算出哈希值

    4.hash算法的冲突概率要小

    由于hash的原理是将输入空间的值映射成hash空间内,而hash值的空间远小于输入的空间。

    根据抽屉原理,一定会存在不同的输入被映射成相同输出的情况。(哈希碰撞)

    抽屉原理:桌上有十个苹果,要把这十个苹果放到九个抽屉里,无论怎样放,我们会发现至少会有一个抽屉里面放不少于两个苹果。

    这一现象就是我们所说的“抽屉原理”。

    第二部分,HashMap原理讲解:

    1.HashMap的继承体系是什么样的?

    1589291281116

    HashMap虽然继承了AbstractMap但是基本上都已经重写了

    2.Node数据结构分析?

        //静态内部类
    	static class Node<K,V> implements Map.Entry<K,V> {
            //存放插入的key的hash的地方,这个hash与hashcode有点区别经过一次扰动存放到这里
            final int hash; 
            //put到map的数据的key
            final K key;
            //put到map的数据的value
            V value;
            //如果重复发生碰撞就形成链表,有next指向下一个
            Node<K,V> next;
            
            ...
    

    3.底层存储结构介绍?

    1589291620929
    • 默认初始化数组长度是16
    • 如果发生冲突了(有两种可能) 哈希碰撞 :两个路由地址相同
      • 可能会形成链表
      • 还有一种可能:当链表的长度大于等于8并且桶位的个数大于等于64个时,链表会变成红黑树,红黑树元素的个数小于6个的时候 会退化为链表
    • HashMap在JDK8中的底层结构式数组+链表+红黑树

    4.put数据原理分析?

    5.什么是Hash碰撞?

    如上图所示,如果加入的数的key的hash值 和以前加入的一个数的 hash值相等 那么就会形成链表,如果链表很长的话,查询效率会很低,极端条件可能为0(N);

    链化就是hash值多个相同,让链表变得很长

    6.jdk8为什么引入红黑树?

    红黑树是自平衡的二叉查找树,可以提高查询效率(红黑树具体性质:下一篇博客有)

    7.HashMap扩容原理?

    ​ 为了解决hash碰撞多了(链化严重)hash表退化为线性表,导致查询效率更低,扩容可以让查找变得很快。

    第三部分,手撕源码:

    1.HashMap核心属性分析(threshold, loadFactory, size, modCount)

    核心常量

    	//缺省table数组大小
    	static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
    	//table数组最大长度
        static final int MAXIMUM_CAPACITY = 1 << 30;
    	//缺省负载因子大小
        static final float DEFAULT_LOAD_FACTOR = 0.75f;
    	//树化阈值(链表长度达到8可能变为红黑树)
        static final int TREEIFY_THRESHOLD = 8;
    	//树降级称为链表的阈值
        static final int UNTREEIFY_THRESHOLD = 6;
    	//树化的另一个参数,当哈希表中的所有元素个数超过64时,才会允许树化
        static final int MIN_TREEIFY_CAPACITY = 64;
    

    属性

        //哈希表   当放元素的时候才进行初始化
    	transient Node<K,V>[] table;
        transient Set<Map.Entry<K,V>> entrySet;
    	//当前哈希表中元素个数
        transient int size
        //当前哈希表结构修改次数(插入或者删除算修改,  元素替换不算修改)
        transient int modCount;
    	//扩容阈值,当你的哈希表中的元素超过阈值时,触发扩容
        int threshold;
    	//负载因子 默认0.75   threshold = capacity(表长) * loadFactor(负载因子)
        final float loadFactor;
    

    2.构造方法分析

    hashmap的构造器基本上都是套娃

        public HashMap(int initialCapacity, float loadFactor) {
            //其实就是做了一些校验
            //capacity必须是大于0 ,最大值也就是 MAX_CAP
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal initial capacity: " +
                        initialCapacity);
            if (initialCapacity > MAXIMUM_CAPACITY)
                initialCapacity = MAXIMUM_CAPACITY;
    
            //loadFactor必须大于0
            if (loadFactor <= 0 || Float.isNaN(loadFactor))
                throw new IllegalArgumentException("Illegal load factor: " +
                        loadFactor);
            //将自定义的负载因子赋值
            this.loadFactor = loadFactor;
            //一开始将容量(这个容量一点是大于等于一个2的次方数(tableSizeFor(int)方法来实现))赋值给扩容阈值,后面初始会重新计算
            this.threshold = tableSizeFor(initialCapacity);
        }
    

    此为tableSizeFor(int)

        /**
         * Returns a power of two size for the given target capacity.
         * 作用:返回一个大于等于当前值cap的一个数字,并且这个数字一定是2的次方数
         *
         * cap = 10
         * n = 10 - 1 => 9
         * 0b1001 | 0b0100 => 0b1101
         * 0b1101 | 0b0011 => 0b1111
         * 0b1111 | 0b0000 => 0b1111
         *
         * 0b1111 => 15
         *
         * return 15 + 1;
         *
         * cap = 16
         * n = 16;
         * 0b10000 | 0b01000 =>0b11000
         * 0b11000 | 0b00110 =>0b11110
         * 0b11110 | 0b00001 =>0b11111
         * =>0b11111 => 31
         * return 31 + 1;
         *
         * 0001 1101 1100 => 0001 1111 1111 + 1 => 0010 0000 0000 一定是2的次方数
         *
         */
        static final int tableSizeFor(int cap) {
            int n = cap - 1 ;
            n |= n >>> 1;
            n |= n >>> 2;
            n |= n >>> 4;
            n |= n >>> 8;
            n |= n >>> 16;
            return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
        }
    

    下面为其他套娃的构造器

        public HashMap(int initialCapacity) {
            //自己传入初始化大小   然后使用默认的0.75负载因子
            this(initialCapacity, DEFAULT_LOAD_FACTOR);
        }
    	
        public HashMap() {
            //使用默认的0.75负载因子
            this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
        }
    
        public HashMap(Map<? extends K, ? extends V> m) {
            this.loadFactor = DEFAULT_LOAD_FACTOR;
            putMapEntries(m, false);
        }
    
    

    3.HashMap put 方法分析 => putVal方法分析

    1.要知道HashMap的put(K key, V value)方法之前 首先要先知道hash(Object)方法

    用来算出这个元素的hash值

         /**
         *作用:让key的hash值的高16位也参与路由运算(数组比较短的时候也可以参与到运算)
         * 异或:相同则返回0,不同返回1
         *
         * h = 0b 0010 0101 1010 1100 0011 1111 0010 1110(hashcode)
         * 0b 0010 0101 1010 1100 0011 1111 0010 1110
         * ^
         * 0b 0000 0000 0000 0000 0010 0101 1010 1100(上一组右移16位,让高16位也参与运算)
         * => 0010 0101 1010 1100 0001 1010 1000 0010(这个就是运算出来的Hash)
         * hashcode 经过扰动函数  然后 就是hash   然后与length
         * 如果key == null 就放到0下表的地方
         */
        static final int hash(Object key) {
            int h;
            return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
        }
    

    2.put(K key, V value)函数里面其实是调用了putVal(hash(key), key, value, false, true);

        public V put(K key, V value) {
            return putVal(hash(key), key, value, false, true);
        }
    

    3.purVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict)的真面目

     /**
         * Implements Map.put and related methods.
         *
         * @param hash(key)算出来的hash值
         * @param key the key
         * @param value the value to put
         * @param onlyIfAbsent  如果是true的话重复的值就不插入进来了,默认false
         * @param evict if false, the table is in creation mode.
         * @return previous value, or null if none
         */
        final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                       boolean evict) {
            //tab:引用当前hashMap的散列表
            //p:表示当前散列表的元素
            //n:表示散列表数组的长度
            //i:表示路由寻址 结果
            Node<K,V>[] tab; Node<K,V> p; int n, i;
    
            //延迟初始化逻辑,第一次调用putVal时会初始化hashMap对象中的最耗费内存的散列表
            if ((tab = table) == null || (n = tab.length) == 0)
                //把长度赋值给n
                n = (tab = resize()).length;
    
    //最简单的一种情况:寻址找到的桶位 刚好是 null,这个时候,直接将当前k-v=>node 扔进去就可以了
            if ((p = tab[i = (n - 1) & hash]) == null)
                tab[i] = newNode(hash, key, value, null);
    
            else /{
                //e:不为null的话,找到了一个与当前要插入的key-value一致的key的元素
                //k:表示临时的一个key
                Node<K,V> e; K k;
    
                //表示桶位中的该元素,与你当前插入的元素的key完全一致,表示后续需要进行替换操作
                if (p.hash == hash &&
                        ((k = p.key) == key || (key != null && key.equals(k))))
                    //key是要插入的数据  p是这个位置的元素
                    e = p;  //把原来位置的p 赋值给e
    
                else if (p instanceof TreeNode)//红黑树,下期讲。
                    e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
                else *{
                    //链表的情况,而且链表的头元素与我们要插入的key不一致。
                    for (int binCount = 0; ; ++binCount) {
                        //条件成立的话,说明迭代到最后一个元素了,也没找到一个与你要插入的key一致的node
                        //说明需要加入到当前链表的末尾(没有一个相同的元素)
                        if ((e = p.next) == null) {
                            p.next = newNode(hash, key, value, null);
                            //条件成立的话,说明当前链表的长度,达到树化标准了,需要进行树化
                            if (binCount >= TREEIFY_THRESHOLD - 1) // 链表的长度》=8(0-7)
                                //树化操作
                                treeifyBin(tab, hash);
                            break;
                        }
                        //条件成立的话,说明找到了相同key的node元素,需要进行替换操作
                        if (e.hash == hash &&
                                ((k = e.key) == key || (key != null && key.equals(k))))
                            break;
                        p = e;  //用于循环
                    }
                }*
    
                //e不等于null,条件成立说明,找到了一个与你插入元素key完全一致的数据,需要进行替换
                if (e != null) { // existing mapping for key
                    V oldValue = e.value;
                    if (!onlyIfAbsent || oldValue == null)
                        e.value = value;
                    afterNodeAccess(e);
                    return oldValue;
                }
            }/
    
            //modCount:表示散列表结构被修改的次数,替换Node元素的value不计数
            ++modCount;
            //插入新元素,size自增,如果自增后的值大于扩容阈值,则触发扩容。
            if (++size > threshold)
                resize();
            afterNodeInsertion(evict);
            return null;
        }
    

    4.HashMap resize 扩容方法分析(核心)

    0、旧的数组容量大于MAXIMUM_CAPACITY……2^31,不会再创建新的数组对象,否则resize方法一定会返回一个新的Node数组对象

    1、新创建的数组对象容量一定是旧数组的2倍

    2、新的数组对象,它的扩容阈值一定是旧的数组对象扩容阈值的2倍

    3、较长的单链表在扩容时是可能会被分割成两个单链表的

    /*
         * 为什么需要扩容?
         * 为了解决哈希冲突导致的链化影响查询效率的问题,扩容会缓解该问题。
    */    
    final Node<K,V>[] resize() {
            //oldTab:引用扩容前的哈希表
            Node<K,V>[] oldTab = table;
            //oldCap:表示扩容之前table数组的长度
            int oldCap = (oldTab == null) ? 0 : oldTab.length;
            //oldThr:表示扩容之前的扩容阈值,触发本次扩容的阈值
            int oldThr = threshold;
            //newCap:扩容之后table数组的大小
            //newThr:扩容之后,下次再次触发扩容的条件
            int newCap, newThr = 0;
    
            //条件如果成立说明 hashMap中的散列表已经初始化过了,这是一次正常扩容
            if (oldCap > 0) {
                //扩容之前的table数组大小已经达到 最大阈值后,则不扩容,且设置扩容条件为 int 最大值。
                if (oldCap >= MAXIMUM_CAPACITY) {
                    threshold = Integer.MAX_VALUE;
                    return oldTab;
                }
    
                //oldCap左移一位实现数值翻倍,并且赋值给newCap, newCap 小于数组最大值限制 且 扩容之前的阈值 >= 16
                //这种情况下,则 下一次扩容的阈值 等于当前阈值 X 2
                else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                        oldCap >= DEFAULT_INITIAL_CAPACITY)  //这个不满足时  newThr =0
                    newThr = oldThr << 1; // double threshold
            }
    
            //oldCap == 0,说明hashMap中的散列表是null
            //1.new HashMap(initCap, loadFactor);
            //2.new HashMap(initCap);
            //3.new HashMap(map); 并且这个map有数据
            else if (oldThr > 0) // initial capacity was placed in threshold
                //把一开始传入的自定义的数值通过》=2的次方数后的数 赋值给了  threshold   newThr =0
                newCap = oldThr;
    
            //oldCap == 0,oldThr == 0
            //new HashMap();
            else {               // zero initial threshold signifies using defaults
                newCap = DEFAULT_INITIAL_CAPACITY;//16
                newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//12
            }
    
            //newThr为零时,通过newCap*loadFactor计算出一个newThr
            if (newThr == 0) {
                float ft = (float)newCap * loadFactor;
                newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                        (int)ft : Integer.MAX_VALUE);
            }
    
            //把newThr赋值给扩容阈值 下一次扩容就按照这个来
            threshold = newThr;
    
            //创建出一个更长 更大的数组
            @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
            table = newTab;
    
            //说明,hashMap本次扩容之前,table不为null
            if (oldTab != null) {
    
                for (int j = 0; j < oldCap; ++j) {
                    //当前node节点
                    Node<K,V> e;
                    //说明当前桶位中有数据,但是数据具体是 单个数据,还是链表 还是 红黑树 并不知道
                    if ((e = oldTab[j]) != null) {
                        //方便JVM GC时回收内存
                        oldTab[j] = null;
    
                        //第一种情况:当前桶位只有一个元素,从未发生过碰撞,这情况 直接计算出当前元素应存放在 新数组中的位置,然后
                        //扔进去就可以了
                        if (e.next == null)
                            newTab[e.hash & (newCap - 1)] = e;
    
                        //第二种情况:当前节点已经树化,本期先不讲,下一期讲 ,
                        else if (e instanceof TreeNode)
                            ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                        else { // preserve order
                            //第三种情况:桶位已经形成链表
    
                            //低位链表:存放在扩容之后的数组的下标位置,与当前数组的下标位置一致。
                            Node<K,V> loHead = null, loTail = null;
                            //高位链表:存放在扩容之后的数组的下表位置为 当前数组下标位置 + 扩容之前数组的长度
                            Node<K,V> hiHead = null, hiTail = null;
    
                            Node<K,V> next;
                            do {
                                next = e.next;
                                //hash-> .... 1 1111  &oldCap = 1000
                                //hash-> .... 0 1111 &oldCap  = 0
                                // 0b 10000   old
                                //把高位链放到高位  低位放到低位
                                if ((e.hash & oldCap) == 0) {
                                    if (loTail == null)
                                        loHead = e;
                                    else
                                        loTail.next = e;
                                    loTail = e;
                                }
                                else {
                                    if (hiTail == null)
                                        hiHead = e;
                                    else
                                        hiTail.next = e;
                                    hiTail = e;
                                }
    
                            } while ((e = next) != null);
    
    
                            if (loTail != null) {
                                loTail.next = null;
                                newTab[j] = loHead;
                            }
    
                            if (hiTail != null) {
                                hiTail.next = null;
                                newTab[j + oldCap] = hiHead;
                            }
    
                        }
                    }
                }
    
            }
            return newTab;
        }
    

    首先定义5个局部变量:loHead、loTail、hiHead、hiTail、next,均是Node类型,lo开头的为一组、hi开头的为另外一组、next单独存在,作者为什么要定义这5个局部变量?

    do……while循环开始……局部变量next负责存储当前节点对象的下一个节点对象e.next,接着会将每个当前节点对象e持有的hash与旧数组容量oldCap作一个按位与计算,按位与计算的结果会出现两种情况:0与非0

    当按位与的结果为0时,由loHead、loTail负责持有单链表中的节点对象(loHead的作用是指向头结点、loTail的作用是指向尾节点)

    当按位与的结果为非0时,由hiHead、hiTail负责持有单链表的节点对象(hiHead指向头结点、hiTail指向尾节点)

    上面的行为,如果都有命中,那么旧数组桶中的一个单链表就会被分割成两个单链表

    在旧数组中的单链表循环结束后,loHead、hiHead分别持有的单链表会分别放到新数组中的桶中,loHead持有的单链表会放到新数组中与旧数组相同的桶下标j处,而hiHead持有的单链表则会放到新数组中的j+oldCap桶下标处.

    1589339239547

    5.HashMap get 方法分析

        public V get(Object key) {
            Node<K,V> e;
            return (e = getNode(hash(key), key)) == null ? null : e.value;
        }
    

    getNode(hash(key), key)是主要的代码,下面来解析这个方法

        final Node<K,V> getNode(int hash, Object key) {
            //tab:引用当前hashMap的散列表
            //first:桶位中的头元素
            //e:临时node元素
            //n:table数组长度
            Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
    
            if ((tab = table) != null && (n = tab.length) > 0 &&
                    (first = tab[(n - 1) & hash]) != null) {
                //第一种情况:定位出来的桶位元素 即为咱们要get的数据
                if (first.hash == hash && // always check first node
                        ((k = first.key) == key || (key != null && key.equals(k))))
                    return first;
    
                //说明当前桶位不止一个元素,可能 是链表 也可能是 红黑树
                if ((e = first.next) != null) {
                    //第二种情况:桶位升级成了 红黑树
                    if (first instanceof TreeNode)//下一期说
                        return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                    //第三种情况:桶位形成链表
                    do {
                        if (e.hash == hash &&
                                ((k = e.key) == key || (key != null && key.equals(k))))
                            return e;
    
                    } while ((e = e.next) != null);
                }
            }
            return null;
        }
    

    6.HashMap remove 方法分析

        public V remove(Object key) {
            Node<K,V> e;
            return (e = removeNode(hash(key), key, null, false, true)) == null ?
                    null : e.value;
        }
    

    调用了 removeNode(hash(key)这个方法

        /**
         * Implements Map.remove and related methods.
         *
         * @param hash hash for key
         * @param key the key
         * @param value the value to match if matchValue, else ignored
         * @param matchValue if true only remove if value is equal
         * @param movable  true:当key和value都相同的情况下才会删除
         * @return the node, or null if none
         */
    	final Node<K,V> removeNode(int hash, Object key, Object value,
                                   boolean matchValue, boolean movable) {
            //tab:引用当前hashMap中的散列表
            //p:当前node元素
            //n:表示散列表数组长度
            //index:表示寻址结果
            Node<K,V>[] tab; Node<K,V> p; int n, index;
    
            if ((tab = table) != null && (n = tab.length) > 0 &&
                    (p = tab[index = (n - 1) & hash]) != null) {
                //说明路由的桶位是有数据的,需要进行查找操作,并且删除
    
                //node:查找到的结果
                //e:当前Node的下一个元素
                Node<K,V> node = null, e; K k; V v;
    
                //第一种情况:当前桶位中的元素 即为 你要删除的元素
                if (p.hash == hash &&
                        ((k = p.key) == key || (key != null && key.equals(k))))
                    node = p;
    
    
                else if ((e = p.next) != null) {
                    //说明,当前桶位 要么是 链表 要么 是红黑树
    
                    if (p instanceof TreeNode)//判断当前桶位是否升级为 红黑树了
                        //第二种情况
                        //红黑树查找操作,下一期再说
                        node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
                    else {
                        //第三种情况
                        //链表的情况
                        do {
                            if (e.hash == hash &&
                                    ((k = e.key) == key ||
                                            (key != null && key.equals(k)))) {
                                node = e;
                                break;
                            }
                            p = e;   //这个node是p的下一个
                        } while ((e = e.next) != null);
                    }
                }
    
    
                //判断node不为空的话,说明按照key查找到需要删除的数据了
                if (node != null && (!matchValue || (v = node.value) == value ||
                        (value != null && value.equals(v)))) {
    
                    //第一种情况:node是树节点,说明需要进行树节点移除操作
                    if (node instanceof TreeNode)
                        ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
    
                    //第二种情况:桶位元素即为查找结果,则将该元素的下一个元素放至桶位中
                    else if (node == p)
                        tab[index] = node.next;
    
                    else
                        //node是p的下一个元素
                        //第三种情况:将当前元素p的下一个元素 设置成 要删除元素的 下一个元素。
                        p.next = node.next;
    
                    ++modCount;
                    --size;
                    afterNodeRemoval(node);
                    //将删除结果返回
                    return node;
                }
            }
            return null;
        }
    

    7.HashMap replace 方法分析

        @Override
        public boolean replace(K key, V oldValue, V newValue) {
            Node<K,V> e; V v;
            //调用了上面的getNode方法,然后判断了当前的结点是否存在,原来的value和现在的value是否相等,
            //如果相等就替换
            if ((e = getNode(hash(key), key)) != null &&
                    ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
                e.value = newValue;
                afterNodeAccess(e);
                return true;
            }
            return false;
        }
    
        @Override
        public V replace(K key, V value) {
            Node<K,V> e;
            if ((e = getNode(hash(key), key)) != null) {
                V oldValue = e.value;
                e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
            return null;
        }
    
    展开全文
  • 该方法和Daikon等现有的程序不变量检测工具检测方法比较具有明显的特色和优势:第一,基于关系数据库技术,具有良好的可扩展性;第二,使用SQL条件查询功能实现非函数依赖程序不变量检测,检测方法具有很好的灵活性...
  • 理论研究了直接调制与外调制模式下的微波光子链路的线性度,以直调激光器为核心器件的直接调制型短距离微波光子链路具有明显优势。着重开展实验研究,采集和分析两种调制模式下的信号,获得关键性能参数,包括链路...
  • apache比较稳定 成熟 少bug 对PHP支持 apache在处理动态请求有优势,一般动态请求要apache去做 一个连接对应一个进程 nginx nginx处理静态文件好 支持高并发连接,每秒最多的并发连接请求理论可以达到50000个 稳定性...

    一个会场一千人左右同时提交数据 如何优化
    不使用apache 换成使用nginx

    那apache与nginx的差别
    apache比较稳定 成熟 少bug 对PHP支持 apache在处理动态请求有优势,一般动态请求要apache去做 一个连接对应一个进程
    nginx nginx处理静态文件好 支持高并发连接,每秒最多的并发连接请求理论可以达到50000个 稳定性较差 一个连接对应多个进程 一个进程死掉时,会影响到多个用户的使用,稳定性差

    apache优化
    修改apache配置日志 减少访问压力
    这个问题可以参考此文章 https://blog.csdn.net/tai532439904/article/details/78484342?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

    你如何对整个网站优化
    数据库建表的时候 就应该考虑好多对应的键值 不应该都是255 需要考虑实际需求去定义键值
    数据库建立索引 对where 连表 唯一性比较强的字段建立索引
    sql语句优化 尽量少使用连表 分组那种全表搜索的方法
    多使用缓存 例如订单还未全付款的那种 或者是数据很少做修改的 存缓存 读取数据优先读取缓存
    前端将多个js请求合成一个请求
    控制大文件的下载
    前端商品使用懒加载 流加载 如果前端图片数量大 可以使用合成图片 服务器优先考虑阿里云服务器那种
    不适合自己的服务器

    session与cookie的区别是什么 请从协议,产生的原因与作用说明?
    session是存在于服务器 cookie是存在于本机 session是基于cookie运行的 http是无状态的 session想获取到自身ID还是得靠传输过来的cookie id来获取

    负载均衡
    就是用户连接服务器 中间多了个中间商赚差价 为了怕服务器宕机 多架设一台服务器 要是负载均衡器宕机后 也会使其失效 则就要多建设一个负载均衡器 负载均衡多个则形成了集群

    短信接口如何防止盗刷
    在短信接口验证方面在加上一个后端传输的图文接口 图文接口正确才可以进行短信验证
    对每个号码 每天验证次数进行限制

    有什么意见或者不同的看法可以下方评论一下 同时也欢迎补充或修改 1023720898@qq.com

    展开全文
  • 数学模型之灰色模型

    千次阅读 2016-07-13 15:24:50
    1 作为一个发展变化的系统,关联度分析实际上是动态过程发展态势的量化比较分析。 发展态势比较:系统各时期的有关统计数据的几何关系的比较。 关联度分析可以确定一个系统的目标层与因素层之间的关系大小。 2 优势...

    灰色系统理论

    1 作为一个发展变化的系统,关联度分析实际上是动态过程发展态势的量化比较分析。

    发展态势比较:系统各时期的有关统计数据的几何关系的比较。

    关联度分析可以确定一个系统的目标层与因素层之间的关系大小。

    2 优势分析

      多个参考数列和比较数列,得到关联矩阵,可以确定子因素对母因素(参考数列)的影响程度。

    3 生成数

      寻找随机变量数列之间的关系。

    4 灰色模型

    5 灰色预测

      通过原始数据的处理和灰色模型的建立,发现和掌握系统发展规律,对系统的未来状态做出科学的定量预测。GM(1,1)模型,灰色马尔科夫模型。道路交通事故的Verhulst模型。

    6 灰色决策模型


    7 模型检验

      残差合格模型

      关联度合格模型

      均方差合格模型

      小误差概率合格模型

    实例:

      道路交通安全系统是一个灰色系统。

    展开全文
  • 理论和实验上,通过依次比较表明新鉴频参量继承了相位调制拍频信号振幅多普勒频移测量方法的特点和优势,相比之下,测量动态范围约提高1倍,测量精度约提高6倍。为了有效利用闲置的回波信号光功率,提出了基于新...
  • 在面临行业环境发生重大变化时,如何准确把握未来的市场变化,提高自身战略的前瞻性和系统性,及早做出必要的创新和调整,成为煤炭领军企业适应形势变化,维持和创造比较优势的重要课题。本文提出需要基于商业生态系统...
  • java面试题之一

    2019-12-10 21:23:16
    1.ArrayList和LinkedList有什么区别: 不同点: 1)ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表...3)理论上来说在做新增和删除操作add和remove时,LinedList比较优势,因为ArrayList要移动数据...

    1.ArrayList和LinkedList有什么区别:
    不同点:
    1)ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
    2)对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
    3)理论上来说在做新增和删除操作add和remove时,LinedList比较占优势,因为ArrayList要移动数据
    相同点:ArrayList和LinkedList都是非线程安全的,允许重复的

    2.讲讲类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数,字段,当new的时候,他们的执行顺序。

    父类静态变量
    父类静态代码块
    子类静态代码块
    父类非静态变量(父类实例成员变量)
    父类构造函数
    子类非静态变量(子类实例成员变量)
    子类构造函数
    展开全文
  • Servlet是Java服务端的小程序,可以生成动态web界面 TomCat在Servlet和网络中的位置 Servlet生命周期 C/S结构和B/S结构 (1)B/S结构的优势和不足 ①开发成本低; ②维护简单 ③升级便利 ④用户使用方便,出现...
  • 人工智能技术以其强大的...金融领域数据复杂多变、易受外部环境影响,数据为各种政策的制定实施提供了强有力的理论支持,时时掌握最新的金融数据,把握经济动态走向显得尤为重要。人工智能在金融领域的重要特点,...
  • 它是一个动态脚本语言,理论上是可以作出更快速的开发,和Java类的互操作也有问题。 可惜,目前的IDE(NetBeans IDE)不能把它的优势完全发挥出来,连开发JavaFX的NetBeans插件都是Beta版本的- - 相比较,...
  • 数据在内存中存储方式

    千次阅读 2017-04-01 17:04:43
    嵌入式vxWorks系统属于静态加载方式,程序直接全部装载在内存去运行,对于这种处理方法,理论上运行速度优势明显,缺点是内存永远不够。 数据存储方式(如下来自网络,写的比较好): 代码段:代码段是用来...
  • 2.SQL作用流核心理论知识 2.1 流表对偶性 2.2 动态表与持续查询 2.3 Early Emit&Retraction 3.核心代码分析 1. 背景   Apache Flink是目前最火的大数据技术组件之一,不仅仅是因为Flink在流处理的优势,...
  • 一、理论知识 多线程是进程执行过程中产生的多条执行线索。线程是比进程执行更小的单位。线程不能独立存在,必须存在于进程中,同一进程的各线程间共享进程空间的...线程两种创建方法比较:实现Runnable接口的优势...
  • 目前视觉SLAM理论上虽已比较成熟,但在实际使用中仍会遇到诸多问题,如容易受到外界因素如光照/动态物体/稀疏纹理/室外大场景/快速运动等因素的影响,这些问题仅使用传统相机似乎无法有效解决。而光场相机相较于传统...
  • 20世纪70年代后,大规模集成电路和计算机控制技术的发展,以及现代控制理论的应用,使得交流电力拖动系统逐步具备了宽的调速范围、高的稳速范围、高的稳速精度、快的动态响应以及在四象限作可逆运行等良好的技术性能...
  • 为了说明改进型Buck变换器相比于传统型Buck变换器的优势,在相同的输出纹波电压指标要求下,对传统型及改进型Buck变换器的电感、电容取值进行了比较分析,得出所提出的改进型Buck变换器采用较小的电感、电容即可满足...
  • 本论文的主要工作是在基于云计算理论的基础上设计适合JEPAY 虚拟银行数据特点的云存储系统,系统的设计工作主要分为两个方面: 一个方面是设计能够为银行数据库提供文件支持的分布式文件系统。对于当前的云计算系统...
  • Taobao oceanbase代码

    2012-04-20 21:13:46
    虽然数据总量比较大,但跟许多行业一样,淘宝业务一段时间(例如小时或天)内数据的增删改是有限的(通常一天不超过几千万次到几亿次),根据这个特点,OceanBase把一段时间内的增删改等修改操作以增量形式记录下来(称之...
  • 法具有群体智能,全局寻优等优势比较适合于函数寻优问题,本案例研究了基于粒子群算法的函数寻优算法。 14 基于粒子群算法的PID控制优化算法(史峰) PID控制方法是工业领域中最常用的控制方法,然而在PID控制...
  • 这样把所学到的理论知识综合的运用到一些较复杂的数字逻辑电路系统中去,使我们在实践基本技能方面得到一次全面系统的锻炼;这样可以使我们了解和掌握现代复杂数字系统芯片的设计方法和所用到的EDA工具,为走上社会...

空空如也

空空如也

1 2 3 4
收藏数 78
精华内容 31
关键字:

动态比较优势理论