先看源码:

首先看构造器,构造器有三种,一种直接给定初始长度的,如下代码

public ArrayList(int initialCapacity) {
if (initialCapacity > 0) { //判断长度是否大于0 如果大于0 则直接将长度给他
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) { //如果等于0,定义一个空数组实例
this.elementData = EMPTY_ELEMENTDATA;
} else { //否则就抛出异常 为负数
throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
}
}

第二种构造函数是不给定长度的,如下代码

public ArrayList() {  //直接给定一个空数组实例
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

第三种构造函数,直接包含指定元素的构造器,放一个集合,如下

public ArrayList(Collection<? extends E> c) {
elementData = c.toArray(); //集合直接转换为数组
if ((size = elementData.length) != 0) { //在数组长度不为0的情况下
// c.toArray might (incorrectly) not return Object[] (see 6260652) 在c.toArray之后返回的可能不是一个数组是需要进行下一步操作
if (elementData.getClass() != Object[].class)
          //通过copyOf将其转换为数组
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.如果为0只直接替换一个空数组
this.elementData = EMPTY_ELEMENTDATA;
}
}

然后就是开始看添加了。

public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!! 标红的方式是要确定定义数组的容量是否足够,接下来有分析
elementData[size++] = e; //size是定义的全局变量 没进行一次赋值 size++之后会改变哟,每增加一个值的时候就加1,第一次赋值是size是0,赋值完成后加1,第二次size就是1了。
return true;
}
private void ensureCapacityInternal(int minCapacity) {  //这个minCapacity代表上边标红方法中的size+1,这个需要记住哟
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); //接下来先介绍红色方法,介绍完后在介绍绿色方法。
}

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};  //这个就是一个空数组实例
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //判断如果是一个空数组实例的话 就拿数组默认长度值(10)来和size+1的值作比较,谁大取谁
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity; //不为空则直接返回size+1的值
}

接下来介绍绿色方法了。

private void ensureExplicitCapacity(int minCapacity) {
modCount++; //修改次数,具体用途还不清楚,下边有解释
// overflow-conscious code
if (minCapacity - elementData.length > 0) //判断size+1是否大于数组缓冲区的长度,如果大于则进行扩容了
grow(minCapacity); //grow这个方法就要开始扩容了
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; //首先把最初的数组长度暂存
int newCapacity = oldCapacity + (oldCapacity >> 1); //最初的数组长度正常一半的长度赋给newCapacity
if (newCapacity - minCapacity < 0) //再次进行判断 如果长度还是不够
newCapacity = minCapacity; //那么就直接将长度给size+1这个值
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); //在确定新数组长度之后就需要将原来已存在数组复制到新数组中,从而保证扩容后不会丢失原来的数据
}

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;  //这个值就是数组默认定义的最大值,下边有详细解释
private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? //检查是否超出数组最大值,如果超出了就将最大值给他,没有超出则给减去8之后的最大值。
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}

以上就是添加方法了。带参数的其他添加跟这个类似,不过添加了下标验证。如下

public void add(int index, E element) {
rangeCheckForAdd(index); //验证数组下标是否越界 ensureCapacityInternal(size + 1); // Increments modCount!! //检查是否需要扩容
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}

addAll同上,先判断是否扩容以及扩容的大小,然后进行arraycopy

public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount 先进行扩容
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}

获取元素:

public E get(int index) {
rangeCheck(index); //先检查下标是否在数组下标范围内,然后就取值 return elementData(index);
}
private void rangeCheck(int index) {  //下标超出 报异常
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

删除元素:

public E remove(int index) {
rangeCheck(index); //检查下标 modCount++;
E oldValue = elementData(index); //先将原来的数组保存 int numMoved = size - index - 1; //需要移动的值
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work return oldValue;
}

以下是自己实现的代码,比较粗糙

package com.interact.test.webService;

import java.util.Arrays;

public class ArrayListTest {

    Object [] arr = new Object[default_size];
private static int size = 0;
private static int default_size = 10; public int getSize(){
return size;
} //获取元素
public Object getIndex(int index){
//需要校验index的值,是否大于0并且小于最大长度
if(index >= 0 && index < arr.length){
return arr[index];
}else{
return "数组下标越界";
}
} //添加元素
public void add(Object o){
if(size >= default_size){ //默认长度不够的时候 进行扩容
default_size += default_size/2; //扩容到原来长度的1.5倍,就是就是加上原来长度的一半
}
arr = Arrays.copyOf(arr,default_size); //扩容之后通过底层的复制数组的方法,将之前保存的数据放到扩容之后的数组中
arr[size++] = o;
} public void remove(int index){
Object [] temp = new Object[size-1];
if(index >= 0 && index <= size){ //先进行校验
for (int i = 0; i < index; i++) {
temp[i] = arr[i];
}
for (int i = index + 1; i < size ; i++) {
temp[i-1] = arr[i];
}
}else{
System.out.println("下标越界");
}
arr = Arrays.copyOf(temp,size-1);
size--;
} public static void main(String [] args){
ArrayListTest arrayListTest = new ArrayListTest();
for (int i = 0; i < 16; i++) {
arrayListTest.add(i);
}
arrayListTest.remove(3);
for (int i = 0; i < arrayListTest.getSize(); i++) {
System.out.println(arrayListTest.getIndex(i));
}
}
}

native关键字的作用

告诉编译器调用的方法是外部定义的,一般是指C写的底层方法。主要意思是java调用java以外的方法的标示。

毫秒以下的单位以及进度

微秒,纳秒,皮秒跟毫秒一样进度为1000,1000毫秒=1秒,1000微秒=1毫秒,1000皮秒=1微秒。

OutOfMemoryError

内存溢出,给定长度满足不了实际长度时就会导致内存溢出。

为什么有时候一下异常throw出去了,但是却没有在函数头部声明

异常分两种,Checked异常和Runtime异常,在使用checked异常时需要在函数头部声明,而Runtime异常则不需要,主要当发生非运行时异常你就需要try catch进行捕获,告诉你哪里出问题了,从而进行修改。而运行时异常,则是在运行过程中才可能出现的异常,编辑器无法帮助你定位,所以即使你声明throw,然后再由调用者try catch捕获也没有用,所以就不再需要在函数头部就行声明。

最大数组大小定义为Integer.MAX_VALUE - 8,减去8的原因

因为要存储2^31 = 2,147,483,648这些长度的数组需要8bytes,所以给定最大长度为Integer.MAX_VALUE - 8。

java中的>> <<的作用

位运算符,转换为二进制,左位移运算符,右位移运算符,三个>>>的跟两个的一样。

java的访问修饰符

public      同类同包不同包的子类不同包的非子类可以使用

protectd 同类同包不同包的子类可以使用

default   同类同包中可以使用

private   只有同类中可以使用

modCount

修改次数,找了找资料,理解之后的大概意思就是,ArrayList不是线程安全的,使用迭代器时,其他线程可能会同时对其进行修改,这时候就会报异常,ConcurrentModificationException,也就是所谓fail-fast策略。而这个策略实现的方式就是,modCount每次修改就会加1,而每次初始化就将这个值暂存起来,放到expectedModCount这个变量中,在迭代过程中会进行比较,如果一样则没问题,不相同则报异常。注意到 modCount 声明为 volatile,保证线程之间修改的可见性。

volatile

编译器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份,并且防止拿到的数据是编译器优化之后的代码。其实就是拿到最真实实时的数据。

泛型的实现原理

主要是类型擦除,在编译成字节码文件时,字节码文件是不包含泛型中的类型信息的,会将类型擦除掉。

还需要理解,参考链接:https://blog.csdn.net/aoxida/article/details/50774459

linkList的实现(已完善)

package com.interact.test.webService;

public class LinkListTest {

    private static int size = 0;
private static Node first;
private static Node last;
private static Node[] objects = new Node[100]; //用来存储数据 //定义一个静态内部类
private static class Node {
private String current;
private Node next;
private Node prev; Node(String current, Node next, Node prev) {
this.current = current;
this.next = next;
this.prev = prev;
}
} public void add(Object o) {
Node temp = last;
Node newNode = new Node(o.toString(), null, temp);
last = newNode;
if (temp == null) {
first = newNode;
} else {
temp.next = newNode;
}
objects[size++] = newNode; //用数组存数据是否有问题
} public Object getIndex(int index) { //可能有问题
if(index < (size >> 1)){ //如果index小于总长度的一半 则从第一个开始找,从前往后推
Node node = first;
for (int i = 0; i < index; i++) {
node = objects[i].next;
}
return node.current;
}else{ //如果index大于总长度的一半 则从最后一个开始找,从后往前推
Node node = last;
for (int i = size-1; i > index; i--) { //这里需要注意 当寻找的下标大于总长度一半的时候 不再是从0开始,而且i开始逐渐减去1
node = objects[i].prev;
}
return node.current;
}
} public void linkFirst(String element){ //将该值作为第一个节点
Node node = first; //先将当前第一个值暂时保存
Node newNode = new Node(element,node,null); //通过Node构造器获取一个新的节点
first = newNode; //然后将新的节点给全局变量第一个节点first
if(node == null){ //如果node是空的,也就说明当前first是空的,说明当前linkList没有值
last = newNode; //此时第一个节点是刚创建的新节点,最后一个也是这个新节点
}else{
node.prev = newNode; //如果不是空的,那么当前第一个节点的前一个节点应该是null,此时就需要将这个新的节点设置为之前第一个节点的前节点
}
size++;
} public void remove(Object o) {
for (int i = 0; i < size; i++) {
if (objects[i].current.equals(o)) {
final Node prev = objects[i].prev; //前一个节点
final Node next = objects[i].next; prev.next = next; //将前一个节点的下一个节点设置成当前节点的下一个节点,从而达到连接后面节点的效果
next.prev = prev; //同样将要移除节点的前一个节点设置为移除节点的前一个节点。 objects[i].current = null; //进行垃圾回收
objects[i] = null;
size--; }
}
} public static void main(String[] args) {
LinkListTest listTest = new LinkListTest();
listTest.add("111");
listTest.add("222");
listTest.add("333");
listTest.add("444");
listTest.add("555");
listTest.add("666");
System.out.println(listTest.getIndex(1));
System.out.println(listTest.getIndex(4));
listTest.remove("222");
listTest.linkFirst("999");
System.out.println(listTest.getIndex(1));
System.out.println(listTest.getIndex(0));
} }

vue组件:主要是拆分代码,减少vue实例的代码量,方便ui的重用。

     vue创建组件的方式,Vue.component(组件名称,组件构造器),跟创建Vue对象相似,同样有data、methods、watch等,但是组件的data必须是一个函数,而且没有el获取根实例。

如下代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="vue.js" type="text/javascript"></script>
</head>
<body>
<div id="app">
<my-com></my-com>
</div>
<script type="text/javascript">
Vue.component("myCom",{
//data必须是一个函数
data :function(){
return{
count:0
}
},
template:"<button v-on:click='count++'>点击了{{count}}次</button>"
})
var vm = new Vue({
el:"#app"
})
</script>
</body>
</html>

还有通过extend的几种方式来实现的

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="vue.js"></script>
</head>
<body>
<div id="app">
<my-com></my-com>
<mycom2></mycom2>
<mycom3></mycom3>
<mycom4></mycom4>
</div>
<template id="temp">
<div >
<h1>第四种组件的实现方式</h1>
<h2>这种写法的好处是能够提示信息</h2>
</div>
</template>
<script>
var com1 = Vue.extend({
template:'<h1>组件的第一种实现方式</h1>'
})
Vue.component('myCom',com1)
Vue.component('mycom2',Vue.extend({
template:'<h2>组件的第二种实现方式</h2>'
}))
Vue.component('mycom3',{
template:'<h3>组件的第三种实现方式</h3>'
})
Vue.component('mycom4',{
template:"#temp"
})
var vm = new Vue({
el:"#app"
})
</script>
</body>
</html>

组件是通过prop来进行数据传递的。

工作之余第二篇(看源码自己实现ArrayList和LinkList)的更多相关文章

  1. Java中常用的七个阻塞队列第二篇DelayQueue源码介绍

    Java中常用的七个阻塞队列第二篇DelayQueue源码介绍 通过前面两篇文章,我们对队列有了了解及已经认识了常用阻塞队列中的三个了.本篇我们继续介绍剩下的几个队列. 本文主要内容:通过源码学习De ...

  2. [session篇]看源码学习session(一)

    假如你是使用过或学习过PHP,你一定觉得很简单.session只不过是$_SESSION就可以搞得,这还不简单只是对一个key-value就能工作了.我觉得可以大多数的phper都是这样的,这是语言本 ...

  3. JDK源码分析系列02---ArrayList和LinkList

    ArrayList和LinkList的源码分析 概要 ArrayList和LinkList是常用的存储结构,不看源码先分析字面意思,Array意思是数组,可知其底层是用数组实现的,Link意思是链接, ...

  4. 专治不会看源码的毛病--spring源码解析AOP篇

    昨天有个大牛说我啰嗦,眼光比较细碎,看不到重点.太他爷爷的有道理了!要说看人品,还是女孩子强一些.原来记得看到一个男孩子的抱怨,说怎么两人刚刚开始在一起,女孩子在心里就已经和他过完了一辈子.哥哥们,不 ...

  5. 支持JDK19虚拟线程的web框架之四:看源码,了解quarkus如何支持虚拟线程

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 前文链接 支持JDK19虚拟线程的web框架,之一:体 ...

  6. 大熊君大话NodeJS之 ------ Connect中间件第二季(源码分析)

    一,开篇分析 大家好,大熊君又回来了,今天这篇文章主要是对"Connect"中间件以及相关辅助中间件,做一个源码分析系列,我想上一篇文章大家也看了, 介绍了使用方式及用途,而这篇也 ...

  7. [Spark内核] 第32课:Spark Worker原理和源码剖析解密:Worker工作流程图、Worker启动Driver源码解密、Worker启动Executor源码解密等

    本課主題 Spark Worker 原理 Worker 启动 Driver 源码鉴赏 Worker 启动 Executor 源码鉴赏 Worker 与 Master 的交互关系 [引言部份:你希望读者 ...

  8. python3-开发进阶 django-rest framework 中的 版本操作(看源码解说)

    今天我们来说一说rest framework 中的 版本 操作的详解 首先我们先回顾一下 rest framework的流程: 请求进来走view ,然后view调用视图的dispath函数 为了演示 ...

  9. 没必要看源码。。把文档学通就已经牛逼了(我们大多还是在应用层,还达不到研究的程度。附class与examples大全链接)

    [学霸]深圳-鑫 2017/7/11 13:54:07只是学习怎么用QT的话,不用看源码.看帮助文档就很好要学习编码风格与思路,就看看源码 [学神]武汉-朝菌 2017/7/11 13:54:39没必 ...

随机推荐

  1. Docker and Kubernetes -- 监控(weave scope)

    docker常用的监控工具 weave scope 简介 Weave Scope是Docker和Kubernetes的可视化监控管理软件 Weave Scope 会自动生成容器之间的关系图,方便理解容 ...

  2. dedecms万能SQL标签使用方法大全

    注意:dede_archives这是系统默认的数据库表,如果你修改过表前缀dede_,请自行更改表名.在以下示例的标签中,有一部分只写出了SQL语句,具体的完整标签写法请参考:织梦SQL标签调用方法. ...

  3. jdk 安装过程配置环境变量 error 的解决过程

    jdk 安装过程配置环境变量 error 的解决过程 问题背景: 我在安装 jdk 过程中在JAVA_HOME和path中添加路径后, cmd 中输入java 和javac均出现错误,因为之前在 D ...

  4. 简谈图论重要性&&图论总结

    从外地学习回来,我对图论才有认识(以前就没接触过,非常尴尬),说实话,学好图论的重要性,就像学数学时在进行解析几何时,图极有可能是打开答案的最后秘钥,也就是数形结合,而懂的人永远明白,用图解决绝对比用 ...

  5. hdu5501 The Highest Mark

    Problem Description The SDOI in 2045 is far from what it was been 30 years ago. Each competition has ...

  6. 牛客练习赛70 A.重新排列 (,字符串思维)

    题意:有一个模板串,给你\(T\)个字符串,选取最短的子串,使其重新排列后包含模板串,求最短的子串的长度 题解:遍历字符串,记录每个字符出现的最后位置,每记录一个后再遍历子串,找到子串需要的所有的字符 ...

  7. PowerShell随笔2---初始命令

    PowerShell便捷之处 PowerShell中兼容运行cmd的命令,比如 ipconfig.ping命令等 PowerShell的命令更友好,可读性更强.比如停止一个服务 CMD命令:sc st ...

  8. 2.API的理解和使用

    标题 : 2.API的理解和使用 目录 : Redis 序号 : 2 ​ zset的成员是唯一的,但分数(score)却可以重复. ​ 有序集合的内部编码 1.ziplist(压缩列表):当有序集合的 ...

  9. C# 类 (8) - 抽象方法

    抽象 抽象方法 只能 定义在抽象类 里,并且抽象方法里没有具体的代码,像这种 为啥要定义一个空空如也的函数呢?这是为了用来约束 它的派生类 的行为, 这个例子,建立了一个数组,放了cat和dog,这两 ...

  10. 牛客多校第九场H Cutting Bamboos(主席树 区间比k小的个数)题解

    题意: 标记为\(1-n\)的竹子,\(q\)个询问,每次给出\(l,r,x,y\).要求为砍区间\(l,r\)的柱子,要求砍\(y\)次把所有竹子砍完,每次砍的时候选一个高度,把比他高的都砍下来,并 ...