工作之余第二篇(看源码自己实现ArrayList和LinkList)
先看源码:
首先看构造器,构造器有三种,一种直接给定初始长度的,如下代码
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)的更多相关文章
- Java中常用的七个阻塞队列第二篇DelayQueue源码介绍
Java中常用的七个阻塞队列第二篇DelayQueue源码介绍 通过前面两篇文章,我们对队列有了了解及已经认识了常用阻塞队列中的三个了.本篇我们继续介绍剩下的几个队列. 本文主要内容:通过源码学习De ...
- [session篇]看源码学习session(一)
假如你是使用过或学习过PHP,你一定觉得很简单.session只不过是$_SESSION就可以搞得,这还不简单只是对一个key-value就能工作了.我觉得可以大多数的phper都是这样的,这是语言本 ...
- JDK源码分析系列02---ArrayList和LinkList
ArrayList和LinkList的源码分析 概要 ArrayList和LinkList是常用的存储结构,不看源码先分析字面意思,Array意思是数组,可知其底层是用数组实现的,Link意思是链接, ...
- 专治不会看源码的毛病--spring源码解析AOP篇
昨天有个大牛说我啰嗦,眼光比较细碎,看不到重点.太他爷爷的有道理了!要说看人品,还是女孩子强一些.原来记得看到一个男孩子的抱怨,说怎么两人刚刚开始在一起,女孩子在心里就已经和他过完了一辈子.哥哥们,不 ...
- 支持JDK19虚拟线程的web框架之四:看源码,了解quarkus如何支持虚拟线程
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 前文链接 支持JDK19虚拟线程的web框架,之一:体 ...
- 大熊君大话NodeJS之 ------ Connect中间件第二季(源码分析)
一,开篇分析 大家好,大熊君又回来了,今天这篇文章主要是对"Connect"中间件以及相关辅助中间件,做一个源码分析系列,我想上一篇文章大家也看了, 介绍了使用方式及用途,而这篇也 ...
- [Spark内核] 第32课:Spark Worker原理和源码剖析解密:Worker工作流程图、Worker启动Driver源码解密、Worker启动Executor源码解密等
本課主題 Spark Worker 原理 Worker 启动 Driver 源码鉴赏 Worker 启动 Executor 源码鉴赏 Worker 与 Master 的交互关系 [引言部份:你希望读者 ...
- python3-开发进阶 django-rest framework 中的 版本操作(看源码解说)
今天我们来说一说rest framework 中的 版本 操作的详解 首先我们先回顾一下 rest framework的流程: 请求进来走view ,然后view调用视图的dispath函数 为了演示 ...
- 没必要看源码。。把文档学通就已经牛逼了(我们大多还是在应用层,还达不到研究的程度。附class与examples大全链接)
[学霸]深圳-鑫 2017/7/11 13:54:07只是学习怎么用QT的话,不用看源码.看帮助文档就很好要学习编码风格与思路,就看看源码 [学神]武汉-朝菌 2017/7/11 13:54:39没必 ...
随机推荐
- HTML字体
字体相关的样式 color 用来设置字体颜色 font-size 字体的大小 和font-size相关的单位 em 相当于当前元素的一个font-size rem 相当于根元素的一个font-size ...
- canal-adapter1.1.14最新版本安装的过程中出现的NullPointerException异常
记录一下我在安装 canal-adapter1.1.14最新版本安装的过程中出现的NullPointerException异常 以下是我的canal-adapter/logs文件夹内adapter.l ...
- c++11新特性实战(二):智能指针
c++11添加了新的智能指针,unique_ptr.shared_ptr和weak_ptr,同时也将auto_ptr置为废弃(deprecated). 但是在实际的使用过程中,很多人都会有这样的问题: ...
- XV6学习(10)锁
在包括XV6的绝大部分操作系统都是多个任务交错执行的.交错的一个原因是多核硬件:多核计算机的多个CPU核心独立执行计算,如XV6的RISC-V处理器.多个CPU核心共享物理内存,XV6利用这种共享来维 ...
- jdk 安装过程配置环境变量 error 的解决过程
jdk 安装过程配置环境变量 error 的解决过程 问题背景: 我在安装 jdk 过程中在JAVA_HOME和path中添加路径后, cmd 中输入java 和javac均出现错误,因为之前在 D ...
- 【noi 2.6_6046】数据包的调度机制(区间DP)
题意:给定一个队列延迟值为Di的任务,以任意顺序入栈和出栈,第K个出栈的延迟值为(K-1)*Di.问最小的延迟值. 解法:f[i][l]表示完成以第i个任务开始,长度为l,到第i+l-1个任务的最小延 ...
- Educational Codeforces Round 94 (Rated for Div. 2) A. String Similarity (构造水题)
题意:给你一个长度为\(2*n-1\)的字符串\(s\),让你构造一个长度为\(n\)的字符串,使得构造的字符串中有相同位置的字符等于\(s[1..n],s[2..n+1],...,s[n,2n-1] ...
- 数理统计8:点估计的有效性、一致最小方差无偏估计(UMVUE)、零无偏估计法
在之前的学习中,主要基于充分统计量给出点估计,并且注重于点估计的无偏性与相合性.然而,仅有这两个性质是不足的,无偏性只能保证统计量的均值与待估参数一致,却无法控制统计量可能偏离待估参数的程度:相合性只 ...
- k8s二进制部署 - 总结
镜像仓库: 安装软件:docker.docker-compose.harbor.nginx 1.下载cfssl.cfssljson.cfssl-certinfo,增加执行权限并放在PATH环境变量路径 ...
- Django分页APP_django-pure-pagination
一.App说明 该App用户Django的数据分页功能 二.安装 pip install django-pure-pagination 三.使用方法 (1)settings注册 INSTALLED_A ...