0%

    本文首发于cartoon的博客
    转载请注明出处:https://cartoonyu.github.io/cartoon-blog/post/java/for与while时间的对比

    相关文章:JAVA遍历机制的性能的比较

前言

索引随机访问数组相信是很常见的操作.

但是昨天在做leetcode的Reverse String时,发现了很奇怪的现象,具体如下图

当时我也觉得不可思议,怎么快了那么多,所以今天复盘一下。

正文

注:这篇文章只涉及原始数组的索引遍历,不涉及包装数据结构以及foreach

测试代码
  • for
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private static void textFor(){
int[] data=new int[1000];
int i=0;
for(;i<1000;i++){
data[i]=i;
}
i=0;
long start=System.currentTimeMillis();
for(;i<1000;i++){
System.out.print(data[i]+" ");
}
long end=System.currentTimeMillis();
System.out.println();
System.out.println("for use:"+(end-start)+"ms");
}
  • while
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private static void textWhile(){
int[] data=new int[1000];
int i=0;
for(;i<1000;i++){
data[i]=i;
}
i=0;
long start=System.currentTimeMillis();
while(i<1000){
System.out.print(data[i++]+" ");
}
long end=System.currentTimeMillis();
System.out.println();
System.out.println("while use:"+(end-start)+"ms");
}
结果
1
2
3
4
5
6
7
8
9
10
11
for use:35ms
while use:15ms

for use:14ms
while use:6ms

for use:14ms
while use:8ms

for use:20ms
while use:5ms

所用时间可能不一样,但是大概比例应该跟我的差不多

有点意外的是,while比for竟然要少一倍(大概)的时间,颠覆了我之前的认知。

结果分析

虽然我没有debug代码,但是我猜测是循环执行语句的多少差别。

for中,执行顺序是

  • 判断循环变量是否越界
  • 执行打印语句
  • 循环变量自增

while中,执行顺序是

  • 判断循环变量是否越界
  • 执行打印语句,循环变量自增

与for相比,while所执行的语句量少掉1/3,所以我觉得这就是原因。(如果有更好的原因可以评论或者发起Issue)

后话

生命不息,技术不止。

很多时候我也为了代码量的减少不理会运行时间的差异,这次吸收教训,之后在实际开发会更加注意时间。

&nbsp;&nbsp;&nbsp;&nbsp;本文首发于cartoon的博客

&nbsp;&nbsp;&nbsp;&nbsp;转载请注明出处:https://cartoonyu.github.io/cartoon-blog/post/java/object%E7%9A%84%E6%88%90%E5%91%98%E6%96%B9%E6%B3%95%E4%BB%A5%E5%8F%8A%E4%BD%9C%E7%94%A8/

前言

对的。这次也是面试题,又是有点懵逼的一道题,记得当时只答出了wait跟notify。。。

正文

学java的都知道,Object是所有类的父类,但是相信很多人都忽略掉Object中的成员方法(包括我)。

翻过官方文档后,发现其实Object类成员方法可以总结为以下几类

&nbsp; 方法名 作用 注意
多线程操作 wait 线程等待,线程进入阻塞状态 /
notify/notifyAll 唤醒线程,线程回到就绪状态 /
垃圾回收 finalize 通知垃圾收集器回收对象 只是提醒,回收时间仍由垃圾收集器决定
对象克隆 clone 克隆对象 protected方法,不能被直接调用,若想实现克隆通过实现Cloneable重写clone方法实现
对象比较 hashCode/equals 判断对象时候相等 hashCode相等,equals不一定相等;equals相等,hashCode一定相等
获取对象信息 getClass 获取对象所属类 在反射中比较常用

后话

其实Object类的很多方法都很实用,多线程同步,对象比较等等,但是平时自己比较少关注,可能是我菜鸡吧。

希望看到文章的你们能有所收获,也希望我以后被问到这个不会再懵逼。

&nbsp;&nbsp;&nbsp;&nbsp;本文首发于cartoon的博客
&nbsp;&nbsp;&nbsp;&nbsp;转载请注明出处:https://cartoonyu.github.io/cartoon-blog/post/java/java实现克隆的方法

前言

这也是昨天的面试题。

当时只说了深拷贝以及浅拷贝,面试官问了两遍还有吗,我很肯定的说就这两种了,面试结束之后查了一下,啪啪打脸。

正文

JAVA实现克隆有两种形式

  • 浅克隆
  • 深克隆
浅克隆与深克隆的区别

JAVA将数据类型分为基本数据类型以及引用数据类型,我认为浅克隆与深克隆的区别主要在于对引用类型的成员属性的操作。深度克隆应该递归克隆引用类型的成员属性。

浅克隆实现
  • 实现Cloneable接口
  • 重写clone方法,调用父类的clone方法

代码

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
public class Text implements Cloneable{

private int age;

private Name name;

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public Name getName() {
return name;
}

public void setName(Name name) {
this.name = name;
}

@Override
protected Object clone(){
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}

class Name{
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

public class Main {

public static void main(String[] args){
Name name1=new Name();
name1.setName("name1");
Text t1=new Text();
t1.setAge(12);
t1.setName(name1);
Text t2=(Text) t1.clone();
System.out.println(t2.getName().getName());
name1.setName("name2");
System.out.println(t2.getName().getName());

}

}

输出

1
2
name1
name2

结果分析

因为只是直接调用父类的clone方法,没有对成员属性进行处理,所以在修改t1属性name的值时,t2属性name的值也会随之改变。

优点

简单易实现

缺点

无法真正克隆对象

深克隆实现
通过递归克隆实现

代码

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
public class Text implements Cloneable{

private int age;

private Name name;

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public Name getName() {
return name;
}

public void setName(Name name) {
this.name = name;
}

@Override
protected Object clone(){
Text text=null;
try {
text=(Text) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
text.setName((Name) text.getName().clone());
return text;
}
}

class Name implements Cloneable{
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
protected Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}

输出

1
2
name1
name1
通过序列化实现

代码

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
public class Text implements Serializable{

private static final long serialVersionUID = 8723901148964L;

private int age;

private Name name;

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public Name getName() {
return name;
}

public void setName(Name name) {
this.name = name;
}

public Object myClone(){
Text text=null;
ByteArrayOutputStream bos=new ByteArrayOutputStream();
try {
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois=new ObjectInputStream(bis);
text=(Text)ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return text;
}
}

class Name implements Serializable {

private static final long serialVersionUID = 872390113109L;

private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public String toString() {
return name;
}
}

输出

1
2
name1
name1

结果分析

采用深克隆能有效隔离源对象与克隆对象的联系。

从实现过程来说,递归克隆存在克隆过程多且复杂的缺点,所以建议采用序列化的方式进行

深克隆。

总结

JAVA对象克隆共有两种形式,三种方法

  • 浅克隆
    • 调用clone方法
  • 深克隆
    • 递归调用clone方法
    • 序列化对象

三种方法之间互有优缺点,具体采用要根据实际情况。

&nbsp;&nbsp;&nbsp;&nbsp;本文首发于cartoon的博客
&nbsp;&nbsp;&nbsp;&nbsp;转载请注明出处:https://cartoonyu.github.io/cartoon-blog/post/java/collections/

&nbsp;&nbsp;&nbsp;&nbsp;推荐一篇不错的博文:https://gitee.com/SnailClimb/JavaGuide/blob/master/docs/java/Basis/Arrays,CollectionsCommonMethods.md

前言

就在刚刚面试,被问到了Collections工具类有什么功能,我懵了,很少涉及到Collections这个工具类,只答了对集合元素的操作以及将线程不同步的集合类转换成线程同步,下定决心复盘一下。

正文

Collections工具类是java.util包中的一个工具类,主要功能是对集合及其元素进行操作。虽然被问的有点懵,在结束查看官方文档时发现其实可以分成以下几大部分

对集合本体的操作
线程同步集合的包装
  • 特征
    • 方法名为synchronizedXxx
  • 适用范围
    • List
    • Collection
    • Set
    • Map
  • 缺点
    • 每次读写都要加锁,锁的层级为对象,不利于多线程的同时操作
    • 在使用Iterator的遍历时修改元素ConcurrentModificationException
    • 建议使用java.util.concurrent的集合线程同步类
返回不可变集合
  • 特征

    • 方法名为emptyXxx(空集合)/singletonXxx(包含传入元素的集合)/unmodifiableXxx(包含传入集合元素的集合)
  • 适用范围

    • emptyXxx

      • Set
      • List
      • Map
      • Iterator
      • Enumeration
    • singletonXxx

      • Set
      • List
      • Map
    • unmodifiableXxx

      • Map

      • List

      • Set

返回指定集的动态类型安全视图
  • 特征
    • 方法名为checkedXxx
  • 适用范围
    • List
    • Map
    • Queue
    • Set
    • Collection
集合间的转换
  • 特征

    • asLifoQueue(将传入的Deque转换成Queue)

    • list(将传入的Enumeration转换成ArrayList)

    • newSetFromMap(根据传入的空Map返回Set)

    • nCopies(根据传入的n返回含n个副本的List)

集合内元素的操作
添加元素到集合中
  • 特征
    • addAll
    • copy(将源集合元素复制到目标集合中)
  • 适用范围
    • addAll
      • Collection
    • copy
      • List
查找元素
  • 特征
    • binarySearch(二分查找特定元素)
    • frequency(查找元素出现次数)
    • indexOfSubList(返回目标list在源list的开始位置)
    • subIndexOfSubList(返回目标list在源list的结束位置)
    • shuffle(返回随机索引元素)
  • 适用范围
    • binarySearch
      • List
    • frequency
      • Collection
    • shuffle
      • List
替换
  • 特征
    • fill(替换集合所有元素)
    • replaceAll(替换特定的值)
  • 适用范围
    • fill
      • List
    • replaceAll
      • List
改变元素位置
  • 特征
    • sort(排序)
    • swap
    • rotate(反转)
    • reverse
  • 适用范围
    • List
对比元素
  • 特征
    • min/max(寻找最大/小元素)
    • disJoint(判断两个集合元素是否全不同)
  • 适用范围
    • Collection

总结

Collections工具类能对各接口以及实现类实现多种操作

  • 集合类级操作
    • 返回线程安全集合
    • 返回不可变集合
    • 返回安全视图
    • 集合间的转换
  • 涉及到内部元素的操作
    • 添加元素到集合中
    • 查找特定元素
    • 替换元素
    • 改变元素位置
    • 元素间的比较

虽然有些方法不如其他包内的工具类好用,但是总体来说功能还是非常强大的。

这篇文章算是对官方文档的总结和归纳,也加以自己的思考,也是面试题之一,希望自己能在之后不会再吃这道题的亏。

&nbsp;&nbsp;&nbsp;&nbsp;本文首发于cartoon的博客

&nbsp;&nbsp;&nbsp;&nbsp;转载请注明出处:https://cartoonyu.github.io/cartoon-blog/post/java/java%E9%81%8D%E5%8E%86%E6%9C%BA%E5%88%B6%E7%9A%84%E6%80%A7%E8%83%BD%E6%AF%94%E8%BE%83/

缘由

&nbsp;&nbsp;&nbsp;&nbsp;近段时间在写leetcode的Lemonade Change时候,发现了for循环与forEach循环的耗时是不一致的,在提交记录上面差了一倍……

&nbsp;&nbsp;&nbsp;&nbsp;平常开发绝大部分业务逻辑的实现都需要遍历机制的帮忙,虽说也有注意到各数据结构操作的性能比较,但是忽视了遍历机制性能的差异。原本前两天就开始动手写,拖延症……

正文

&nbsp;&nbsp;&nbsp;&nbsp;现阶段我所知道JAVA遍历机制有三种

  • for循环

  • forEach循环

  • Iterator循环

&nbsp;&nbsp;&nbsp;&nbsp;JAVA数据结构千千万,但是大部分都是对基础数据结构的封装,比较HashMap依赖于Node数组,LinkedList底层是链表,ArrayList对数组的再封装……扯远了

&nbsp;&nbsp;&nbsp;&nbsp;总结来说,JAVA的基础数据结构,我觉得有两种

  • 数组
  • 链表

&nbsp;&nbsp;&nbsp;&nbsp;如果是加上Hash(Hash的操作与数组以及链表不太一致),就是三种

&nbsp;&nbsp;&nbsp;&nbsp;因为平常开发大部分都优先选择包装后的数据结构,所以下面我会使用

  • ArrayList(包装后的数组)
  • LinkedList(包装后的链表)
  • HashSet(包装后的Hash类型数组)

&nbsp;&nbsp;&nbsp;&nbsp;这三种数据结构在遍历机制不同的时候时间的差异

&nbsp;&nbsp;&nbsp;&nbsp;可能有人对我为什么不对比HashMap呢,因为JAVA设计中,是先实现了Map,再实现Set。如果你有阅读过源码就会发现:每个Set子类的实现中,都有一个序列化后的Map对应属性实现,而因为Hash的查找时间复杂度为O(1),得出key后查找value的时间大致是一致的,所以我不对比HashMap。

题外话

&nbsp;&nbsp;&nbsp;&nbsp;我在阅读《疯狂JAVA》读到:JAVA的设计者将Map的内部entry数组中的value设为null进而实现了Set。因为我是以源码以及官方文档为准,具体我不清楚正确与否,但是因为Hash中的key互不相同,Set中元素也互不相同,所以我认为这个观点是正确的。

&nbsp;&nbsp;&nbsp;&nbsp;为了测试的公平性,我会采取以下的限定

  • 每种数据结构的大小都设置三种量级
    • 10
    • 100
    • 1000
  • 元素都采用随机数生成
  • 遍历进行操作都为输出当前元素的值

&nbsp;&nbsp;&nbsp;&nbsp;注:时间开销受本地环境的影响,可能测量值会出现变化,但是总体上比例是正确的

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
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    public class TextArray {

    private static Random random;

    private static List<Integer> list1;

    private static List<Integer> list2;

    private static List<Integer> list3;

    public static void execute(){
    random=new Random();
    initArray();
    testForWith10Object();
    testForEachWith10Object();
    testIteratorWith10Object();
    testForWith100Object();
    testForEachWith100Object();
    testIteratorWith100Object();
    testForWith1000Object();
    testForEachWith1000Object();
    testIteratorWith1000Object();
    }

    private static void testForWith10Object(){
    printFor(list1);
    }

    private static void testForWith100Object(){
    printFor(list2);
    }

    private static void testForWith1000Object(){
    printFor(list3);
    }

    private static void testForEachWith10Object(){
    printForeach(list1);
    }

    private static void testForEachWith100Object(){
    printForeach(list2);
    }

    private static void testForEachWith1000Object(){
    printForeach(list3);
    }

    private static void testIteratorWith10Object() {
    printIterator(list1);
    }

    private static void testIteratorWith100Object() {
    printIterator(list2);
    }

    private static void testIteratorWith1000Object() {
    printIterator(list3);
    }

    private static void printFor(List<Integer> list){
    System.out.println();
    System.out.print("data:");
    long start=System.currentTimeMillis();
    for(int i=0,length=list.size();i<length;i++){
    System.out.print(list.get(i)+" ");
    }
    System.out.println();
    long end=System.currentTimeMillis();
    System.out.println("for for "+list.size()+":"+(end-start)+"ms");
    }

    private static void printForeach(List<Integer> list){
    System.out.println();
    System.out.print("data:");
    long start=System.currentTimeMillis();
    for(int temp:list){
    System.out.print(temp+" ");
    }
    System.out.println();
    long end=System.currentTimeMillis();
    System.out.println("foreach for "+list.size()+":"+(end-start)+"ms");
    }

    private static void printIterator(List<Integer> list){
    System.out.println();
    System.out.print("data:");
    Iterator<Integer> it=list.iterator();
    long start=System.currentTimeMillis();
    while(it.hasNext()){
    System.out.print(it.next()+" ");
    }
    System.out.println();
    long end=System.currentTimeMillis();
    System.out.println("iterator for "+list.size()+":"+(end-start)+"ms");
    }

    private static void initArray(){
    list1=new ArrayList<>();
    list2=new ArrayList<>();
    list3=new ArrayList<>();
    for(int i=0;i<10;i++){
    list1.add(random.nextInt());
    }
    for(int i=0;i<100;i++){
    list2.add(random.nextInt());
    }
    for(int i=0;i<1000;i++){
    list3.add(random.nextInt());
    }
    }
    }
  • 输出(忽略对元素的输出)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    for for 10:1ms
    foreach for 10:0ms
    iterator for 10:2ms

    for for 100:5ms
    foreach for 100:4ms
    iterator for 100:12ms

    for for 1000:33ms
    foreach for 1000:7ms
    iterator for 1000:16ms
    10 100 1000
    for 1ms 5ms 33ms
    forEach 0ms 4ms 7ms
    Iterator 2ms 12ms 16ms
  • 结论

    &nbsp;&nbsp;&nbsp;&nbsp;for的性能最不稳定,foreach次之,Iterator最好

  • 使用建议

    1. 在数据量不明确的情况下(可能1w,10w或其他),建议使用Iterator进行遍历

    2. 在数据量明确且量级小的时候,优先使用foreach

    3. 需要使用索引时,使用递增变量的开销比for的要小

LinkedList的比较

  • 代码

    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
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    public class TextLinkedList {

    private static Random random;

    private static List<Integer> list1;

    private static List<Integer> list2;

    private static List<Integer> list3;

    public static void execute(){
    random=new Random();
    initList();
    testForWith10Object();
    testForEachWith10Object();
    testIteratorWith10Object();
    testForWith100Object();
    testForEachWith100Object();
    testIteratorWith100Object();
    testForWith1000Object();
    testForEachWith1000Object();
    testIteratorWith1000Object();
    }

    private static void testForWith10Object() {
    printFor(list1);
    }

    private static void testForEachWith10Object() {
    printForeach(list1);
    }

    private static void testIteratorWith10Object() {
    printIterator(list1);
    }

    private static void testForWith100Object() {
    printFor(list2);
    }

    private static void testForEachWith100Object() {
    printForeach(list2);
    }

    private static void testIteratorWith100Object() {
    printIterator(list2);
    }

    private static void testForWith1000Object() {
    printFor(list3);
    }

    private static void testForEachWith1000Object() {
    printForeach(list3);
    }

    private static void testIteratorWith1000Object() {
    printIterator(list3);
    }

    private static void printFor(List<Integer> list){
    System.out.println();
    System.out.print("data:");
    long start=System.currentTimeMillis();
    for(int i=0,size=list.size();i<size;i++){
    System.out.print(list.get(i));
    }
    System.out.println();
    long end=System.currentTimeMillis();
    System.out.println("for for "+list.size()+":"+(end-start)+"ms");
    }

    private static void printForeach(List<Integer> list){
    System.out.println();
    System.out.print("data:");
    long start=System.currentTimeMillis();
    for(int temp:list){
    System.out.print(temp+" ");
    }
    System.out.println();
    long end=System.currentTimeMillis();
    System.out.println("foreach for "+list.size()+":"+(end-start)+"ms");
    }

    private static void printIterator(List<Integer> list){
    System.out.println();
    System.out.print("data:");
    Iterator<Integer> it=list.iterator();
    long start=System.currentTimeMillis();
    while(it.hasNext()){
    System.out.print(it.next()+" ");
    }
    System.out.println();
    long end=System.currentTimeMillis();
    System.out.println("iterator for "+list.size()+":"+(end-start)+"ms");
    }


    private static void initList() {
    list1=new LinkedList<>();
    list2=new LinkedList<>();
    list3=new LinkedList<>();
    for(int i=0;i<10;i++){
    list1.add(random.nextInt());
    }
    for(int i=0;i<100;i++){
    list2.add(random.nextInt());
    }
    for(int i=0;i<1000;i++){
    list3.add(random.nextInt());
    }
    }
    }
  • 输出(忽略对元素的输出)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    for for 10:0ms
    foreach for 10:1ms
    iterator for 10:0ms

    for for 100:1ms
    foreach for 100:0ms
    iterator for 100:3ms

    for for 1000:23ms
    foreach for 1000:25ms
    iterator for 1000:4ms
    10 100 1000
    for 0ms 1ms 23ms
    forEach 1ms 0ms 25ms
    Iterator 0ms 3ms 4ms
  • 结论

    &nbsp;&nbsp;&nbsp;&nbsp;foreach的性能最不稳定,for次之,Iterator最好

  • 使用建议

    1. 尽量使用Iterator进行遍历

    2. 需要使用索引时,使用递增变量的开销比for的要小

HashSet的比较

&nbsp;&nbsp;&nbsp;&nbsp;注:因Hash遍历算法与其他类型不一致,所以取消了for循环的比较

  • 代码

    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
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    public class TextHash {

    private static Random random;

    private static Set<Integer> set1;

    private static Set<Integer> set2;

    private static Set<Integer> set3;

    public static void execute(){
    random=new Random();
    initHash();
    testIteratorWith10Object();
    testForEachWith10Object();
    testIteratorWith100Object();
    testForEachWith100Object();
    testIteratorWith1000Object();
    testForEachWith1000Object();
    }

    private static void testIteratorWith10Object() {
    printIterator(set1);
    }

    private static void testForEachWith10Object() {
    printForeach(set1);
    }

    private static void testIteratorWith100Object() {
    printIterator(set2);
    }

    private static void testForEachWith100Object() {
    printForeach(set2);
    }

    private static void testIteratorWith1000Object() {
    printIterator(set3);
    }

    private static void testForEachWith1000Object() {
    printForeach(set3);
    }

    private static void initHash() {
    set1=new HashSet<>();
    set2=new HashSet<>();
    set3=new HashSet<>();
    for(int i=0;i<10;i++){
    set1.add(random.nextInt());
    }
    for(int i=0;i<100;i++){
    set2.add(random.nextInt());
    }
    for(int i=0;i<1000;i++){
    set3.add(random.nextInt());
    }
    }

    private static void printIterator(Set<Integer> data){
    System.out.println();
    System.out.print("data:");
    long start=System.currentTimeMillis();
    Iterator<Integer> it=data.iterator();
    while (it.hasNext()){
    System.out.print(it.next()+" ");
    }
    System.out.println();
    long end=System.currentTimeMillis();
    System.out.println("iterator for "+data.size()+":"+(end-start)+"ms");
    }

    private static void printForeach(Set<Integer> data){
    System.out.println();
    System.out.print("data:");
    long start=System.currentTimeMillis();
    for(int temp:data){
    System.out.print(temp+" ");
    }
    System.out.println();
    long end=System.currentTimeMillis();
    System.out.println("foreach for "+data.size()+":"+(end-start)+"ms");
    }
    }
  • 输出(忽略对元素的输出)

    1
    2
    3
    4
    5
    6
    7
    8
    iterator for 10:0ms
    foreach for 10:0ms

    iterator for 100:6ms
    foreach for 100:0ms

    iterator for 1000:30ms
    foreach for 1000:9ms
    10 100 1000
    foreach 0ms 0ms 9ms
    Iterator 0ms 6ms 30ms
  • 结论

    &nbsp;&nbsp;&nbsp;&nbsp;foreach性能遥遥领先于Iterator

  • 使用建议

    &nbsp;&nbsp;&nbsp;&nbsp;以后就选foreach了,性能好,写起来也方便。

总结

  1. for循环性能在三者的对比中总体落于下风,而且开销递增幅度较大。以后即使在需要使用索引时我宁愿使用递增变量也不会使用for了。
  2. Iterator的性能在数组以及链表的表现都是最好的,应该是JAVA的设计者优化过了。在响应时间敏感的情况下(例如web响应),优先考虑。
  3. foreach的性能属于两者之间,写法简单,时间不敏感的情况下我会尽量选用。

&nbsp;&nbsp;&nbsp;&nbsp;以上就是我对常见数据结构遍历机制的一点比较,虽然只是很初步,但是从中我也学到了很多东西,希望你们也有所收获。

&nbsp;&nbsp;&nbsp;&nbsp;如果你喜欢本文章,可以收藏阅读,如果你对我的其他文章感兴趣,欢迎到我的博客查看。

&nbsp;&nbsp;&nbsp;&nbsp;本文首发于cartoon的博客

&nbsp;&nbsp;&nbsp;&nbsp;转载请注明出处:https://cartoonyu.github.io/cartoon-blog/post/assorted/4种极大提升学习效率的工具

&nbsp;&nbsp;近段时间发现身边很多人都在用TUDO List,但是我觉得TUDO List的效率还是不够高,所以就写一下提升学习效率的工具。

TUDO List

​ 因为这阵子真的很多东西做,持续的时间比较长。而且会有时候(大概率)忘掉做某几件事,所以学习效率偏低,直到遇到了TUDO List。

​ TUDO List,可以理解为一个任务清单,它包含了你一天所有要做的事情,而且过一段时间你会发现自己已经依赖上TUDO List管理你的日程。

​ 市面上TUDO List的APP很多,有小黄条,TickTick,Mircosoft To-Do,Google Keep(好像是叫这个名字),还有很多很多,但是我还是喜欢TickTick。

​ 下面是我TickTick的收集箱。

(嗯,我知道我还有很多要做)

​ 我觉得TickTick比其他TUDO List好的主要有三点

  • 几乎全平台的支持
    • 安卓端是离线app加上联网更新形式,断网都可以继续用
    • 桌面端通过chrome插件提供服务
    • 苹果家的设备就不清楚了(穷)
  • 在界面舒适度与易用性取得平衡
    • 小黄条的界面,我就是看了一眼简介的截图就没有下载的欲望
    • Mircosoft家的,我不知道界面怎么样,但是我体验过小娜,我觉得不会好用
    • Google家的,好的,根本不能正常用
  • 自带番茄时钟
    • 我觉得现代人都摆脱不了手机(包括我),想认真看书的时候总是想看手机。所以番茄时钟能有效监督学习
    • 自带统计视图,能随时看到当周完成的情况

​ 有利也有弊,弊是有些功能需要付费使用,一个月10来块。始终还是要恰饭的嘛,能理解能理解。付费功能包括TUDO Task的统计,番茄时钟的统计建议。

​ 我对免费的功能已经满足了,所以我不是付费用户,付费解锁功能戳不到我的需求点。

思维导图

​ 不知道有多少读者是在做纸质笔记的,我的话已经很久没有做纸质笔记了。那么平常我在看书的时候,是怎么做笔记的呢。

​ 思维导图,我第一次接触已经爱上它了。

​ 下面是我学习帅张的git相关书籍做的思维导图,虽然过了三个月了,但是我还是可以根据思维导图讲出原书的大概内容。

​ 因为我是比较怕写字的(包括打字),所以我现在只会用思维导图做笔记了。

​ 市面上思维导图的软件很多,有XMind(开源,全平台支持),百度脑图(只有web端),MindMaster(苹果专属),MindLine(国人开发,支持云同步)等等。

​ 我说说我在用的XMind吧,优点有很多

  • 模板简洁而且漂亮。即使免费版也内置很多模板,ZEN版本跟Pro版本会更多
  • 操作简单。桌面端新建子分支或者兄弟分支会使用到快捷键(我比较少用,但是上手了非常方便),移动端(我常用)主要操作底下四颗按钮完成日常功能
  • 支持导出多格式。免费版只支持导出图片以及pdf,PRO版支持更多,但是我觉得这方面日常使用免费版已经足够了。

​ 下面到缺点的部分了。

​ 其实在我看来缺点只有一个,就是不支持云同步,确实有点反人类。

​ 我记得上次我双清平板忘记备份思维导图了(因为平板上独占数据只有思维导图),结果除了之前上传到OneDrive的几个之外,全都没了。。。大概10个上面图片的规模的思维导图吧。。。。

​ 我断断续续找了一个月,找到一个MindLine符合我使用场景之余是支持云同步的,但是界面说实话还有待改进,所以我只能继续用XMind。

Markdown

​ 如果不习惯用思维导图但是又想摆脱纸张的限制,我也可以提供选择。

​ 可能有人已经注意到,这篇文章的风格有点怪怪的。确实,是有点朴素(打死都不承认是怪怪的),因为我是用markdown去写的,具体原因看第二条推文。

​ 效果不截图了,整篇文章都是效果,或者可以去我的博客逛一下,有时间我也会写搭建博客的教程放在博客上。

​ 首先先说一下,markdown是一种标记语言,不是什么软件。具体介绍可以点击查看

​ 在我看来,markdown浑身都是优点

  • 操作简单。只需要掌握特定语法就可以写出比word整洁很多的文章,而且可以完全摆脱鼠标(纯文字文章的话)

  • 花样很多。markdown原生支持html语言,就是如果你懂html,可以像写网页那样写文章

  • 可选编辑器很多。微软家的VSCode,GitHub家的Sublime(实际上也是微软家的),Typora(我在用的,支持实时渲染),有道云笔记,印象笔记等等

  • 将主要精力放在思考上。整体风格更加符合思考的习惯,而且摆脱了鼠标的使用,可以将主要精力放在思考上。

    缺点嘛,也有两个,但是都是面对新手的。

  • 上手复杂。因为需要特定语法,所以上手需要一个阵痛期,但是practice makes prefect。习惯了之后效率会飞涨。我觉得这个教程不错

  • 插入图片比较麻烦,markdown支持三种图片插入方式

    • (不推荐)插入相对目录下的图片,但是需要确保路径的正确
    • (还行,但是不推荐)插入转码后的图片。这个比较麻烦,首先需要将图片转换成Base64字符串,再将字符串复制到markdown底部(转码后的字符串会很长),再利用语法引用。
    • (推荐)引用图床图片。图床这个不解释了,可以将它理解为一个网络的图片仓库。图床主要有三种
      • 免费的公有图床。百度一搜会很多。不推荐,容易出现图片丢失
      • 免费的私人图床。我所知道的只有微博图床,但是操作上有点麻烦,还行,但是不推荐。
      • 收费的私人图床。简单来说就是自己用OSS搭一个图床,本地上传到云端OSS。七牛云(一定容量免费),阿里云(我在用),腾讯云。七牛云就算了,需要拍身份证。个人推荐阿里云(9块/年)。

云盘

​ 可能很多人bb我:云盘谁没有,某度云盘几个T。

​ 是的,某度云盘确实占领了天朝大部分的市场。但是我想说的是,免费的是最贵的。想想某度的广告,想想几十K的下载速度,我不想吐槽了。

​ 云盘为什么能提升效率呢。

​ 我个人需求是

  • 平板是Wifi版的,需要经常带出去,出去的地方大多没有网络或者网络很差
  • 平常数据种类多而且量级比较大,而且需要多设备同步
  • 需要在线或缓存中阅读pdf

​ 我日常使用情况

  • 电脑下载需要看的电子书,直接放在OneDrive文件夹上,Win10自动上传

  • 平板脱机缓存电子书,网络环境不允许时可以随时使用电子书

  • 在平板上做好XMind之后,上传到OneDrive,需要再继续时下载

​ 所以我OneDrive是这样子的。

​ 通过OneDrive,我的数据能随时同步而且不丢失(再次为我丢失的思维导图伤心。。。)

​ 市面上有很多很多云盘。有我们能用到的,也有我们用不到的:OneDrive,某度云盘,亚马逊,iCloud,Google Drive,坚果云等等。

​ 苹果党,iCloud;

​ Win10党,OneDrive(虽然免费空间只有5G,但是好用,我愿意付费);

​ 有特殊手段的安卓党,Google Drive;

​ 数据量大且不常使用,某度还是可以选择一下的;

​ 以上几款软件都是开源或者免费+付费形式的。免费的部分基本上够用了,付费也在承受范围之内(真希望XMind出一个云同步的功能,我绝对付费)。

题外话:

​ 这段时间在混帅张的星球,受到了很多启发。关于软件跟知识是否付费的争论,我是站在付费的那一边。

​ 曾经在星球上面看到一个观点:免费是最贵的。软件都是程序猿一块块砖搬出来的,也是有成本的。免费使用,意味着在某些方面软件开发方是赚钱的,广告,流量,会员等等。

​ 自己本身也是学编程的,明白写程序是很辛苦的。免费产品我是不排斥的,没人嫌钱多。付费产品?我也愿意付费的,前提是有足够理由让我付费以及我能承担起这个费用。

​ 盗版我是不提倡的,但是有时候超出我的承受范围,我会选择某宝。而且盗版可能有一部分功能缺失掉了,恰好缺失的功能能极大提高你的效率。

​ 再次希望以上几款软件能对你们有帮助!

&nbsp;&nbsp;&nbsp;&nbsp;本文首发于cartoon的博客

&nbsp;&nbsp;&nbsp;&nbsp;转载请注明出处:https://cartoonyu.github.io/cartoon-blog

当修改gitnore文件后,常常出现文件不生效的情况,是因为之前的修改已经提交到暂存区上了。
解决方法

1
2
3
git add .     //防止已有修改还没到暂存区的情况
git rm -r --cached . //清除暂存区记录
git add . //提交修改记录到暂存区中

执行到第三步即能使gitnore文件生效,后续操作会按照gitnore规则执行

&nbsp;&nbsp;&nbsp;&nbsp;本文首发于cartoon的博客

&nbsp;&nbsp;&nbsp;&nbsp;转载请注明出处:https://cartoonyu.github.io/cartoon-blog

今天在做leetcode的时候,遇到了运算符的不同而导致结果不一致的问题。记录一下提醒自己

中文名称与英文名称

&:按位与(Bitwise and)
&&:逻辑与(logical and)
|:按位或(Bitwise or)
||:逻辑或(logical or)

区别

若第一个条件就可以决定表达式的值,逻辑运算符不会继续检查后续条件,而位运算符则会全部检查。

&nbsp;&nbsp;&nbsp;&nbsp;本文首发于cartoon的博客

&nbsp;&nbsp;&nbsp;&nbsp;转载请注明出处:https://cartoonyu.github.io/cartoon-blog

&nbsp;&nbsp;&nbsp;&nbsp;这段时间把疯狂JAVA再看了一遍,发现Stack,ArrayDeque,LinkedList都可以作为栈使用,所以就稍微从性能以及实现的细节对比这三者的区别。

类继承树

区别

底层数据存储方式

&nbsp; 存储方式
Stack 长度为10的数组
ArrayDeque 长度为16的数组
LinkedList 链表

方法参照表

Stack ArrayDeque LinkedList
push(e) addFirst(e)/offerFirst(e) addFirst(e)/offerFirst(e)
pop() removeFirst()/pollFirst() removeFirst()/pollFirst()
peek() getFirst()/peekFirst() getFirst()/peekFirst()

线程安全

&nbsp; 线程安全
Stack 线程同步
ArrayDeque 线程不同步
LinkedList 线程不同步

性能选项

&nbsp;&nbsp;&nbsp;通常情况下,不推荐使用Vector以及其子类Stack

1.需要线程同步

&nbsp;&nbsp;&nbsp;&nbsp;使用Collections工具类中synchronizedXxx()将线程不同步的ArrayDeque以及LinkedList转换成线程同步。

2.频繁的插入、删除操作:LinkedList

3.频繁的随机访问操作:ArrayDeque

4.未知的初始数据量:LinkedList

&nbsp;&nbsp;&nbsp;&nbsp;本文首发于cartoon的博客

&nbsp;&nbsp;&nbsp;&nbsp;转载请注明出处:https://cartoonyu.github.io/cartoon-blog

&nbsp;&nbsp;&nbsp;&nbsp;近段时间把自己电脑(win)、虚拟机(Ubuntu)以及阿里云(ubuntu)都重置了一遍,三个地方都有用到JDK,不想之后找教程找的那么麻烦。所以就自己总结一遍,一次性把轮子造好。

  • 环境

    1
    2
    3
    Win10 1803 Home
    Ubuntu 16.04.3
    Ubuntu 18.04.1

    &nbsp;&nbsp;&nbsp;&nbsp;其中服务器与虚拟机配置方法一致,只是目录不同,就归成Ubuntu一类好了。

  • Windows环境下安装

  1. 下载JDK的安装程序

    1
    https://www.oracle.com/technetwork/java/javase/downloads/jdk10-downloads-4416644.html
  2. 按照步骤安装

  3. 配置环境变量

&nbsp;&nbsp;&nbsp;&nbsp;此电脑–属性–编辑系统设置–环境变量

  • 新建系统变量JAVA_HOME,并在变量值选择Java的安装目录

  • 新建变量CLASSPATH,写入图中的变量值

  • 选择变量Path–编辑文本–在最后追加图中的值

  • 验证安装是否成功

&nbsp;&nbsp;&nbsp;&nbsp;cmd下输入java -version

&nbsp;&nbsp;&nbsp;&nbsp;出现图中的输出语句即为成功

  • Ubuntu环境下安装
  1. 下载JDK的压缩包

    1
    https://www.oracle.com/technetwork/java/javase/downloads/jdk10-downloads-4416644.html

    &nbsp;&nbsp;&nbsp;&nbsp;勾选同意协议后选择.tar.gz结尾的选项

  2. 解压到指定文件夹

    1
    tar -zxvf packageName.tar.gz  //packageName为jdk压缩包包名

    &nbsp;&nbsp;&nbsp;&nbsp;在服务器中我是直接通过xftp的GUI进行移动,虚拟机中受限于权限,我在终端上用root打开文件管理器移动的。

  3. 配置环境变量

    • 打开etc目录下的profile或者bashrc

      1
      2
      vim /etc/profile
      vim ~/.bashrc

      &nbsp;&nbsp;&nbsp;&nbsp;vim的安装以及基本命令可以戳此查看

    • 在末尾追加以下信息并保存

      1
      2
      3
      4
      export JAVA_HOME=/root/usr/jdk
      export JRE_HOME=/root/usr/jre
      export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
      export PATH=${JAVA_HOME}/bin:$PATH
    • 刷新配置文件

      1
      2
      source /etc/profile
      source ~/.bashrc
  4. 验证安装是否成功

    &nbsp;&nbsp;&nbsp;&nbsp;终端下输入java -version