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

====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. ADO.NET的五大对象

    ADO.NET是一种数据访问技术,使得应用程序可以连接到数据库存储,并以各种方式操作存储在其中的数据.该技术基于.NETFramework,与.NET Framework类库的其余部分高度集成. 其中 ...

  2. 三色标记法与读写屏障, G1工作过程

    https://www.jianshu.com/p/12544c0ad5c1 https://www.cnblogs.com/GrimMjx/p/12234564.html 自我总结和记忆: 为了解决 ...

  3. 说说对 Node 中的 fs 模块的理解? 有哪些常用方法?

    一.是什么 fs(file system),该模块提供本地文件的读写能力,基本上是POSIX文件操作命令的简单包装 可以说,所有与文件的操作都是通过fs核心模块实现 导入模块如下: const fs  ...

  4. Django基础之路由层

    内容概要 路由匹配 无名有名分组 反向解析 无名有名分组反向解析(难理解) 路由分发 名称空间 伪静态 内容详细 1 路由匹配 urls.py url()方法第一个参数其实是一个正则表达式 第一个参数 ...

  5. DDD实战课(实战篇)--学习笔记

    目录 DDD实践:如何用DDD重构中台业务模型? 领域建模:如何用事件风暴构建领域模型? 代码模型(上):如何使用DDD设计微服务代码模型? 代码模型(下):如何保证领域模型与代码模型的一致性? 边界 ...

  6. 『无为则无心』Python基础 — 3、搭建Python开发环境

    目录 1.Python开发环境介绍 2.Python解释器的分类 3.下载Python解释器 4.安装Python解释器 5.Python解释器验证 1.Python开发环境介绍 所谓"工欲 ...

  7. Windows10无线能连上但没有网络

    解决思路:重置widows10网络配置 步骤: 1.win+X键 2.在命令提示符窗口中,输入命令行:netsh winsock reset 3.然后按下回车键,这时就会提示重置Winsock目录成功 ...

  8. 基于GIS的国土空间规划平台建设

    ​ 本期介绍基于地理信息平台的国土空间规划平台的规划辅助编制应用.在梳理国土空间规划科学流程的基础上,将规划编制各关键环节信息化.工具化.智能化:充分发挥清华同衡大数据与智能模型相结合的定量评估.精准 ...

  9. c#在类中使用session

    先要继承页面的System.Web.UI.Page using System; using System.Collections.Generic; using System.Linq; using S ...

  10. 入门Kubernetes - 滚动升级/回滚

    一.前言 上一篇文章中对yaml文件格式进行了解,并对k8s中各种主要资源通过yaml创建时的定义模板.接来下就进一步学习k8s的各种特点.并应用在示例中. 接下来先实现.Net Core Api程序 ...