为了有针对性的准备面试,锁屏面试题百日百刷开始每日从各处收集的面经中选择几道经典面试题分享并给出答案供参考,答案中会做与题目相关的扩展,并且可能会抛出一定问题供思考。这些题目我会标注具体的公司、招聘类型(校招、社招、实习)以及面试阶段。下面是今日面试题:

====ArrayList 和 LinkedList 实现原理和前者的扩容机制?[阿里云][校招][面]

以下内容均是以jdk1.8来讲:

ArrayList实现原理:

Arraylist底层是用一个Object数组elementData来保存数据的:

transient Object[] elementData; // non-private to simplify nested class access

从创建一个ArrayList开始看,一般都是通过new即通过构造函数来创建一个ArrayList看源码:

public ArrayList() {

        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

}

public ArrayList(int initialCapacity) {

        if (initialCapacity > 0) {

            this.elementData = new Object[initialCapacity];

        } else if (initialCapacity == 0) {

            this.elementData = EMPTY_ELEMENTDATA;

        } else {

            throw new IllegalArgumentException("Illegal Capacity: "+

                                               initialCapacity);

        }

}

public ArrayList(Collection<? extends E> c) {

        elementData = c.toArray();

        if ((size = elementData.length) != 0) {

            // c.toArray might (incorrectly) not return Object[] (see 6260652)

            if (elementData.getClass() != Object[].class)

                elementData = Arrays.copyOf(elementData, size, Object[].class);

        } else {

            // replace with empty array.

            this.elementData = EMPTY_ELEMENTDATA;

        }

}

很简单,无非就是无参构造只会创建一个空的数组,带初始化数组大小的构造器会创建一个知道容量的ArrayList,以及用另一个ArrayList初始化新的ArrayList的构造函数。

再看ArrayList的操作:

1)添加:

public boolean add(E e) {

        ensureCapacityInternal(size + 1);  // Increments modCount!!

        elementData[size++] = e;

        return true;

}

private void ensureCapacityInternal(int minCapacity) {

        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);

        }

        ensureExplicitCapacity(minCapacity);

}

private void ensureExplicitCapacity(int minCapacity) {

        modCount++;

        // overflow-conscious code

        if (minCapacity - elementData.length > 0)

            grow(minCapacity);

}

private void grow(int minCapacity) {

        // overflow-conscious code

        int oldCapacity = elementData.length;

        int newCapacity = oldCapacity + (oldCapacity >> 1);

        if (newCapacity - minCapacity < 0)

            newCapacity = minCapacity;

        if (newCapacity - MAX_ARRAY_SIZE > 0)

            newCapacity = hugeCapacity(minCapacity);

        // minCapacity is usually close to size, so this is a win:

        elementData = Arrays.copyOf(elementData, newCapacity);

}

这里,modCount是我们每次对ArrayList做改变大小的时候会做一次自增操作的变量,记录下修改次数。核心的常问的一个方法就是这个grow()方法,这是当ArrayList中元素的数组(上面讲到的存储数据的)elementData容量不够时做的扩容操作。这里我们以用空参构造器创建ArrayList时,第一次向ArrayList添加元素时会做一次扩容操作来讲:

判断容量是否足够的就是这么一句:if (minCapacity - elementData.length > 0)

在grow中,核心的一段就是int newCapacity = oldCapacity + (oldCapacity >> 1);新的容量是旧的容量的1.5倍(oldCapacity >> 1实际上是一次oldCapacity除2操作)

注意这里扩容触发条件是在使用无参构造器第一次添加元素,以及添加元素时,判断发现数组长度不够时。

2) 删除:

public boolean add(E e) {

        ensureCapacityInternal(size + 1);  // Increments modCount!!

        elementData[size++] = e;

        return true;

}

private void ensureCapacityInternal(int minCapacity) {

        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);

        }

        ensureExplicitCapacity(minCapacity);

}

private void ensureExplicitCapacity(int minCapacity) {

        modCount++;

        // overflow-conscious code

        if (minCapacity - elementData.length > 0)

            grow(minCapacity);

}

private void grow(int minCapacity) {

        // overflow-conscious code

        int oldCapacity = elementData.length;

        int newCapacity = oldCapacity + (oldCapacity >> 1);

        if (newCapacity - minCapacity < 0)

            newCapacity = minCapacity;

        if (newCapacity - MAX_ARRAY_SIZE > 0)

            newCapacity = hugeCapacity(minCapacity);

        // minCapacity is usually close to size, so this is a win:

        elementData = Arrays.copyOf(elementData, newCapacity);

}

  

看代码很简单,就是做一次数组复制,移动要删除的某一位置元素的后面的元素,并且将旧数组置为null做垃圾回收。指定remove(Object O)这个方法也是先找对应元素的下标,然后做与上面代码差不多的操作。有兴趣的可以看下源码。

查询就没啥好讲的了,底层是数组存储,查询很容易做到。可自行看源码。

LinkedList实现原理:

LinkedList存储是通过双向链表实现的:

transient Node<E> first;

transient Node<E> last;

private static class Node<E> {

        E item;

        Node<E> next;

        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {

            this.item = element;

            this.next = next;

            this.prev = prev;

        }

 }

  

它的空参构造不做任何操作,带参构造LinkedList(Collection<? extends E> c)也是着重体现在添加元素的操作,所以直接讲它的一些操作,这里需要大家能够懂链表的增删改查的操作,因为LinkedList的这些操作就是根据这些来的。希望大家能够找一下资料了解下,这里篇幅有限,不作讲解。

1) 添加:

头部添加:

public void addFirst(E e) {

        linkFirst(e);

 }

private void linkFirst(E e) {

        final Node<E> f = first;

        final Node<E> newNode = new Node<>(null, e, f);

        first = newNode;

        if (f == null)

            last = newNode;

        else

            f.prev = newNode;

        size++;

        modCount++;

}

  

看下上面关于LinkedList一开始讲的,可以看到LinkedList成员变量就有保存first和last,这里从头部插入和从尾部插入基本上操作一致,只是保存位置不同,看代码很容易看懂,就是对链表的操作,至于add()方法是在尾部插入新的元素。

2) 删除

removeFirst()和removeLast()以及remove()(remove和removeFirst()操作一致),都是简单去链表表头或表尾的操作,可以自己看源码,熟悉链表操作的同学很容易就能看懂,不作赘述。说一说remove(int index):

public E remove(int index) {

        checkElementIndex(index);

        return unlink(node(index));

 }

Node<E> node(int index) {

        // assert isElementIndex(index);

// 这里是做了一个加速查找的操作

// 获取index处的节点。    

        // 若index < 双向链表长度的1/2,则从前先后查找;    

        // 否则,从后向前查找。

        if (index < (size >> 1)) {

            Node<E> x = first;

            for (int i = 0; i < index; i++)

                x = x.next;

            return x;

        } else {

            Node<E> x = last;

            for (int i = size - 1; i > index; i--)

                x = x.prev;

            return x;

        }

  }

E unlink(Node<E> x) {

        // assert x != null;

        final E element = x.item;

        final Node<E> next = x.next;

        final Node<E> prev = x.prev;

        if (prev == null) {

            first = next;

        } else {

            prev.next = next;

            x.prev = null;

        }

        if (next == null) {

            last = prev;

        } else {

            next.prev = prev;

            x.next = null;

        }

        x.item = null;

        size--;

        modCount++;

        return element;

}

  

这里是先根据index找到LinkedList中存储的节点,见上图node()方法,然后开始删除元素操作,见unlink()方法,熟悉链表的删除操作,很容易能看懂,判断删除的元素是否是表头后表尾,是的话简单的赋值一下即可,不是的话做相应的删除操作。Remove(Object o)这个方法也是先找到这个list中是否有这个元素,先通过查找,再调用unlink()方法删除。

3)查找

LinkedList查找操作在上面的删除操作中,先查找在做删除的步骤中已经讲过(上面的node方法),请自行对照源码查看查找操作。

锁屏面试题百日百刷-java大厂八股文(day3)的更多相关文章

  1. Android逆向之旅---Android中锁屏密码算法解析以及破解方案

    一.前言 最近玩王者荣耀,下载了一个辅助样本,结果被锁机了,当然破解它很简单,这个后面会详细分析这个样本,但是因为这个样本引发出的欲望就是解析Android中锁屏密码算法,然后用一种高效的方式制作锁机 ...

  2. Java计算手机九宫格锁屏图案连接9个点的方案总数

    (一)问题 九宫格图案解锁连接9个点共有多少种方案? (二)初步思考 可以把问题抽象为求满足一定条件的1-9的排列数(类似于“八皇后问题”),例如123456789和987654321都是合法的(按照 ...

  3. Java防锁屏小程序

    为防止系统桌面自动锁屏,只需打成jar包,写个批处理程序start.bat,双击执行保持dos窗口执行即可,无其他影响. 程序设计为每30秒动一次鼠标,可根据需要调整. 附代码: package ma ...

  4. 第一百三十四节,JavaScript,封装库--遮罩锁屏

    JavaScript,封装库--遮罩锁屏 封装库新增1个方法 /** zhe_zhao_suo_ping()方法,将一个区块元素设置成遮罩锁屏区块 * 注意:一般需要在css文件将元素设置成隐藏 ** ...

  5. Appium 解决锁屏截屏问题(java篇)

    今天有个小伙伴问我,怎么把锁屏进行解锁操作?   A.思路在初始化driver后,加入等待判断是否有锁屏(元素)(记得要加入等待) B.如果有就进行解锁,就一般的输入数字密码然后进行解锁(当然了你要知 ...

  6. android黑科技系列——修改锁屏密码和恶意锁机样本原理分析

    一.Android中加密算法 上一篇文章已经介绍了Android中系统锁屏密码算法原理,这里在来总结说一下: 第一种:输入密码算法 将输入的明文密码+设备的salt值,然后操作MD5和SHA1之后在转 ...

  7. 解决:Android4.3锁屏界面Emergency calls only - China Unicom与EMERGENCY CALL语义重复

    从图片中我们可以看到,这里在语义上有一定的重复,当然这是谷歌的原始设计.这个问题在博客上进行共享从表面上来看着实没有什么太大的意义,不过由于Android4.3在锁屏功能上比起老版本做了很大的改动,而 ...

  8. 【Android代码片段之八】监听Android屏幕是否锁屏

    实现方法:1)通过BroadcastReceiver接收广播Intent.ACTION_SCREEN_ON和Intent.ACTION_SCREEN_OFF可以判断屏幕状态是否锁屏,但是只有屏幕状态发 ...

  9. Android 锁屏软件MemoryDebris测试报告

    目 录 项目基本信息 第1章         引言 1.1        编写目的 1.2        项目背景 1.3        参考资料 1.4        术语和缩略语 第2章      ...

随机推荐

  1. 通过Dapr实现一个简单的基于.net的微服务电商系统(十七)——服务保护之动态配置与热重载

    在上一篇文章里,我们通过注入sentinel component到apigateway实现了对下游服务的保护,不过受限于目前变更component需要人工的重新注入配置以及重启应用更新componen ...

  2. 从Vehicle-ReId到AI换脸,应有尽有,解你所惑

    最近在做视频搜索的技术调研,已经初步有了一些成果输出,算法准确性还可以接受,基本达到了调研的预期.现将该技术调研过程中涉及到的内容总结一篇文章分享出来,内容比较多,初看起来可能关系不大,但是如果接触面 ...

  3. UF_VIEW 视图操作

    Open C   uc6400uc6401uc6402uc6403uc6404uc6405uc6406uc6408uc6409uc6430uc6431uc6432uc6433  获得视图3×3矩阵uc ...

  4. Mac下安装及配置Appium环境

    candiceli   Mac下安装及配置Appium环境 我是小白,自己研究appium好几周了. 一开始按照同事这篇文章设置Mac下的环境,http://www.cnblogs.com/tangd ...

  5. Pytest测试框架入门到精通(一)

    Python测试框架之前一直用的是unittest+HTMLTestRunner,听到有人说Pytest很好用,所以这边给大家介绍一下Pytest的使用 pytest是一个非常成熟的全功能的Pytho ...

  6. 如何基于MindSpore实现万亿级参数模型算法?

    摘要:近来,增大模型规模成为了提升模型性能的主要手段.特别是NLP领域的自监督预训练语言模型,规模越来越大,从GPT3的1750亿参数,到Switch Transformer的16000亿参数,又是一 ...

  7. Redis的bitmap从基础到业务

    1. 位与字节 1个字节(byte)等于8个位(bit).(计算机常识). 2. string与bitmap Redis里的bitmap是属于string这个数据类型里的.可以help进行查看bit相 ...

  8. Git——基础篇

    Git--基础篇 Git简而言之就是个版本控制工具,用于跟踪和管理代码的工具. 版本控制 本地版本控制 就是我们自己本地对文档进行保存文件的每一个修改版本的记录. 集中式版本控制--SVN 有一个远程 ...

  9. Docker安装MySQL8.0

    环境 CentOS 7.5 Docker 1.13.1 MySQL 8.0.16 安装 拉取镜像 默认拉取最新版本的镜像 $ docker pull mysql 如果要指定版本,使用下面的命令 $ d ...

  10. 资源:Nginx安装包的下载路径

    下载路径如下: Nginx所有版本:http://nginx.org/download/