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

====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. c语言经典算法---计算Fibonacci数列

    算法是一个程序和软件的灵魂,作为一名优秀的程序员,只有对一些基础的算法有着全面的掌握,才会在设计程序和编写代码的过程中显得得心应手.下面我就分享一个C语言中比较基础却极为重要的一个算法----计算Fi ...

  2. 来了!STM32移植LuatOS,潘多拉示例全新教程

    进击的五月,继上期<使用Air724UG制作简易贪吃蛇>教程之后,@打盹的消防车 又为大家带来基于STM32的潘多拉LuatOS移植全新教程: 为什么使用潘多拉作为教程呢? STM32不能 ...

  3. noip2013 总结

    转圈游戏 题目 n 个小伙伴(编号从 0 到 n-1)围坐一圈玩游戏.按照顺时针方向给 n 个位置编号,从0 到 n-1.最初,第 0 号小伙伴在第 0 号位置,第 1 号小伙伴在第 1 号位置,-- ...

  4. [INS-32033] Central Inventory location is not writable

    这个是因为之前安装过一次图形界面,已经创建过Inventory,所以会报错. 解决:删除oraInventory这个目录.

  5. redis学习第二天

    Redis 在 2.8.9 版本之后添加了 HyperLogLog 结构 Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常 ...

  6. 在Visual Studio 中使用git——同步到远程服务器-下(十二)

    在Visual Studio 中使用git--什么是Git(一) 在Visual Studio 中使用git--给Visual Studio安装 git插件(二) 在Visual Studio 中使用 ...

  7. Cloudflare DDoS配置案例

    导航: 这里将一个案例事项按照流程进行了整合,这样查看起来比较清晰.部分资料来自于Cloudflare 1.DDoS介绍 2.常用DDoS攻击 3.DDoS防护方式以及产品 4.Cloudflare ...

  8. 最强阿里巴巴历年经典面试题汇总:C++研发岗

    (1).B树.存储模型 (2).字典树构造及其优化与应用 (3).持久化数据结构,序列化与反序列化时机(4).在无序数组中找最大的K个数? (4).大规模文本文件,全是单词,求前10词频的单词 (5) ...

  9. 6、inotify实时备份

    备份用户nfs共享文件系统,存储单点解决方案inotify+rsync(增量,无差异备份),inotify是单线程, inotify是一种强大的,细粒度的,异步的文件系统事件监控机制,通过加入了ino ...

  10. 16、如何将安装在chrome上的插件(扩展程序)打包成".crx"文件

    1.打开扩展程序: 2.打开开发者模式并选择要打包的插件: 3.打包扩展程序: (1) (2) (3)