0%

声明:本文使用JDK1.8

先看下List在Collection中的框架图:

ArrayList源码分析

大家基本都知道ArrayList的底层是数组的数据结构,下面来看下它的随机访问、删除等的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
private static final int DEFAULT_CAPACITY = 10;//初始容量为10
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
private int size;
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//ArrayList扩容函数方法
private void ensureExplicitCapacity(int minCapacity) {
modCount++;

// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// 计算当前ArrayList大小
int oldCapacity = elementData.length;
//这里我们可以看出,ArrayList每次扩容是增加50%,oldCapacity >> 1是指往左移一位,也就是除以2
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);
}
//根据下标index获取元素值
public E get(int index) {
rangeCheck(index);//检查小标是否越界
return elementData(index);
}
//将index位置的值设为element,并返回原来的值
public E set(int index, E element) {
rangeCheck(index);

E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
//向数组中末尾增加一个元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//向指定位置index处增加element
public void add(int index, E element) {
rangeCheckForAdd(index);

ensureCapacityInternal(size + 1); // Increments modCount!!
//将index以及index之后的数据复制到index+1的位置往后,即从index开始向后挪了一位
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
//根据指定的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;
}

LinkedList源码分析

来看下LinkedList的部分源码,底层是基于双向链表的数据结构。
定义:

1
2
3
4
5
6
package java.util;public class LinkedList<E> extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
transient int size = 0;
transient Node<E> first;
transient Node<E> last;
}

方法:

Read more »

欢迎指正。

今天看到GET/POST/PUT,就想了解下这些的区别。HTTP方法有这几种:GET,POST,PUT和DELETE。

HTTP方法

GET

获取信息。GET请求必须是安全且幂等的,这意味着无论使用相同参数重复多少次,结果都是一样的。对于网上说的,GET请求有长度限制,其实,URL不存在参数上限的问题,HTTP协议规范没有对URL长度进行限制。对GET的URL长度限制的是浏览器,这里的限制是URL,而不是参数的限制。

POST

请求URL中的资源对提供的实体执行某些操作。POST通常用于创建或者更新实体。POST把提交的数据则放置在是HTTP包的包体中。

PUT

将实体存储在URL中。PUT更新现有的实体,或者新增指定的资源(如果id不存在,新增一个含id资源)。PUT请求是幂等的。幂等性是PUT期望与POST请求之间的主要区别。

DELETE

Read more »

声明:本文使用JDK1.8

在Java中,对于字符串的操作有这三种:String、StringBuilder、StringBuffer。这三者的效率是:StringBuilder > StringBuffer > String。

1
2
3
4
5
6
7
8
9
String a = "abc";
a = a + "d";
System.out.println(a);
StringBuffer buffer = new StringBuffer();
buffer.append("a");
System.out.println(buffer);
StringBuilder builder = new StringBuilder();
builder.append("b");
System.out.println(builder);

String

先来看下String的源码,如图所示:

从图中我们可以看出,String 是由 char 数组构成的,而且有 final 关键字修饰,这说明 String 类型的对象是不可以改变的。那么,平时我们使用“+”来拼接字符串是什么实现的?

如上面的代码,首先创建一个 String 对象 a,再把“abc”赋值给它,后面Java虚拟机又创建了一个 String 对象 a,然后再把原来的 a 的值和 “d” 加起来再赋值给新的 a,而原来的a 就会被Java虚拟机的垃圾回收机制(GC)给回收掉了,所以,a 实际上并没有被更改,也就是前面说的 String 对象一旦创建之后就不可更改了。从这里可以看出,对于频繁操作的字符串,不建议使用 String 类型,这将会是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢。

对于 String 类型对象的“+”操作,通过在 StringBuilder 的 append 方法上面打断点,可以发现对于String的操作,其实是使用了 StringBuilderappend 操作,这个不是线程安全。详细可以看下面关于 StringBuilder 的源码。

Read more »

方法

检查参数的有效性

绝大多数方法和构造器对于传递给它们的参数值都会有某些限制。例如,索引值必须是非负数,对象引用不能为null,等等,这些都是很常见的。对于参数的校验,可以使用 @NotNull

必要时进行保护性拷贝

谨慎设计方法签名

慎用重载

在Java中,参数类型或者个数不一样,对返回参数没有要求,叫做重载。

慎用可变参数

可变参数如下代码所示:

1
2
3
4
5
6
7
8
static int sum(int... args) {
int sum = 0;
for (int arg : args) {
sum += arg;
}

return sum;
}
Read more »

类和接口

使类和成员的可访问性最小化

1、尽可能地使每个类或者成员不被外界访问。换句话说,应该使用与你正在编写的软件的对应功能相一致的、尽可能最小的访问级别。例如,想在实体类的中,使用private定义属性值。

在公有类中使用访问方法而非公有域

使可变性最小化

不要提供任何会修改对象状态的方法(也称为 mutator)

保证类不会被扩展。

这样可以防止粗心或者恶意的子类假装对象的状态已经改变,从而破坏该类的不可变行为。为了防止子类化,一般做法是使这个类成为 final的,但是后面我们还会讨论到其他的做法。

使所有的域都是final的。

通过系统的强制方式,这可以清楚地表明你的意图。而且,如果一个指向新创建实例的引用在缺乏同步机制的情况下,从一个线程被传递到另一个线程,就必需确保正确的行为,正如内存模型( memory model)中所述。

Read more »

最近比较空,就抽时间看了《Efffectice Java》这本书,虽然这本书的第二版和现在的Java版本有点老了,但是看了一遍后,感觉,其中的一些思想,平时写代码的时候需要注意的地方,还是讲的不错的。

用静态工厂方法代替构造器

对于Map的new,在Java的后面的版本中,已经修改了,不需要如书上说的创建一个newMap的静态工厂类,可以如下进行创建一个新的Map:

1
Map<Integer,String> map = new HashMap<>();

遇到多个构造器参数时要考虑用构建器

重叠构造器模式

这种模式就是利用重载,不断调用构造函数,生成需要的bean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class MyClass{
//必要的参数
private final int one;
private final int two;
//可选的参数
private final int three;
private final int four;
public MyClass1(int one,int two){
this(one,two,0);
}
public MyClass(int one,int two,int three){
this(one,two,three,0);
}
public MyClass(int one,int two,int three,int four){
this.one=one;
this.two=two;
this.three=three;
this.four=four;
}
@Override
public String toString(){
return " one="+this.one+" two="+this.two+" three="+this.three+" four="+this.four;
}
}

从这代代码可以看出,这种写法有点复杂,个人觉得实际开发中,不会去使用

JavaB ean

Read more »

声明:本文使用JDK1.8

Java8提供了丰富的lambda的API。今天我们来讲讲关于其中的stream(流的操作)。对于List集合,有了stream的流操作,感觉如虎添翼。

生成一个List

1
2
3
4
5
6
7
String str = "1,2,3,4,10,11,9,66,222,12";
List<Integer> list = Stream.of(str.split(","))
.map(Integer::valueOf)
.filter(x-> !Objects.equals(x,3))
.sorted(Comparator.reverseOrder())
.limit(4)
.collect(Collectors.toList());

上面的代码Stream.of 为我们生成了一个List,但是我们需要的Integer,所以我们还需要使用map来转换类型。然后对于生成的列表,我们不想要其中的3,于是我们使用filter来过滤掉列表中的3。对于现在列表我们又想是倒序后的列表的前四条数据,于是我们在使用sorted来对列表来进行倒序后,再使用limit取前四条。顺便说一句,对于比较,推荐使用Objects.equals(a,b).
下面来简单的介绍下stream。

map

map:对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素
map方法是对列表里面的对象的转换,比如上面的map的功能是吧String转换成Integer。除了这样的转换,你也可以对对象进行你需要的操作。比如现在有一个对象ListDTO:

1
2
3
4
5
6
7
8
9
10
11
@Data
public class ListDTO{
public Integer id;
public String name;
public ListDTO() {
}
public ListDTO(Integer id, String name) {
this.id = id;
this.name = name;
}
}

现在这么一个List,你现在只想得到这个列表中ListDTO对象里面的id,现在你就可以这样操作:

1
List<Integer> idList = beans.stream().map(ListDTO::getId).collect(Collectors.toList());
Read more »

之前Mac上面使用Python都是使用系统自带的Python,哈哈。。。使用Mac的知道,Mac上面已经安装了Python2.7,这个是Mac系统需要的。现在Python有两个版本,2.x和3.x。Python2.7支持延迟时间是到2020年,2.7是2.x系列的最后一个版本,之后将不再更新了。虽然这两个版本很相似,只是语法上面有点差别。所有,如果现在学Python的话,还是下载Python3来学习吧。

Homebrew

在Mac上面安装一些软件,还是在Homebrew下面安装吧,像Git,mysql,node.js等,都可以在homebrew里面安装,这样安装卸载都方便,只需要brew install/uninstall <软件名字>

安装Xcode

安装Homebrew之前,需要安装Xcode,这个可以到APP Store里面去下载。

安装Homebrew

1.到Homebrew官网去下载
2.在命令终端里面下载

1
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

安装Python3

homebrew的一些基本命令:

Read more »

相比较于之前的版本之前的版本,增加了很大新特性,其中关于时间多了很多功能:LocalDateTime,LocalDate,LocalTime。

时间介绍

LocalDate是年月日;LocalTime是时分秒;LocalDateTime则是两者的合并,可以通过下面得到:

1
2
3
LocalTime localTime = LocalTime.now();
LocalDate localDate = LocalDate.now();
LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);

1
LocalDateTime localDateTime = LocalDateTime.now();

Java8对这几个时间提供了丰富的时间计算接口。
关于Date和String之间的转化,我这里就不多说了

String –> LocalDate/LocalTime/LocalDateTime

String类型转化新特性时间可以使用 DateTimeFormatter 功能。

1
2
DateTimeFormatter dateformatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate ld = LocalDate.parse("2017-08-01", dateformatter);
Read more »

最近想自己搭建一个个人博客,就去网上去看下有没有开源的代码可以拿来使用,在比较了网上一些开源的博客后,选择了Hexo。主要自己想在平时学习积累的时候,有个地方记录下自己的学习,嗯,还有一个和重要的的因素,那就是顺便装个B。

什么是 Hexo?

Hexo 是一个快速、简洁且高效的博客框架。Hexo 使用 Markdown(或其他渲染引擎)解析文章,在几秒内,即可利用靓丽的主题生成静态网页。

安装

安装

安装hexo之前先安装 Node.js 和 Git,之后使用npm下载hexo

1
$ npm install -g hexo-cli

建站

安装 Hexo 完成后,请执行下列命令,Hexo 将会在指定文件夹中新建所需要的文件。

1
2
3
$ hexo init <folder>
$ cd <folder>
$ npm install
Read more »