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

====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. 采用MVC模式创建一个简单的javascript App

    初次翻译,翻译的不好,还请见谅 JavaScript中最好的一部分之一,也可能是最糟糕的. 在HTML文档的头部添加一个开始和结束脚本标记,并在其中引入一些意大利面条式的代码,毫无疑问这是一种过分简单 ...

  2. springboot——重定向解决刷新浏览器造成表单重复提交的问题(超详细)

    原因:造成表单重复提交的原因是当我们刷新浏览器的时候,浏览器会发送上一次提交的请求.由于上一次提交的请求方式为post,刷新浏览器就会重新发送这个post请求,造成表单重复提交. 解决办法: 将请求当 ...

  3. 【Azure 机器人】微软Azure Bot 编辑器系列(6) : 添加LUIS,理解自然语言 (The Bot Framework Composer tutorials)

    欢迎来到微软机器人编辑器使用教程,从这里开始,创建一个简单的机器人. 在该系列文章中,每一篇都将通过添加更多的功能来构建机器人.当完成教程中的全部内容后,你将成功的创建一个天气机器人(Weather ...

  4. MySQL 数据库设计的“奥秘”

    2 MySQL 数据库设计的"奥秘" [主题]逻辑设计:数据类型与 Schema 所谓"万丈高楼平地起",一个稳固的建筑离不开扎实的基础.同样,良好的的「逻辑设 ...

  5. Java中对象调用方法的顺序

    Java虚拟机会预先为加载到内存中的每个类维护一个方法表(Method Table),其中列出了所有类中所有方法的签名. 现在有2个类A和B,其中,B是A的子类,和一个B类型的对象x,当调用x.f(a ...

  6. jQuery获取标签信息

    <!DOCTYPE html><html><head>    <meta charset="utf-8"/>    <titl ...

  7. 乘风破浪,.Net Core遇见MAUI(.NET Multi-platform App UI),进击现代化跨设备应用框架

    什么是MAUI https://github.com/dotnet/maui .NET Multi-platform App UI (MAUI) 的前身是Xamarin.Forms(适用于Androi ...

  8. 10、ssh中scp、sftp程序详解

    每次都是全量拷贝,rsync是增量拷贝 10.1.scp的基本用法: -r:拷贝目录; -p: 保持属性: -l:限速设置; scp -P52113 /etc/hosts lc@172.16.1.41 ...

  9. JS 获取JSON数据的属性

    var tballdata= [{ 'tjqd': '', 'A1': '', 'A2': '', 'A3': '', 'A4': '' };] if (typeof tballdata[0] == ...

  10. AcWing 105. 七夕祭

    七夕节因牛郎织女的传说而被扣上了「情人节」的帽子. 于是TYVJ今年举办了一次线下七夕祭. Vani同学今年成功邀请到了cl同学陪他来共度七夕,于是他们决定去TYVJ七夕祭游玩. TYVJ七夕祭和11 ...