精华内容
下载资源
问答
  • 技术面试中常见问题以及提升建议
    2021-12-30 11:26:11

    近期我和很多候选人进行了一些交流,在技术交流过程中发现了一些普遍的现象,很多情况候选人都了解存在的问题,但是又不知道如何去弥补这些问题。今天我来和大家一起探讨下,然后提供一些建议,希望能帮助大家。

    存在的普遍性问题

    在说问题之前,我们先抛出一个面试题,这个面试题设计是由浅入深,看你对知识点的掌握程度。

    1. 跨域你了解吗,什么是同源;
    2. 那么跨域有那几种解决方案呢?
    3. 既然你提到了 jsonp 那么 jsonp 的原理是什么呢?
    4. 在 iframe 中有 postmessage 来支持跨域,那么其中有什么安全问题呢?
    5. Access-Control-Allow-Origin 如果来支持多个域名呢?

    深度不足

    在聊的过程中,有这种场景回答
    举个例子

    “老师,这个知识点我看过,大概就是这样这样…如果说具体的概念,我用专业的术语说不上来”。
    “老师,这个我知道,后面如果需要,Google 下应该就没有问题了”。
    “老师,这个知识点,我没有用过,所以我不是很了解”。

    这里问题就在于大家平时没有注重积累,看知识点不能只是看了就结束了,应该要注重练习实践。正好最近上了一个陈振平老师的课,里面有一个叫做
    费曼学习法
    在这里插入图片描述

    • 被动学习,最多只能留存 30%,而主动学习最低 50%,最高 90%
    • 你看文章,其实只能留存 10% ,也就是对知识点有一个大概的认识,也就是我们第一个人回答的问题
    • 主动学习包括,讨论、实践、传授他人,也就是如果你能和别人探讨这个知识点,那么你可以留存 50%,如果你能亲自实践,比如跨域解决方案,你能自我实现那么你能留存 70%,而如果你可以把这个知识点写文章传授出来,那么你基本就掌握了,并且几乎不会忘记。

    只掌握自己工作层面的知识

    同样上面的问题,在遇到第五个问题时,有些同学会这样回答我

    “老师,那一般是后端处理的,所以这块我不是很了解”

    如果你这样回答,那么在面试过程中会大大的减分,先不论这个知识点前后端区分,即使是后端的知识点,你对跨域整个知识体系来说也是应该需要掌握的。
    同样的,我们会面临很多这类问题,又比如网络相关的,我们都听过一个面试题

    从 URL 输入到浏览器之后,浏览器发生了什么

    这个问题时非常常见的,那么把问题改进一下

    如果有用户反馈网页响应慢,那么从你角度思考下,这中间会有哪些原因呢,并且应该如何来定位

    很多同学这时候就会说包体积如何优化,图片资源怎么加载,页面结构应该如何布局等等。当然这也没有错,只是不够全面,应该先从 DNS 、再到 HTTP 、再到缓存等知识、然后再到页面性能等。
    这时候如果 DNS 存在问题,应该如何定位呢?很多东西就会反馈给我,不了解,又或者说三次握手如何定位快和慢呢?
    往往没有注重这些知识的积累,专注在前端知识点了,

    系统化实践不足

    这个就是我们上面说过的,这个知识点我没有用过,所以我不了解,当然这样诚实回答是最好的。
    这里的核心问题就是平时缺乏实践,特别对于一些常见的知识点,需要进行实践。针对比较常见的知识点一定要进行实践应用,也就是我们费曼学习法中,必须要通过实践来掌握 70% 的知识点。

    性能方面欠缺

    大部分同学都是说自己长期做业务,所以这块目前了解不多,或者实践经验较少。简历中常常也会说优化了什么,但是深入一问,优化的数据是从多少到多少,数据怎么来的,优化过程时怎么样的,能回答上来的也就寥寥无几了。
    而这个原因可能会自己的业务有关,其次也存在没有很好的引路人,导致的问题。

    架构方面思考不足

    平时我喜欢问一下开放性问题,比如

    如果让你来实现一个扫码登录,你觉得应该如何实现?
    这中间会存在什么安全性问题?
    这个实现过程中融合了哪些技术点?

    如果让你实现一个商品详情页面,你觉得应该如何来做?
    这中间要注重用户体验,毕竟是访问最多的核心页面,你觉得应该注重哪些细节设计?

    这就非常考验一个人的综合能力了,在短时间内看一个人是否能把多个知识点融合。不仅考验一个人的知识点的全面性也考验一个人的临场发挥能力。
    当然这属于拔高的面试题,但是大部分前端同学都只能体现前端的设计,对于后端的设计或者整体的方案了解都不会非常充分。

    欠缺流程规范

    在一个优秀的团队中,流程、制度和规范是非常标准的,大家都是按照流水线搬砖,由此来避免一些常见的低级问题。但是在很多团队里面都没有形成一定的规范,其次也不了解。
    比如:团队的协议规范、团队的代码规范、代码 review 规范、代码的 Git 管理规范等等。

    那么有这些问题,应该如何来解决呢?

    如何针对性解决

    1. 深度不足系统化实践不足,这点我上面已经提到了,大家要应用费曼学习法,多进行实践,也可以和同事多讨论,或者写文章来传授;
    2. 性能方面欠缺考虑,比如在做一个官网需求,大家可以自我思考,我们应该看哪些性能指标,如果性能指标产生问题时,我们应该针对性的进行优化,这样慢慢的你就积累了相关的经验了;
    3. 架构方面思考不足,平时要注重全面的技术点了解,并做好技术方案,技术方案要参考现有外部的设计方案,设计完成后要考量方案的可行性、安全、性能、资源损耗(人力、机器)等,通过全面的技术方案设计以后,你会对整个架构设计有一个比较全面的了解;
    4. 流程规范,这个大家就参考下大厂的一些规范和标准,有些可以参照执行一下,无论团队大小,叫做麻雀虽小,五脏俱全。

    如果还有其他方面的问题,大家也可以进行交流。

    更多相关内容
  • 看完还不懂HashMap算我输(附职场面试常见问题

    万次阅读 多人点赞 2020-04-06 17:37:17
    Hash冲突:得到下标值: 我们都知道在HashMap中 使用数组加链表,这样问题就来了,数组使用起来是有下标的,但是我们平时使用HashMap都是这样使用的: HashMap,String> hashMap=new HashMap(); hashMap.put(2,"dd")...

    HashMap的原理与实现

    版本之更迭:

    –》JDK 1.7 : Table数组+ Entry链表;
    –》JDK1.8 : Table数组+ Entry链表/红黑树;(为什么要使用红黑树?)

    一问HashMap的实现原理

    • 你看过HashMap源码吗,知道底层的原理吗
    • 为什么使用数组+链表
    • 用LinkedList代替数组可以吗
    • 既然是可以的,为什么不用反而用数组。

    重要变量介绍:

    ps:都是重要的变量记忆理解一下最好。

    • DEFAULT_INITIAL_CAPACITY Table数组的初始化长度: 1 << 4 2^4=16(为什么要是 2的n次方?)
    • MAXIMUM_CAPACITY Table数组的最大长度: 1<<30 2^30=1073741824
    • DEFAULT_LOAD_FACTOR 负载因子:默认值为0.75。 当元素的总个数>当前数组的长度 * 负载因子。数组会进行扩容,扩容为原来的两倍(todo:为什么是两倍?)
    • TREEIFY_THRESHOLD 链表树化阙值: 默认值为 8 。表示在一个node(Table)节点下的值的个数大于8时候,会将链表转换成为红黑树。
    • UNTREEIFY_THRESHOLD 红黑树链化阙值: 默认值为 6 。 表示在进行扩容期间,单个Node节点下的红黑树节点的个数小于6时候,会将红黑树转化成为链表。
    • MIN_TREEIFY_CAPACITY = 64 最小树化阈值,当Table所有元素超过改值,才会进行树化(为了防止前期阶段频繁扩容和树化过程冲突)。

    实现原理:

    实现原理图 我们都知道,在HashMap中,采用数组+链表的方式来实现对数据的储存。
    在这里插入图片描述
    HashMap采⽤Entry数组来存储key-value对,每⼀个键值对组成了⼀个Entry实体,Entry类实际上是⼀个单向的链表结 构,它具有Next指针,可以连接下⼀个Entry实体。 只是在JDK1.8中,链表⻓度⼤于8的时候,链表会转成红⿊树!

    第一问: 为什么使用链表+数组:要知道为什么使用链表首先需要知道Hash冲突是如何来的:

    答: 由于我们的数组的值是限制死的,我们在对key值进行散列取到下标以后,放入到数组中时,难免出现两个key值不同,但是却放入到下标相同的格子中,此时我们就可以使用链表来对其进行链式的存放。
    第二问 我⽤LinkedList代替数组结构可以吗?
    对于题目的意思是说,在源码中我们是这样的

    Entry[] table=new Entry[capacity];
    // entry就是一个链表的节点
    

    现在进行替换,进行如下的实现

    List<Entry> table=new LinkedList<Entry>();
    

    是否可以行得通? 答案当然是肯定的。
    第三问 那既然可以使用进行替换处理,为什么有偏偏使用到数组呢?
    因为⽤数组效率最⾼! 在HashMap中,定位节点的位置是利⽤元素的key的哈希值对数组⻓度取模得到。此时,我们已得到节点的位置。显然数组的查 找效率⽐LinkedList⼤(底层是链表结构)。
    ArrayList,底层也是数组,查找也快啊,为啥不⽤ArrayList? 因为采⽤基本数组结构,扩容机制可以⾃⼰定义,HashMap中数组扩容刚好是2的次幂,在做取模运算的效率⾼。 ⽽ArrayList的扩容机制是1.5倍扩容(这一点我相信学习过的都应该清楚),那ArrayList为什么是1.5倍扩容这就不在本⽂说明了。

    Hash冲突:得到下标值:

    我们都知道在HashMap中 使用数组加链表,这样问题就来了,数组使用起来是有下标的,但是我们平时使用HashMap都是这样使用的:

     HashMap<Integer,String> hashMap=new HashMap<>();
    hashMap.put(2,"dd");
    

    可以看到的是并没有特地为我们存放进来的值指定下标,那是因为我们的hashMap对存放进来的key值进行了hashcode(),生成了一个值,但是这个值很大,我们不可以直接作为下标,此时我们想到了可以使用取余的方法,例如这样:

    key.hashcode()%Table.length;
    

    即可以得到对于任意的一个key值,进行这样的操作以后,其值都落在0-Table.length-1 中,但是 HashMap的源码却不是这样做?
    对其进行了与操作,对Table的表长度减一再与生产的hash值进行相与:

      if ((p = tab[i = (n - 1) & hash]) == null)
                tab[i] = newNode(hash, key, value, null);
    

    我们来画张图进行进一步的了解;
    在这里插入图片描述
    这里我们也就得知为什么Table数组的长度要一直都为2的n次方,只有这样,减一进行相与时候,才能够达到最大的n-1值。
    举个栗子来反证一下:
    我们现在 数组的长度为 15 减一为 14 ,二进制表示 0000 1110 进行相与时候,最后一位永远是0,这样就可能导致,不能够完完全全的进行Table数组的使用。违背了我们最开始的想要对Table数组进行最大限度的无序使用的原则,因为HashMap为了能够存取高效,,要尽量较少碰撞,就是要尽量把数据分配均匀,每个链表⻓度⼤致相同。
    此时还有一点需要注意的是: 我们对key值进行hashcode以后,进行相与时候都是只用到了后四位,前面的很多位都没有能够得到使用,这样也可能会导致我们所生成的下标值不能够完全散列。
    解决方案:将生成的hashcode值的高16位于低16位进行异或运算,这样得到的值再进行相与,一得到最散列的下标值。

    static final int hash(Object key) {
            int h;
            return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
        }
    

    二问讲一讲HashMap的get/put过程

    • 知道HashMap的put元素的过程是什么样吗?
    • 知道get过程是是什么样吗?
    • 你还知道哪些的hash算法?
    • 说一说String的hashcode的实现

    Put方法

    1.对key的hashCode()做hash运算,计算index;
    2.如果没碰撞直接放到bucket⾥;
    3.如果碰撞了,以链表的形式存在buckets后;
    4.如果碰撞导致链表过⻓(⼤于等于TREEIFY_THRESHOLD),就把链表转换成红⿊树(JDK1.8中的改动);
    5.如果节点已经存在就替换old value(保证key的唯⼀性)
    6.如果bucket满了(超过load factor*current capacity),就要resize

    在得到下标值以后,可以开始put值进入到数组+链表中,会有三种情况:

    1. 数组的位置为空。
    2. 数组的位置不为空,且面是链表的格式。
    3. 数组的位置不为空,且下面是红黑树的格式。

    同时 对于KeyValue 也要经历一下步骤

    • 通过 Key 散列获取到对于的Table;’
    • 遍历Table 下的Node节点,做更新/添加操作;
    • 扩容检测;
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                      boolean evict) {
           Node<K,V>[] tab; Node<K,V> p; int n, i;
           if ((tab = table) == null || (n = tab.length) == 0)
    // HashMap的懒加载策略,当执行put操作时检测Table数组初始化。
               n = (tab = resize()).length;
           if ((p = tab[i = (n - 1) & hash]) == null)
    //通过``Hash``函数获取到对应的Table,如果当前Table为空,则直接初始化一个新的Node并放入该Table中。       
               tab[i] = newNode(hash, key, value, null);
           else {
               Node<K,V> e; K k;
               //进行值的判断: 判断对于是不是对于相同的key值传进来不同的value,若是如此,将原来的value进行返回
               if (p.hash == hash &&
                   ((k = p.key) == key || (key != null && key.equals(k))))
                   e = p;
               else if (p instanceof TreeNode)
              // 如果当前Node类型为TreeNode,调用 PutTreeVal 方法。
                   e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
               else {
    //如果不是TreeNode,则就是链表,遍历并与输入key做命中碰撞。 
                   for (int binCount = 0; ; ++binCount) {
                       if ((e = p.next) == null) {
       
    //如果当前Table中不存在当前key,则添加。
                           p.next = newNode(hash, key, value, null);
                           if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                          
    //超过了``TREEIFY_THRESHOLD``则转化为红黑树。
                               treeifyBin(tab, hash);
                           break;
                       }
                       if (e.hash == hash &&
                           ((k = e.key) == key || (key != null && key.equals(k))))            
    //做命中碰撞,使用hash、内存和equals同时判断(不同的元素hash可能会一致)。
                           break;
                       p = e;
                   }
               }
               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;
       }
    

    以上就是HashMap的Put操作,若是对其中的红黑树的添加,以及Node链表和红黑树的转换过程我们暂时不进行深入的讨论,这个流程大概还是可以进行理解,下面来深入讨论扩容问题。

    resise方法

    HashMap 的扩容实现机制是将老table数组中所有的Entry取出来,重新对其Hashcode做Hash散列到新的Table中,可以看到注解Initializes or doubles table size. resize表示的是对数组进行初始化或
    进行Double处理。现在我们来一步一步进行分析。

     /**
         * Initializes or doubles table size.  If null, allocates in
         * accord with initial capacity target held in field threshold.
         * Otherwise, because we are using power-of-two expansion, the
         * elements from each bin must either stay at same index, or move
         * with a power of two offset in the new table.
         *
         * @return the table
         */
        final Node<K,V>[] resize() {
        //先将老的Table取别名,这样利于后面的操作。
            Node<K,V>[] oldTab = table;
            int oldCap = (oldTab == null) ? 0 : oldTab.length;
            int oldThr = threshold;
            int newCap, newThr = 0;
            //表示之前的数组容量不为空。
            if (oldCap > 0) {
            // 如果 此时的数组容量大于最大值
                if (oldCap >= MAXIMUM_CAPACITY) {
                // 扩容 阙值为 Int类型的最大值,这种情况很少出现
                    threshold = Integer.MAX_VALUE;
                    return oldTab;
                }
                
                
                //表示 old数组的长度没有那么大,进行扩容,两倍(这里也是有讲究的)对阙值也进行扩容
                else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                         oldCap >= DEFAULT_INITIAL_CAPACITY)
                    newThr = oldThr << 1; // double threshold
            }
            //表示之前的容量是0 但是之前的阙值却大于零, 此时新的hash表长度等于此时的阙值
            else if (oldThr > 0) // initial capacity was placed in threshold
                newCap = oldThr;
            else {               // zero initial threshold signifies using defaults
            //表示是初始化时候,采用默认的 数组长度* 负载因子
                newCap = DEFAULT_INITIAL_CAPACITY;
                newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
            }
            //此时表示若新的阙值为0 就得用 新容量* 加载因子重新进行计算。
            if (newThr == 0) {
                float ft = (float)newCap * loadFactor;
                newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                          (int)ft : Integer.MAX_VALUE);
            }
            // 开始对新的hash表进行相对应的操作。
            threshold = newThr;
            @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
            table = newTab;
            if (oldTab != null) {
            //遍历旧的hash表,将之内的元素移到新的hash表中。
                for (int j = 0; j < oldCap/***此时旧的hash表的阙值*/; ++j) {
                    Node<K,V> e;
                    if ((e = oldTab[j]) != null) {
                    //表示这个格子不为空
                        oldTab[j] = null;
                        if (e.next == null)
                        // 表示当前只有一个元素,重新做hash散列并赋值计算。
                            newTab[e.hash & (newCap - 1)] = e;
                        else if (e instanceof TreeNode)
                        // 如果在旧哈希表中,这个位置是树形的结果,就要把新hash表中也变成树形结构,
                            ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                        else { // preserve order
                        //保留 旧hash表中是链表的顺序
                            Node<K,V> loHead = null, loTail = null;
                            Node<K,V> hiHead = null, hiTail = null;
                            Node<K,V> next;
                            do {// 遍历当前Table内的Node 赋值给新的Table。
                                next = e.next;
                                // 原索引
                                if ((e.hash & oldCap) == 0) {
                                    if (loTail == null)
                                        loHead = e;
                                    else
                                        loTail.next = e;
                                    loTail = e;
                                }
                                // 原索引+oldCap
                                else {
                                    if (hiTail == null)
                                        hiHead = e;
                                    else
                                        hiTail.next = e;
                                    hiTail = e;
                                }
                            } while ((e = next) != null);
                            // 原索引放到bucket里面
                            if (loTail != null) {
                                loTail.next = null;
                                newTab[j] = loHead;
                            }
                            // 原索引+oldCap 放到bucket里面
                            if (hiTail != null) {
                                hiTail.next = null;
                                newTab[j + oldCap] = hiHead;
                            }
                        }
                    }
                }
            }
            return newTab;
        }
    

    get方法

    1.对key的hashCode()做hash运算,计算index;
    2.如果在bucket⾥的第⼀个节点⾥直接命中,则直接返回;
    3.如果有冲突,则通过key.equals(k)去查找对应的Entry;
    4. 若为树,则在树中通过key.equals(k)查找,O(logn);
    5. 若为链表,则在链表中通过key.equals(k)查找,O(n)。

    在进行取值时候,因为对于我们传进来的key值进行了一系列的hash操作,首先,在传进来 key值时候,先进性hash操作,

      final Node<K,V> getNode(int hash, Object key) {
            Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
            // 判断 表是否为空,表重读是否大于零,并且根据此 key 对应的表内是否存在 Node节点。    
            if ((tab = table) != null && (n = tab.length) > 0 &&
                (first = tab[(n - 1) & hash]) != null) {
                if (first.hash == hash && // always check first node
                    ((k = first.key) == key || (key != null && key.equals(k))))
                    // 检查第一个Node 节点,若是命中则不需要进行do... whirle 循环。
                    return first;
                if ((e = first.next) != null) {
                    if (first instanceof TreeNode)
                    //树形结构,采用 对应的检索方法,进行检索。
                        return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                    do {
                    //链表方法 做while循环,直到命中结束或者遍历结束。
                        if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                            return e;
                    } while ((e = e.next) != null);
                }
            }
            return null;
        }
    

    containsKey 方法

    根据get方法的结果,判断是否为空,判断是否包含该key

     public boolean containsKey(Object key) {
            return getNode(hash(key), key) != null;
        }
    

    还知道哪些hash算法

    先说⼀下hash算法⼲嘛的,Hash函数是指把⼀个⼤范围映射到⼀个⼩范围。把⼤范围映射到⼀个⼩范围的⽬的往往是为了 节省空间,使得数据容易保存。

    ⽐较出名的有MurmurHashMD4MD5等等

    String中hashcode的实现

    public int hashCode() {
            int h = hash;
            if (h == 0 && value.length > 0) {
                char val[] = value;
    
                for (int i = 0; i < value.length; i++) {
                    h = 31 * h + val[i];
                }
                hash = h;
            }
            return h;
        }
    

    String类中的hashCode计算⽅法还是⽐较简单的,就是以31为权,每⼀位为字符的ASCII值进⾏运算,⽤⾃然溢出来等效 取模。
    哈希计算公式可以计为ss[[00]]3311^^((nn–11)) ++ ss[[11]]3311^^((nn–22)) ++ …… ++ ss[[nn–11]]
    那为什么以31为质数呢? 主要是因为31是⼀个奇质数,所以31i=32i-i=(i<<5)-i,这种位移与减法结合的计算相⽐⼀般的运算快很多

    三问 为什么hashmap的在链表元素数量超过8时候改为红黑树

    • 知道jdk1.8中hashmap改了什么吗。
    • 说一下为什么会出现线程的不安全性
    • 为什么在解决hash冲突时候,不直接用红黑树,而是先用链表,再用红黑树
    • 当链表转为红黑树,什么时候退化为链表

    第一问改动了什么
    1.由数组+链表的结构改为数组+链表+红⿊树。
    2. 优化了⾼位运算的hash算法:h^(h>>>16)
    3. 扩容后,元素要么是在原位置,要么是在原位置再移动2次幂的位置,且链表顺序不变。
    注意: 最后⼀条是重点,因为最后⼀条的变动,hashmap在1.8中,不会在出现死循环问题。

    HashMap的线程不安全性

    HashMap 在jdk1.7中 使用 数组加链表的方式,并且在进行链表插入时候使用的是头结点插入的方法。
    :这里为什么使用 头插法的原因是我们若是在散列以后,判断得到值是一样的,使用头插法,不用每次进行遍历链表的长度。但是这样会有一个缺点,在进行扩容时候,会导致进入新数组时候出现倒序的情况,也会在多线程时候出现线程的不安全性。
    但是对与 jdk1.8 而言,还是要进行阙值的判断,判断在什么时候进行红黑树和链表的转换。所以无论什么时候都要进行遍历,于是插入到尾部,防止出现扩容时候还会出现倒序情况。

    所以当在多线程的使用场景中,尽量使用线程安全的ConcurrentHashMap。至于Hashtable而言,使用效率太低。

    线程安全

    // 扩容 
    void transfer(Entry[] newTable, boolean rehash) {
                        int newCapacity = newTable.length;
                        for (Entry<K,V> e : table) { // A
                             while(null != e) { 
                                   Entry<K,V> next = e.next; 
                                   if (rehash) {
                                           e.hash = null == e.key ? 0 : hash(e.key); 
                                   }
                                   int i = indexFor(e.hash, newCapacity); 
                                   e.next = newTable[i];
                                   newTable[i] = e;
                                   e = next; } 
                              }
     }
    

    jdk1.7若是产生了多线程,例如 thread1,和thread2,同时想要进入到 transfer中,此时会出现如下图所示的情况:

    在这里插入图片描述

    此时对于我们的1会拥有两个临时变量,我们称为e1与e2。这个时候,线程一会先执行上述的函数,进行数组的翻倍,并且,会进入逆序的状态, 此时的 临时变量e1和next1都已经消失,但是对于每个节点上面所拥有的连接不会更改,这个时候,1上还有一个e2临时变量,2上有一个next2临时变量。如下图所示:
    在这里插入图片描述
    完成了线程一的扩容以后,线程二也会创建一个属于自己的数组,长度也是6。这个时候开始又执行一遍以上的程序。

    // 第一遍过来
    e.next = newTable[i]; 
    newTable[i] = e;
    e = next; 
    
    

    在这里插入图片描述

    此时完成了第一次的循环以后,进入到以上的情况,这个时候 执行e.next = newTable[i]; 寓意为: 2所表示的下一个指向 newTable[i],此时我们就发现了问题的所在,在执行完第一遍循环以后,2所表示的下一下就已经指向了 newTable[i],就是我们的1 ,当然这样我们就不用动,那我们就不动就好了,然后完成以后就如下图所示。

    // 第二遍来
    e.next = newTable[i]; 
    newTable[i] = e;
    e = next; 
    

    在这里插入图片描述
    这个时候开始第三次的循环,首先执行 Entry<K,V> next = e.next; ,这个时候我们就发现了问题,e2和e2的next2都执行了1,这个时候我们再度,执行以上的语句就会指向一个空的节点,当然空就空了,暂时也还不会出现差错,但是执行到 e.next = newTable[i];时候,会发现,执行到如下图所示的情况。这个时候出现了循环链表,若是不加以控制,就会耗尽我们的cpu。

    在这里插入图片描述

    第三问为什么不一开始就使用红黑树,不是效率很高吗?
    因为红⿊树需要进⾏左旋,右旋,变⾊这些操作来保持平衡,⽽单链表不需要。
    当元素⼩于8个当时候,此时做查询操作,链表结构已经能保证查询性能。
    当元素⼤于8个的时候,此时需要红⿊树来加快查 询速度,但是新增节点的效率变慢了。
    因此,如果⼀开始就⽤红⿊树结构,元素太少,新增效率⼜⽐较慢,⽆疑这是浪费性能的。
    第四问什么时候退化为链表
    为6的时候退转为链表。中间有个差值7可以防⽌链表和树之间频繁的转换。
    假设⼀下,如果设计成链表个数超过8则链表转 换成树结构,链表个数⼩于8则树结构转换成链表,
    如果⼀个HashMap不停的插⼊、删除元素,链表个数在8左右徘徊,就会 频繁的发⽣树转链表、链表转树,效率会很低。

    四问HashMap的并发问题

    • HashMap在并发环境下会有什么问题
    • 一般是如何解决的

    问题的出现

    (1)多线程扩容,引起的死循环问题
    (2)多线程put的时候可能导致元素丢失
    (3)put⾮null元素后get出来的却是null

    不安全性的解决方案

    1. 在之前使用hashtable。 在每一个函数前面都加上了synchronized 但是 效率太低 我们现在不常用了。
    2. 使用 ConcurrentHashmap函数,对于这个函数而言 我们可以每几个元素共用一把锁。用于提高效率。

    五问你一般用什么作为HashMap的key值

    • key可以是null吗,value可以是null吗
    • 一般用什么作为key值
    • 用可变类当Hashmap1的Key会有什么问题
    • 让你实现一个自定义的class作为HashMap的Key该如何实现

    key可以是null吗,value可以是null吗

    当然都是可以的,但是对于 key来说只能运行出现一个key值为null,但是可以出现多个value值为null

    一般用什么作为key值

    ⼀般⽤Integer、String这种不可变类当HashMap当key,⽽且String最为常⽤。
    (1)因为字符串是不可变的,所以在它创建的时候hashcode就被缓存了,不需要重新计算。 这就使得字符串很适合作为Map中的键,字符串的处理速度要快过其它的键对象。 这就是HashMap中的键往往都使⽤字符串。
    (2)因为获取对象的时候要⽤到equals()和hashCode()⽅法,那么键对象正确的重写这两个⽅法是⾮常重要的,这些类已 经很规范的覆写了hashCode()以及equals()⽅法。

    用可变类当Hashmap1的Key会有什么问题

    hashcode可能会发生变化,导致put进行的值,无法get出来,如下代码所示:

     HashMap<List<String>,Object> map=new HashMap<>();
            List<String> list=new ArrayList<>();
            list.add("hello");
            Object object=new Object();
            map.put(list,object);
            System.out.println(map.get(list));
            list.add("hello world");
            System.out.println(map.get(list));
    

    输出值如下:

    java.lang.Object@1b6d3586
    null
    

    实现一个自定义的class作为Hashmap的key该如何实现

    对于这个问题考查到了下面的两个知识点

    • 重写hashcode和equals方法需要注意什么?
    • 如何设计一个不变的类。
      针对问题⼀,记住下⾯四个原则即可
      (1)两个对象相等,hashcode⼀定相等
      (2)两个对象不等,hashcode不⼀定不等
      (3)hashcode相等,两个对象不⼀定相等
      (4)hashcode不等,两个对象⼀定不等
      针对问题⼆,记住如何写⼀个不可变类
      (1)类添加final修饰符,保证类不被继承。 如果类可以被继承会破坏类的不可变性机制,只要继承类覆盖⽗类的⽅法并且继承类可以改变成员变量值,那么⼀旦⼦类 以⽗类的形式出现时,不能保证当前类是否可变。
      (2)保证所有成员变量必须私有,并且加上final修饰 通过这种⽅式保证成员变量不可改变。但只做到这⼀步还不够,因为如果是对象成员变量有可能再外部改变其值。所以第4 点弥补这个不⾜。
      (3)不提供改变成员变量的⽅法,包括setter 避免通过其他接⼝改变成员变量的值,破坏不可变特性。
      (4)通过构造器初始化所有成员,进⾏深拷⻉(deep copy)
      (5) 在getter⽅法中,不要直接返回对象本⾝,⽽是克隆对象,并返回对象的拷⻉ 这种做法也是防⽌对象外泄,防⽌通过getter获得内部可变成员对象后对成员变量直接操作,导致成员变量发⽣改变

    后记

    1. 对于HashMap而言,扩容是一个特别消耗内存的操作。所以当程序员在使用HashMap的时候,估算map的大小,初始化的时候给一个大致的数值,避免map进行频繁的扩容。
    2. 负载因子是可以修改的,也可以大于1,但是建议不要轻易修改,除非情况非常特殊。
    3. HashMap是线程不安全的,不要在并发的环境中同时操作HashMap,建议使用ConcurrentHashMap。
    展开全文
  • 实战应用篇为职场Excel技能提升,系统讲解了实际工作中常见的7大类问题,内容包括数据整理、统计计算、查找筛选、数据展示、多表操作、打印输出和数据安全等。  本书内容丰富,深度和广度兼顾,可以帮助职场中经常...
  • 自己工作 10+ 年了,大概 5 年前从技术转管理后,多少还是发现一些职场新人职业发展的问题。 表现惊艳的新人肯定有,这种人往往在学校里面或者进入职场后就养成了一些非常良好的做事方式和工作习惯,工作效率高,...

    作者:沐榕休

    www.cnblogs.com/sankt/p/8658028.html

     

    自己工作 10+ 年了,大概 5 年前从技术转管理后,多少还是发现一些职场新人职业发展的问题。

    表现惊艳的新人肯定有,这种人往往在学校里面或者进入职场后就养成了一些非常良好的做事方式和工作习惯,工作效率高,产出多,甚至很短时间内就可以带新人,年度最佳新人当之无愧。

    然而表现不到位的似乎更多一些,这些员工工作时间短(比如说应届毕业生或者不到两年)尤其性格内向的往往容易掉入错误泥潭,无法自拔,而这些错误的行为会对未来的发展造成非常负面的影响,从而导致职场发展往失败的道路上越走越远。

     

    我分别从以下角度来阐述一下。

     

    1. 不愿意和同事沟通,不愿意向同事多学习

    这种员工大多比较内向或者性情有点高冷,须不知三人行,必有我师。多向同事学习,互通有无,对自己以后的发展有百利而无一害。有导师制或者老员工带,情况或许会有所改善,但如果内因没有改变,最终效果依旧不容乐观。

    技术开发工作中遇到一些技术难题非常正常,当然独立思考固然可贵,但是公司项目往往有一定的时间限制,优先解决问题永远放在第一位,而不是一个人在那里苦苦挣扎和搜索解决方案。如果时间压力不大,多思考一下也未尝不可。考虑到交付压力,这个时候就需要积极和同事,技术经理沟通,寻找解决思路,通常情况下,积极的沟通好过自己的单打独斗。也许同事或者老板的一句话,就应了那据古诗,山重水复疑无路,柳暗花明又一村。与此同时也和同事建立了更好的友谊,在老板心里也留下了做事有方法的好印象。

     

    2. 视野狭窄,只关注自己的一亩三分地,囿于角色

    这种情况其实在职场中多见不仅是初级程序员,甚至工作五年以上的程序员也有类似的问题,不是自己的事情不闻不问,而且危害更大。

    公司项目往往大而全,如果仅仅专注自己的那个角落,那么永远都是只见树木,不见森林。

    我相信没有老板会介意下面的程序员多承担一些责任,多做一些事情,最后给项目组多一些产出。

    既然老板不介意,那么就应该大胆的跳出自己的职责范围,多看看公司的其他项目,丰富自己的行业知识。

    职责外的事情,帮的上的不要躲避,,努力承担更多的东西。帮助别人就是提高自己,教学相长就是这个意思。况且你这次帮了别人,下次你的项目紧或者遇到技术难题了,受助之人肯定投桃报李,这样就形成了良性互助氛围,整个项目组的产出也同步提升了。

    一般来说,公司要提升一个人,最好的策略就是先让候选人做一些将来职位才需要做的事情。做的好,理所当然就要提拔。做的不好,则可以提前发现该员工的问题,暂缓提拔,需要多考察一段时间。这样的试错成本毫无疑问是最低的。

     

    3. 格局太小,对项目以外的技术视而不见,知识面陈旧,匮乏,技能极其单一

    这里其实谈到了整个IT行业的问题,技术发展太快了。主要还是一个持续提升竞争力的一个话题。

    今天还是桌面开发,明天web开发就成为主流。

    好不容易掌握了关系型数据库,No-SQL成为主流。

    费了九牛二虎之力,熟练掌握Java, C#等静态语言,发现动态语言GO, Python成为云计算,机器学习的标配。

    移动开发昨天还是Object-C, Java, 今天就变成Swift, Kotlin。

    当然这里不是说让大家紧跟潮流,扼住时尚。那样做除了疲于奔命,累死在工作台,没有其他的结果。

    其实只要选择一个方向,纵深学习和积累,必有所成。

    比如说,你熟悉 Java, 那么学习 Kotlin 绝对驾轻就熟。

    你有扎实的关系型数据库基础,那么掌握 MangoDB 肯定是件轻而易举的事情。

    编成思想和解决问题的思路都是相通的,平时的学习和工作中要善于思考,举一反三。并且做到与时俱进,及时更新自己的知识库和技能属性,保持良好的市场竞争力。

    做完事情后多思考,怎样做得更好,站在更好的要求上看问题.

    我举两个实际案例,

    程序员A在某国企里面,持续开发 Windows Form, 拖拉控件为主,对SQL Server数据库增删改查,时间长达五年之久,突然有一天打算离职,看看新的机会,以为有五年工作经验,可以很轻松找一个更好的工作。但是实际上求职之路异常艰辛,名义上的五年工作经验,其实就是极其单一的技能重复使用了五年。而且大环境也变了,主流已经是web开发,移动开发了。因为没有及时更新自己的知识库和技能储备,那么真要跳槽的时候可能已经跳不动了。

    程序员B在某外企,氛围比较安逸轻松,项目节奏慢,看似也作了不少项目,但做的项目几乎比较类似,难度一般,涉及面挺广,但技术点都是蜻蜓点水,浅尝辄止。这样过了三年,其实积累也是比较松散,知识的深度没有,核心技能并没有养成。

     

    4. 对未来没有想法,不考虑三五年自己要做什么

    作者本人就犯过类似的错误,幡然醒悟的时候,三年时间已经过去了。

    程序员的职业生涯里面最初的三年其实是一个非常重要的打磨和规划时期,如果在迷茫中度过,那么事后想起肯定会扼腕叹息,奈何流水已经东去,再无复返之理。

    有目标,而没有具体的计划,那么就是一个愿景而已。

    建议不管是初级还是高级程序员,都应该积极向前辈或者直属老板沟通,看看他们有没有值得借鉴的规划和建议。

    职场大忌就是被动等待命运的安排,作者本人也是在职业生涯初期等待老板来帮我规划未来,到现在为止,十年过去了,也没有等到。。。

    所以老板不会主动帮助你规划未来,最重要的事情还是自己对自身的要求和期望。

     

    5. 没有意识到是给自己打工还是公司打工

    这也是一个非常好的话题,很多人都没有想明白,甚至包括一些工作十年之久的程序员。

    想明白这一点,工作积极性明显就会好太多。自我驱动, 让工作更加有趣和有意义。

    国内知名的互联网公司大老板说过一句话,非常值得深思。

    “我每年付你20万,五年也就是100万。如果你在这里混日子,那么最后吃亏的肯定是你。你的五年青春就只值100万吗?”

    所以职场新人的主人翁意识一定要加强,你要持续提升自己的能力,持续强化自己创造价值的能力。

    举个例子,比如说现在公司支付你20万每年,那么你应该有目标能给公司带来远超过20万的收益,多多益善。

    你有这个能力,公司肯定也会对你相应回报。如果公司不给你升职加薪,那么一走了之,潇洒痛快。优质的人才从来都是抢着要。

     

    6. 不愿意走出舒适区域,不敢尝试新的东西

    这点倒是因人而异,不可强求。这个话题其实有点广义。

    如果你在某个方向做的非常好,而且回报也不错,那么不愿意涉足其他领域也无可厚非。

    这个世界唯一不变的东西就是世界一直在改变。今天还有的岗位,明天也许就要消失。世界要抛弃你,都不会打一声招呼。

    举个例子,你在公司是SQL Server或者Oracle专家,但是公司计划转非关系型数据库,如果你害怕改变甚至拒绝改变,那么意味着你可能要错过另外一个全新的数据存储平台。进而错过很多机会,而那些勇于接受变化,顺应趋势的人肯定会获得更好的时代回报。

    又比如说,你现在用的技术在日常项目中都刚好够用,那么从改善用户体验和使用更加主流的技术角度看,是不是应该要尝试一些新的东西,同时也刷新了自己的技术栈,一举两得,何乐而不为呢?

    还有一个例子是一位资深程序员习惯了长期的慢节奏的工作氛围,因为公司改组被裁员,不过自身条件不错,很快就加入国内一家一线互联网公司,但是完全适应不了互联网快节奏,工作一段时间就以公司管理”混乱”,战略规划”经常”改变为借口离职了,接下来很长一段时间找不到一个合适自己的工作,加上中年已到,如果自身不积极调整,接下来的工作和生活肯定困难重重。

     

    总结

    最后再简单小结一下,职场新人需要做的就是从小事做起,学会吃亏,以结果为目标导向,日常工作中积极和同事,老板沟通。

    工作中要善于总结方法,经常更新问题的思考模式,对职业负责,对目标负责,对自己负责,脚踏实地,主动找事情做,而不是被动等事情来找你。

    相信职场新人如果能成功避开上面说的几个误区,那么在职业发展道路上就可以少走一些弯路,少犯一些错误,从而更快地实现自己的小目标。

    展开全文
  • 常见问题敏捷是如何解决的? 敏捷痛点及解决方式 问题 解决方式 团队目标或任务不明确 敏捷章程愿景、使命和使命测试 团队工作协议不明确 敏捷章程...

    常见的问题敏捷是如何解决的?

    敏捷痛点及解决方式

    问题

    解决方式

    团队目标或任务不明确

    敏捷章程愿景、使命和使命测试

    团队工作协议不明确

    敏捷章程价值观、原则、工作协议

    团队环境不明确

    敏捷章程边界、承诺和前瞻性

    需求不明确

    用户故事地图和影响地图来构建产品路线图

    用户体验不佳

    设计阶段就让客户尽早参与

    估算不准确

    分解故事让故事变小

    工作分配或工作进展不明确

    自我管理工作、每日站会、看板查看进展

    团队面临障碍

    仆人式领导,如还是无法消除则聘请教练或上报故事

    产品待办事项列表不够完善,导致延误或超时

    PO和团队一起研讨故事,考虑分拆故事以使用更小的故事

    缺陷或BUG

    结对工作、产品集体负责制、普适测试

    工作未完成

    确定故事完成的定义,包括验收标准、发布标准在内

    技术债务(代码质量降级)

    重构、敏捷建模、普适测试、自动化代码质量分析、完成的定义

     

    产品复杂性过高及解决方式

    问题

    解决方式

    团队合作过程进展缓慢或没有改善

    每次回顾中,改进项目不要超过三个,仆人式领导帮助团队学习怎样去整合这些待改进项

    前期工作过多导致返工

    刺探学习、并不需要设计,只需要交付价值。缩短迭代,并创建一个稳健的完成的定义

    错误的开始,前功尽弃

    让PO成为团队不可分割的一份子

    产品代办事项列表杂乱无序

    按价值排序

    仓促或等待,不均匀的工作流程

    不是超出能力所及,并且成员不能多任务,必须要专注

    相关方要求无法满足

    仆人式领导与这个相关方一起工作

    意想不到或不可预见的延误

    更频繁的检查,使用看板面板检查工作流,了解需求对团队或产品的影响

    孤立的团队,而不是跨职能团队

    项目人员作为跨职能团队自我组织

     

    扩展-其他主流的敏捷方法论

    特性驱动开发(FDD)

       为产品开发一个整体的模型,构建特性列表和工作计划;团队对开发的特性进行设计和构建。

    FDD实践

       领域对象建模:描述问题领域中重要对象的类型及其相互关系,为系统设计提供一个整体框架,其中包括构造类图等。这个在数据仓库设计的应用较多。

       按照特性开发:将需求问题分解成可以解决的小问题。

       类(代码)所有权:指定某个人负责某个类的代码一致性、性能和概念的完整性。

       特性小组:组长更像是教练去做协调,而不是超级开发人员。

       配置管理:设计、测试用例、脚本等等也应该受到版本控制。

       定期构建:更容易的创建演示程序。

       可视化的进度报告:如白板。

    FDD小结

       特性驱动开发(FDD)就是将需求化成小问题。

       FDD实践:领域对象剑魔、按照特性开发、特性小组、配置管理、定期构建、可视化的进度报告。

     

     

    动态系统开发方法(DSDM)

       也称为业务中心框架开发方法,是敏捷方法的一种。倡导以业务为核心,快速而有效的进行系统开发。重点在于快速交付并补充如何应用这些控制的指导原则框架。

    DSDM提倡20%的时间完成80%的功能,已经在数据仓库、组件开发、原型业务等应用。

     

    DSDM开发周期

       项目准备阶段:确保启动、建立正确的项目,可行性研究阶段和业务阶段都是顺序进行的,为后续的冲刺、增量式开发,制定了基本规则。

       可行性研究阶段:侧重评估DSDM方法是否适用于本项目,以便确定这样做是否值得;在制定一份全面的可行性报告,还需要提供应对和控制风险的策略,如果对业务叨叨足够的理解了就可以了,即足够就好,无需过多。

       业务研究阶段:对业务流程进行分析和定义。功能性需求还是非功能需求分类,并且划分对应的优先级。所有制定优先级的原则也必须要以业务为导向,把技术上要求先实现的功能作为高优先级。这个开发计划应该包含功能建模阶段和设计编程阶段的开发策略、测试策略和配置管理计划。

       功能建模阶段(冲刺式):深入分析业务区定义的功能并进行细化,在分析原型的基础上构建软件模块,将创建的原型交付给用户评审;评审后,进一步的充实和改进。这样不断的冲刺,使得原型逐渐演化成可工作的软件。产出物带有优先级的功能、功能性原型评审文档、非功能性需求、实施计划(业务方案、培训计划、各种知识和技术的准备等)。

       系统设计编程阶段(冲刺式):根据功能建模的标准建造实际的系统并通过测试,测试应该是贯穿于整个功能建模和设计编码过程的。

       实施阶段:培训用户,完成系统开发环境向生产环境的移交。

       项目后期:评审当前使用的方法并评估是否达到预期的结果。

     

    DSDM小结

       提倡20%的时间,完成80%的功能。

       开发周期:项目准备阶段、可行性研究阶段、业务研究阶段、功能建模阶段(冲刺式)、系统设计编程阶段(冲刺式)、实施阶段、项目后期。

     

    水晶(Crystal)

       水晶方法体系与XP一样,都是以人为中心的理念,但实践上有所不同。水晶方法考虑到人们一般很难遵循一个纪律约束性很强的过程。因此,与XP的高度纪律性不同,水晶方法体系探索了用最少的纪律约束而仍能产出成果的方法,从而在产出效率上与易于运作上达到纪律约束而仍能产出成本的方法,从而在产出效率与易于运作上达到一种平衡。虽然水晶系列不如XP那样产出效率,但是会有更多人接受。

     

    Crystal开发方法

       透明水晶(Crystal Clear)、黄水晶(Crystal Yellow)、橙水晶(Crystal Orange)、红水晶(Crystal Red)分别适用于不同的项目。

     

    Crystal重要性

       根据项目的错误引发后果分为:

       不舒适(C-Loss Of Comfort);

       经济损失(D-Loss Of Discretionary Money);

       严重经济损失(E-Loss Of Essetial Money);

       生命危险(L-Life Critical);

     

    水晶与敏捷相同的原则

       频繁交付:增量构建、检查验收。

       反思改进:及时反思和改进。

       渗透式沟通:同一空间。

       个人安全:不会受到惩罚。

       焦点:确定首先要做什么,然后按照时间,以平和的心态去开展工作。

       与专家用户建立方便的联系:例如质量快速反馈、设计理念快速反馈。

       配有自动测试:允许人们不同步的对工作进行检查、可撤销更改、还原系统设置等。

     

    Crystal小结

       开发方法:透明水晶(Crystal Clear)、黄水晶(Crystal Yellow)、橙水晶(Crystal Orange)、红水晶(Crystal Red)

       重要性:不舒适(C-Loss Of Comfort);经济损失(D-Loss Of Discretionary Money);  严重经济损失(E-Loss Of Essetial Money); 生命危险(L-Life Critical);

       与敏捷相同原则:频繁交付、反思改进、渗透式沟通、个人安全、焦点、自动化测试等。

     

    精益开发

       敏捷和精益的价值观是紧密相关的。精益的一系列原则是从精益生产中来的。精益带给开发人员一些技术和概念,如价值流向图、浪费的7种形式、拉动系统及在制品。

     

    精益核心概念

       消除浪费:要最大化价值,必须最小化浪费。

       构建质量:不会试图在结束的时候测试质量。相反,开发人员在整个开发过程中会构建产品质量和持续的确保质量,通常使用的技术有重构、持续集成和单元测试

       创建知识:尽早和频繁的使用沟通技术,尽可能快的得到反馈。尽可能持续的保持学习状态。

       推迟决策:决策太早使你不能获得足够的信息;决策的太晚会使你承担更高的成本风险。所以开发人员要在两者之间找到平衡。

       快速交付:快速交付软件已最大化软件的价值(ROI)在快速的冲刺过程中,也可以找到更好的解决方案。

       对人尊重:尊重管理层和员工,开发过程中允许他们具有灵活性,并持续的改进过程,以期吸引和留住高素质员工。

       整体优化:尝试优化时,应该试图包含尽可能多的价值流。局部的优化若不能带来整体的改善是没有价值的。

     

    精益小结

       精益是日本丰田实践而来的,敏捷是学习了精益的部分思想。

       核心概念:消除浪费:构建质量、创建知识、推迟决策、快速交付、对人尊重、整体优化。

     

    看板开发

       近年来最热门的敏捷和精益开发方法之一,能够改善协作、优化管理、显著的提高交付速度、质量和灵活性。规则简单,其有效实施依赖于对原理的理解、对高质量的坚持和实践的应变。

       限制了在制品(WIP)的数量,这样可以帮助识别在开发过程中产生的问题和最小化浪费,以及与成本相关的变更,并使用一个拉动系统工作。

     

    核心实践

       可视化工作(价值)流:产品开发的加工对象信息是抽象和不可见的。看板开发方法把可视化工作流作为基础实践,先让价值和价值流动具体可见,然后才是管理和优化。

       限制在制品(WIP)数量:即每个冲刺所要完成的工作是有限制的。

       度量和管理流动:快速,顺畅的价值流动是看板开发方法的目标。快速流动带来快速的价值产出和快速反馈。顺畅流动意味着稳定和可预测的价值交付能力。度量为改善价值流动提供方向参考,同时为改善的结果提供反馈。

       协同改进:发现问题还不够,重要的是如何去解决这些问题。

       显式化流程规则:明确定义和沟通团队所遵循的流程规则。定义了一个价值项从一个阶段进入下一个阶段所必须达到的标准。定了从分析阶段进入开发阶段所必须达到的条件。这与精益制造中内建质量的思想是一致的。

     

    小结

       核心实践:可视化工作(价值)流、限制在制品(WIP)数量、度量和管理流动、协同改进、显式化流程规则。

     

    Scrum Of Scrums

    是大规模敏捷框架,采用经济视角、应用系统思维、为可能性预留方案、快速整合学习周期进行增量式构建、客观评估设定里程碑、解锁成员内在动力、决策分散。

     

    大规模敏捷开发(LeSS)

    是以扩展Scrum方法为共同目标来组织多个开发团队的框架。系统思维、整体产品专注、透明等。

     

    总结

    敏捷永恒不变的真理:检查、适应和透明度是成功交付价值的关键因素。没有适应检查只是浪费精力。

    有价值的软件使得客户满足、欢迎变更、经常交付可用的软件、协力协作、激励项目人员、面对面的交谈、可用的软件是衡量进度的标准、可持续的开发、不断完善、简洁、定期反省。

    最佳架构、需求和设计将出自于自组织团队

    规范敏捷(DA):以人为先、面向学习、完全交付生命周期、目标驱动、企业意识、可扩展。

    评估变更的理想方式是首先尝试一个或两个迭代,然后通过回顾和重新评估来反思。

    透明是需要勇气的,可以通过使用状态板或白板。

    不要盲目的使用敏捷方法,授权经验不足的自组织团队解决一切问题。

    缺乏管理层支持时,要找到共同点和改进之处。

    较小的不太关键的项目应采用简单的方法并实施较弱的控制。大型任务或生命关键型项目建议使用更高的严格程度和验证级别。

    适应性评估模型:(越靠近中心点越敏捷,预测型、混合型、敏捷型)

    文化:支持、信任度、决策能力

    团队:规模、经验水平、客户或业务联系程度

    项目:变更可能性、产品或服务关键性、增量交付

     

    我为什么要学习敏捷?敏捷究竟对我有什么好处?

       在国内职场有一句话很经典,要想涨工资,请选择跳槽。这背后说明了,国内公司还处于一个矮子里挑高个。敏捷不提倡加班,在国内的公司99.99%都出现敏捷是一种新的压榨方式。但是国内每一个公司都知道国外的公司都在使用敏捷,如果自己还不使用的话,可能会给市场淘汰了。虽然在使用的敏捷都是假敏捷,但是还是想要以此来充实自己的公司,也以便更好的在资本市场上讲故事,这就如同17年的人工智能、大数据那般,可惜现在大家对人工智能、大数据这类东西摸透了,也看透了,以及国内真正做的起大数据的公司就头部那几家,其他公司大多数都是在使用大数据技术应用层面,仅仅用来存储数据,这也是为什么现在大数据公司在资本市场上不在像17年那般受到疯狂的追棒。

       现在的敏捷时代,这个情况有点类似17年的人工智能、大数据那般。如果作为管理人员的你掌握了敏捷,你应用过敏捷,能够吹一吹,你会发现Off拿到的可能性更高了,可谈的薪资更高了。

       另外敏捷是一种思想,也是一种方法论,对于个人在做事方面,未来国内企业环境不再是以加班,而是更人性化的方式时,你的价值就更大了。

       要想涨工资,请选择跳槽。

       要想涨更多,请学习敏捷。

     

    展开全文
  • 只要你奔波于忙碌的职场,每天就会有一个共同的“工作”要做——那就是,回答问题。下面小编为大家整理了职场沟通的必备技巧,欢迎大家阅读参考!职场沟通必备技巧问答是最基本的交流方式,也是职场中感情沟通的重要...
  • 尊重原则:职场新人新到职场遭到“欺生”很常见,如果掌握了处事技巧这就没问题了,小峰在佛山觅得一份新的文案策划职位,新公司让其过完年后报到。有一年工作经验的小峰,这次并不像当初大学刚毕业时表现得那么青涩...
  • 力求帮助大家提升Excel的了解度,并希望通过本课程可以帮您解决日常办公中常见的一些问题,提高办公效率。会包含如何利用Excel制作抽奖程序、如何使用Excel制作条形码、二维码、如何使用Excel制作个人工作安排等等。
  • 安全测试面试常见问题有哪些?

    千次阅读 2021-11-19 14:39:44
    由于许多应用程序包含机密数据,需要被保护泄漏。软件测试需要定期在这样的应用程序上进行,以识别威胁并立即采取行动。 什么是漏洞 漏洞可以被定义为任何系统的弱点(Vulnerability),入侵者或bug可以通过该...
  • 2、进程的常见状态?以及各种状态之间的转换条件?3、进程的调度算法有哪些?4、非抢占式调度与抢占式调度?5、什么是死锁?产生死锁的条件?如何避免死锁?6、进程间通信(IPC)有哪些方式?他们的区别?7、线程间...
  • 问题1:测试用例是你自己写的吗(或是问你是否写过测试用例)? 我写过测试用例,一般情况下,我们项目组内成员都是各自负责各自的模块,进行相应... 以上就是测试用例设计面试的常见问题,欢迎小伙伴们在评论区补充~~
  • 常见人事问题

    千次阅读 2016-10-09 10:34:03
    整体素养的常见问题 一、请你自我介绍一下你自己? (面试官目的:深度了解求职者,看求职者基本的沟通和自我认知能力) NO: 只说姓名、年龄、爱好等基本的信息后就没了。只重复简历里的内容,如工作经验就...
  • 问题零,强调自己正确。 问题一,缺乏背景铺垫,缺乏关键目标。 问题二,缺乏可信性评估和证明。 问题三,缺乏目标激励。 问题四,沉浸于自high,无法自拔。 问题五,主次不分,啰里啰唆。 问题六,缺乏必要的尊重。
  • 自己工作 10+ 年了,大概 5 年前从技术转管理后,多少还是发现一些职场新人职业发展的问题。 表现惊艳的新人肯定有,这种人往往在学校里面或者进入职场后就养成了一些非常良好的做事方式和工作习惯,工作效率高,...
  • 笔者工作10+年了,大概5年前从技术转管理后,多少还是发现一些职场新人职业发展的问题。表现惊艳的新人肯定有,这种人往往在学校里面或者进入职场后就养成了一些非常良好的做事...
  • 常见面试人事问题汇总(下)41、你希望与什么样的上级共事?42、在完成某项工作时,你认为领导要求的方式不是最好的,自己还有更好的方法,你应该怎么做?43、与上级意见不一是,你将怎么办?44、你工作经验欠缺,...
  • 不论是正在求职的菜鸟,还是“资深”的职场人士,面试都是一道必经的考验。 你没有遇到过这样的情况: 连续面试过好几家公司,为什么总是通不过? 被面试官问了几个常见问题,为什么总是回答不好? 面试心仪的...
  • 数据结构常见问题和概念

    千次阅读 2019-10-21 11:50:37
    面试中关于数组的常见问题   寻找数组中第二小的元素 找到数组中第一个不重复出现的整数 合并两个有序数组 重新排列数组中的正值和负值   栈   著名的撤销...
  • 名企常见题库 随着每一年应届生数量水涨船高,企业对于人才筛选的要求也越来越高,其中就包括刷人比例最高的笔试环节。 庞大的需求也催生出各式各样的人力测评系统,从最开始大家熟悉的SHL、Cut-e,到北森、智鼎、...
  • 神马是PUA? PUA,全称是Pick-up Artist,这是起源于美国的“搭讪艺术”,原本是用于男女两性交往的一套方法。...常见职场PUA套路有哪些? 套路一 一系列否定用语,包括: “你tmd太差了,什么都做不好。” “..
  • 第一章 关于以往工作的问题 企业主要考察求职者以往的业绩、职业态度、责任感、进取精神、开拓精神等。 1、介绍一下这家公司和你的工作内容吧 问题分析:HR主要考查该公司的规模,个人的工作能力是否能胜任该职位。 ...
  • 职场中典型的学生思维有哪些?

    万次阅读 2020-12-03 23:15:41
    骑驴找马,总想着跳槽跳槽 不可否认,在北京这座城市,跳槽是一个很常见的事情,很多人的高薪都是一步步跳出来的,包括我自己也曾跳槽, 但是孩子们诶,你们才刚刚毕业,独立处理事情的能力都没有,就妄想着跳槽?...
  • 包含并管理应用对象Bean的配置和生命周期,这个意义上是一个容器 将简单的组建配置,组合成复杂的应用,这个意义上是一个框架 2.谈谈你对AOP的理解 AOP--面向切面编程:能够将那些与业务无关的,但为业务模块...
  • k8s面试中最常见的50个问题(翻译)

    千次阅读 2021-01-21 21:38:18
    它吸引了许多想提升自己职场能力的有经验的专业人员。许多跨国公司如华为、口袋妖怪、Box、ebay、Ing、日本Yahoo、SAP、纽约时报、Open Ai, Sound Cloud也在使用k8s。我相信你已经知道了这些事实,促使你打开这篇...
  • [产品面试]常见5个面试问题(九)

    千次阅读 2021-12-15 12:00:42
    可以结合用户体验的5大要素来讲,包括战略层(定位与目标用户)、范围层(功能列表及优先级)、结构层(功能关系、信息架构)、框架层(流程与逻辑)、表现层(UI、交互)。 这里,我以人体来举例: 骨骼:...
  • 简单开场:包括基本的教育经历和一些基本信息,一到两句就可以了。 为什么要进入这个行业:比如说做过什么实习,参加过什么比赛,有过什么特殊的经历。 举一个最有代表性的例子来证明。 “我暑假曾在一家公司做过...
  • 职场,人才的价值是如何评估的,今天做个框架模板,那么第一版,简单一点,以后也许会做一些调整和增补。一个人才的价值是如何评定的,我认为可以分为以下几个大的模块。通用能力,专业能力,资源,个...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 12,586
精华内容 5,034
关键字:

常见的职场问题包括