Vuejs template快速入门

一直想写一个Vuejs的系列的博客,苦于一直没有时间,今天的这个博客也是思考了很久,打算随便写点东西,今天说说Vuejs的模版的使用。

Vue模版的使用方法

  1. 直接用Html

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    <div id="container">
    <mytemp></mytemp>
    </div>

    <script src="vue.min.js"></script>
    <script>
    Vue.component('mytemp', {
    template: ' <h1>Hello I am Temple</h1>',

    })
    new Vue().$mount("#container");

    </script>

  2. 使用标签

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

    <div id="container">
    <mytemp></mytemp>
    </div>
    <script id="temp1" type="x-template">
    <h1>Hello I am Temple</h1>
    </script>
    <script src="vue.min.js"></script>
    <script>
    Vue.component('mytemp', {
    template: '#temp1'
    })
    new Vue().$mount("#container");

    </script>


  3. 使用template标签

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    <div id="container">
    <mytemp></mytemp>
    </div>
    <template id="temp1">
    <h1>Hello I am Temple</h1>
    </template>
    <script src="vue.min.js"></script>
    <script>
    Vue.component('mytemp', {
    template: '#temp1'
    })
    new Vue().$mount("#container");

    </script>

局部模版

上面我们使用的全局模版,这个模版可以在所有的vue对象中使用,如果我们仅仅想让在当前的vue中生效,就要用局部的模版注册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 <div id="container">
<mytemp></mytemp>
</div>
<template id="temp1">
<h1>Hello I am Temple</h1>
</template>
<script src="vue.min.js"></script>
<script>
new Vue({
components: {
'mytemp': {
template: '#temp1'
}
}
}).$mount("#container");

</script>

模版的传值


单项传递

对于我们的传值,我们可以看成父模版向子模版传值,其中vue是父对象,template是子对象,这里我们需要使用props属性,修改我们的额demo如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div id="container">
<mytemp v-bind:myname="name"></mytemp>
</div>
<template id="temp1">
<h1>Hello I am {{myname}} Temple</h1>
</template>
<script src="vue.min.js"></script>
<script>
new Vue({
data: {
name: "Vue"
},
components: {
'mytemp': {
template: '#temp1',
props: ["myname"]
}
}
}).$mount("#container");

</script>

注意:props这里尽量使用小写字段,驼峰命名需要转化成-

双向传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div id="container">
<mytemp v-bind:myname.sync="name"></mytemp>
<h1>{{name}}</h1>
</div>
<template id="temp1">
<input type="" name="" v-model="myname" /> {{myname}}
</template>
<script src="vue.min.js"></script>
<script>
new Vue({
data: {
name: "Vue"
},
components: {
'mytemp': {
template: '#temp1',
props: ["myname"]
}
}
}).$mount("#container");

</script>

参考资料:Vue.js——60分钟组件快速入门(上篇)

Linux如何实现截图的快捷键

关于linux的截图功能一直觉得没有比较好用的,不过也能凑合使用,不能于QQ等截图功能相提并论。 下面说说如何设置linux下的截图快捷键,个人使用的是Ubuntu。

使用命令:


 gnome-screenshot  

我们使用 gnome-screenshot -h 来查看下对应的命令:

Usage:
 gnome-screenshot [OPTION…]

Help Options:
 -h, --help                     Show help options
 --help-all                     Show all help options
 --help-gapplication            Show GApplication options
 --help-gtk                     Show GTK+ Options

Application Options:
 -c, --clipboard                Send the grab directly to the clipboard
 -w, --window                   Grab a window instead of the entire screen
 -a, --area                     Grab an area of the screen instead of the entire screen
 -b, --include-border           Include the window border with the screenshot
 -B, --remove-border            Remove the window border from the screenshot
 -p, --include-pointer          Include the pointer with the screenshot
 -d, --delay=seconds            Take screenshot after specified delay [in seconds]
 -e, --border-effect=effect     Effect to add to the border (shadow, border, vintage or none)
 -i, --interactive              Interactively set options
 -f, --file=filename            Save screenshot directly to this file
 --version                      Print version information and exit
 --display=DISPLAY              X display to use

根据个人的需要,我觉得使用 -a -i 两个参数就能够满足我的个人需求:


  gnome-screenshot -a -i 

后面的工作就是把这个功能添加到快捷键列表中去,进入设置,添加对应的快捷键如下:    

竖排选择快捷键

Sumblie Text 3 竖排选择快捷键

前段时间一直在找如何使用Sublime text 3使用竖排编辑的文字的快捷键,一直都在傻傻的使用右键+Shift的组合方式使用。 今天找的了使用方法:

  1. 先全选要编辑的文本

  2. Ctrl+Shift+L进入竖排编辑

  3. 使用Home 和End来移动光标的位置

竖排选择快捷键

radmin静默安装脚本

最近有一个需求,是配置新电脑的环境,步骤很简单,停止windows update 服务和禁止windows update服务,把登陆的ctrl+alt+delete的方式去除,最后要安装radmin。

经过几台的安装实在是对繁杂的安装过程不感冒,决定用DOS写一个脚本来完成这些工作,禁用服务和去除Ctrl+Alt+Delete两个步骤用DOS很简单完成,静默安装软件花了点时间,因为不同的安装包对使用的命令不同。静默安装的方式

当然也可以用最简单的方式查看,静默安装的方式:

1
2
3

xxx.exe /?

最终我的脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13

@echo off
echo wait....
net stop wuauserv //停止服务
sc config wuauserv start= disabled //禁止windows更新服务
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v disablecad /t REG_DWORD /d 1 /f //去除ctrl+alt+delete
rem gpupdate /force
echo install radmin
radmin.exe /S /v/qn //静默安装
start /wait keymaker.exe //启动注册机
C:\Windows\SysWOW64\rserver30\rserver30.exe /setup //启动设置界面
echo SUCCESS!!
pause

再提供一个msi包的静默安装方法

1
2
3
4
5
6
7
8
9
10
11
12
13

@echo off
echo wait....
net stop wuauserv
sc config wuauserv start= disabled
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v disablecad /t REG_DWORD /d 1 /f
rem gpupdate /force
echo install radmin
radmin.msi /quiet
start /wait keymaker.exe
C:\Windows\SysWOW64\rserver30\rserver30.exe /setup
echo SUCCESS!!
pause

Maven教程--快速入门

最近一段时间准备整理下Maven相关的知识,受前面几次野心太大的教训,这次没写一篇就发一篇,不再一直憋着等到后面一起发布。本篇文章是一个使用入门的教程(至于安装和配置,已经有很多教程了,这里就不再赘述了)

什么是Maven

对于这个话题,官方有很多解释,但很多的解释都不是很好理解,既然这里是个人的博客,我就怎么容易理解怎么写。

从简单的角度去理解maven,可以简单的理解成一种项目的管理方式(如同vs中的项目文件,不过vs封装的更好一点),从一个项目的角度来考虑,项目主要包含源代码,资源文件,配置文件他其他的相关引用,如何管理这些文件呢? 我们很自然的可以想到把项目的文件和引用放到一个文本中,当编译器再次加载项目的时候就不用再重新扫描文件目录。

对于这个文本格式,我们很自然的想到用json或者xml格式去存储,这样就很容易明白为什么我们要使用Maven了。 讲到这里也不得不把官方的解释拿过来:

Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project’s build, reporting and documentation from a central piece of information.

翻译过来就是:

Apache Maven是一个软件项目管理和逻辑工具。基于项目对象模型(POM)的概念, 可以用来管理项目的构建, 信息中心的报告和文件 。

Maven的在windows和Linux下的安装,这里就不多说了,网上有很多图文并茂的教程,这里就不多说了,也不做推荐了

Maven的快速入门

Maven是一个项目管理工具,对于工具来说只有使用的时候才能更好的理解它,这里我们先不将任何概念,先说说如何使用Maven。

使用Maven命令创建一个新的Maven项目:

1
mvn archetype:generate 

在选择项目Number和Maven版本的时候,由于Maven已经直接帮我门选好,我们可以直接按回车确认(1107的quickstar),后面需要我们输入GroupId,ArtifactId和其他的一些相关信息,可以使用如下:

Maven选择项目

在最终确认的时候,我们等待Maven创建完项目,项目创建完成后,我们使用tree命令来看下Maven生成的项目目录。

Maven选择项目

可以看到Maven的quickstart已经给我我们创建好了相关的目录和代码,看下对应生成的代码:

运行Maven Install 命令,把当前的程序安装到本地仓库。运行完成后,我们查看生成后的项目目录结构如下:

Maven选择项目

Maven常用命令

对于maven常用的命令,我们需要对maven的其他只是进行了解。

  • Maven的生命周期

    maven的强大在于他完整的生命周期,这里就不全部列出来所有的周期,挑几个重要的列出来:

    名称 含义
    1. process-resources 复制并处理资源文件,至目标目录,准备打包。
    2. compile 编译项目的源代码。
    3. process-classes 复制并处理资源文件,至目标测试目录。
    4. process-test-resources 复制并处理资源文件,至目标测试目录
    5. test-compile 编译测试源代码
    6. test 框架运行测试
    7. package 打包
    8. install 安装本地仓库
    9. deploy 发布到远程仓库
  • 常用命令的使用

    命令 含义
    mvn archetype:generate 创建maven项目
    mvn compile 编译源代码
    mvn test-compile 编译测试代码
    mvn test 运行应用程序中的单元测试
    mvn site 生成项目相关的网站
    mvn clean 清除目标目录生成结果
    mvn package 生成jar包
    mvn install 安装本地仓库

AMD的规范演化

对于web项目来说,打交道的不仅仅有后台,前台页面也是少不了的,而前台的页面js也常常是我们后台程序员必须要使用的语言, 今天说下项目中的js的组织方式。

文件函数型

所谓文件函数型是指所有的js的脚本都是中都是一个一个的方法,没有任何的封装,这也是传统的项目中常用的方法。如下:

file1.js

1
2
3
4
function add(a,b){
return a+b;
}

file2.js

1
2
3
4
function minutes(a,b){
return a-b;
}

当我们想使用个方法的时候只需要应用相应的js就行了,这样的缺点很明显,就是要时刻明确文件之间的相互依赖关系的顺序,不然就会导致程序无法正常的运行。 比如还是上面的两个js,我在file1.js中有家一段代码:

file1.js

1
2
3
4
5
6
7
8
function add(a,b){
return a+b;
}

function sum(a,b,c){
return add(a,minutes(b,c));
}

如果在引用的时候,先引用的file1.js则会导致files1.js中有方法没有找到。 如果项目足够的大,就纯粹的js的依赖关系就足以让我们焦头烂额。

这种方法还有很多潜在的风险,如果我在file1.js中有定义了一个minutes方法,这样file2.js中的方法就面临被覆盖的风险,所以这种布局的方式,不应该是项目的首选。

jquery扩展型

在web的项目中,在面对dom操作的时候,传统的js的过于繁杂,所以jquery的使用应该占了很大的一部分的比重(确切的说在MVVM框架流行前)。

jquery也提供了两种扩展方法:   

  • 静态方法扩展    

    jquery静态方法的扩展是直接扩展到$对象上面 :

1
2
3
$.funcA=function(){
// do something
}

在任何引入jquery的地方都可以使用$.funcA()来调用

  • jquery对象方法扩展
1
2
3
4
$.fn.funcB=function(){
//do something
}

使用的方法: $(“#id”).funcB()来调用。

上面的方面解决了方法见的相互的依赖顺序问题,但没有解决方法被覆盖的问题,同时又带来了一个副作用,增加了js方法的调用深度,降低了js的执行效率。

模块加载型

模块话加载是对jquery扩展和文件方法的一个进化,把每个方法都用一个模块封装起来,不至于被外面的方法覆盖。

module1.js

1
2
3
4
5
6
7
8
var module1=function(){

return {
add:function(a,b){
return a+b;
}
}
}();

module2.js

1
2
3
4
5
6
7
var module2=function(){
return {
minutes:function(a,b){
return a-b;
}
}
} ();

如果想使用add方法可以直接引用module1.js,调用module1.add方法。

上面的方法解决的方法被覆盖的问题,但没有解决模块化依赖的问题,这个问题的解决就要靠我们下面要说的AMD的规范。

AMD模块开发规范

上面模块话的开发虽然解决的js的方法的覆盖问题,但js依赖的问题仍然存在,解决这个问题的终极方案就是AMD规范。

AMD规范就是其中比较著名一个,全称是Asynchronous Module Definition,即异步模块加载机制。从它的规范描述页面看,AMD很短也很简单,但它却完整描述了模块的定义,依赖关系,引用关系以及加载机制。

这里用requirjs来解释说明AMD加载的原理:

定义模块:

1
2
3
4
5
6
7
8
9
define(['math', 'graph'], 
function ( math, graph ) {
return {
plot: function(x, y){
return graph.drawPie(math.randomGrid(x,y));
}
}
};
);

上面代码 依赖math和graph两个库,在定义模块前声明了依赖。

引用模块:

1
2
3
require(['foo', 'bar'], function ( foo, bar ) {
foo.doSomething();
});

更多内容参见:
阮一峰-requireJS和AMD规范

vim的使用入门

一直都想写关于vim的使用教程,因为在很多的场景下不得不去使用vim去编辑文本,今天有时间就相关的常用的命令整理下(本文只适合入门的读者,想获得高级教程请止步) ,工具只要经常的使用自然就能熟能生巧。

vim概述

vim是linu下面常用的文本编辑工具,可以使用 vim -v 来查看有没有安装,如果没有安装可以使用 以下命令安装:

sudo apt-get install vim        

vim 有两种模式,命令模式和插入模式。从字面的意思很容易理解两者的用的情景:

  • 命令模式:在这个情况下每一个字符都是一个命令 ,如果想转换成插入模式直接输入插入命令即可(a,A,i,I等)

  • 插入模式:可以正常的输入文本,使用esc可以切换成命令模式

vim 使用


打开、新建、保存、退出


  • 打开和新建文件
vim 1.txt      //如果1.txt存在直接打开,如果不存在直接新建

上面的命令是在命令行中直接使用的,如果我们已经打开了一个文件,又想再打开文件:

:e 1.txt      

如果想打开多个文件,可以直接在后面跟上多个文件名,用空格隔开.

vim 1.txt  2.txt
  • 保存和退出
    命令 Demo 相关功能
    :w :w 保存
    :w fileName :w save.txt 另存为
    :wq或者ZZ :wq或者ZZ 保存并退出
    :q! :q! 不修改直接退出
    :wq! :wq! 保存并退出(root用户才能使用)

插入、删除、跳转


命令 Demo 相关功能
a a 在光标所在位置后插入
A A 在光标所在行尾插入
i i 在光标所在字符前插入
I I 在光标所在字符行首插入
o o 在光标下插入
O O 在光标下插入
x x 删除光标所在处字符
hjkl hjkl 移动光标
gg gg 跳转到第一行
G G 到最后一行
nG或:n nG或:n 到第n行
$ $ 移动到行首
0 0 移动到行尾
------------------------------------- #### 选择、 拷贝、粘贴、剪切 -------------------------------------  
命令 Demo 相关功能
v v 字符选择
V V 选择一行
ctrl+v ctrl+v 矩形选择
y y 复制选中
yy yy 复制一整行
nyy 2yy 复制前n行
p p 在光标下面粘贴
P P 在光标上面粘贴
d d 剪切选中
dd dd 剪切一整行,可以用来删除
------------------------------------- #### 搜索、替换 -------------------------------------
命令 Demo 相关功能
/string /abc 搜索字符串 ,使用n可以跳转到下一个匹配
:set ic :set ic 字符搜索不区分大小写
:set noic :set noic 字符搜索区分大小写
%s/old/new/g或者%s/abc/abc/c %s/abc/ABC/g || %s/abc/ABC/c 全文文本替换 /g不询问直接替换 /c询问是否替换
n,m/old/new/g或者n,m/abc/abc/c n,m/abc/ABC/g || n,m/abc/ABC/c 指定行号文本替换 /g不询问直接替换 /c询问是否替换

翻页


整页翻页 ctrl-f ctrl-b

f就是forword b就是backward

翻半页: ctrl-d ctlr-u
d=down u=up

滚一行: ctrl-e ctrl-y

zz 让光标所杂的行居屏幕中央
zt 让光标所杂的行居屏幕最上一行 t=top
zb 让光标所杂的行居屏幕最下一行 b=bottom


全选


ggVG
稍微解释一下上面的命令

gg 让光标移到首行,在vim才有效,vi中无效
V 是进入Visual(可视)模式
G 光标移到最后一行

个人博客的一些感受

有几天没有更新博客,现在已经是我的博客建立快200天了,在建立博客的过程中,也学到一些东西,同时也踩了一些坑,下面说说个人的经历:

seo相关

关于seo我了解的也是皮毛,收录也一直很低,中间有很长一段时间百度的收录基本上跌为0,可能是开始的时候使用了github Pages的原因,后来这个才知道gitpages对于百度的收录并不是很友好,在百度站长工具展现出很多拒绝访问,这个收录当然不会很高。

后来使用了code.net作为免费的云服务器,但是这个网站与github相比还是有点抠门的,对于免费的用户还有有一些限制的,比如项目最多建立5个,网站建立的时候还要保留对他网站的外链;空间也不大,但对于个人博客来说已经足够使用了,毕竟是个免费的,不能有太多过高的要求。

对于更好的能够被收录(个人博客虽然不能全部靠搜索引擎来获得流量,但也是一种引流的方式)个人有以下几点建议:

  1. 一定要是原创,不要做抄袭很伪原创的东西

  2. 使用sitemap.xml,这个对于搜索引擎来说是一个“康庄大道”,具体可以参见sitemap

  3. 使用robots.txt,这个是搜索引擎访问的第一个页面。具体robots

  4. 使用各个搜索引擎的push方法,对搜索引擎来说要更友好

  5. 对于关键字来说,要保证关键字不要太多,方向大体统一。

统计相关

对于统计来说,我本想用google的统计平台,但苦于没有稳定的翻墙网络,所以就使用了百度统计,功能也很好用,用起来也很顺手,唯一的就是没有能够获得当前所有访客的数据的接口。

对于上面的问题,我也找到了几个解决方案,这个更推荐使用51la统计,虽然界面不是很友好,但数据却很准确,一天真正的做到一个ip只会增加一次。

评论系统

说道评论系统这个是一个问题,我觉得一个好的开源评论系统应该有以下功能:

  1. 可以支持国内的大多数的账号登录,比如QQ,微博

  2. 有评论提醒功能

  3. 要有对评论进行赞同和不赞同的功能。

  4. 使用方便,一段代码就能够解决

  5. 响应速度快,不应该影响博客打开的速度

有人说免费不应该吗?这个我觉得可以免费,但有广告或者可以收费。

博客的评论我使用过多说,后来多说关闭,使用网易云跟帖,网易后来也关闭了,到目前为止使用了韩国的一个免费产品来必力,功能很全面,唯一的缺点就是加载有点慢。

jekyll 相关

很早的时候听过jekyll 这个工具,但却没怎么使用过,知识只有亲手用的时候才能更好的理解,如果想学习jekyll 去搭建博客参见我原来的博客如何搭建免费博客,其他的可以去官网上找找教程和文档,这里就不多说了。

其他

当然目前的博客的访问量也没有多少,做博客更是个人的记录,当然能有更多人的认同,关注,甚至批评你的观点也是个人的一个成长。

HaspMap的原理

实现简单的Map


前几天有想法弄懂HashMap的实现的原理,我自己也YY了一个想法去实现一个简单的Map, 代码如下:

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
public class KeyValuePair<K,V> {

public K Key;
public V Value;

public K getKey() {
return Key;
}

public void setKey(K key) {
Key = key;
}

public V getValue() {
return Value;
}

public void setValue(V value) {
Value = value;
}
}

```
然后使用List作为Container对数据进行存储,主体的内部实现原理如下:

```Java
public class MyMap<K, V> {
private List<KeyValuePair<K, V>> map;

public MyMap() {
map = new ArrayList<KeyValuePair<K, V>>();
}

public V put(K k, V v) {
KeyValuePair<K, V> keyValuePair = new KeyValuePair<K, V>();
keyValuePair.setKey(k);
keyValuePair.setValue(v);
map.add(keyValuePair);
return v;
}

public V get(K k) {
for (KeyValuePair pair : map) {
if (pair.getKey().equals(k)) {
return (V) pair.getValue();
}
}
return null;
}
}

虽然也能实现类似的效果,但我们可以看到这个的map的时间复杂度是O(n),当集合数量很大时,则效率可以的非常的糟糕,下面做一个对比的测试:

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
@Test
public void MapTest(){

long start=System.currentTimeMillis();
MyMap<String,String> map =new MyMap();
for (int i=0;i<10000;i++){
map.put("Key"+i,"value"+i);
}
for (int i=0;i<10000;i++){
map.get("Key"+i);
}
long end=System.currentTimeMillis();
System.out.println("耗时:"+(end-start));

start=System.currentTimeMillis();
Map<String,String> hashMap =new HashMap<>();
for (int i=0;i<10000;i++){
hashMap.put("Key"+i,"value"+i);
}
for (int i=0;i<10000;i++){
hashMap.get("Key"+i);
}
end=System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
}
```

运行结果如下:

```cte
耗时:1815
耗时:14
```

整整慢了100多倍!

### HashMap的实现原理
----------------------------

对于上面的代码,我们应该知道性能最慢的是查找对应的key值,对于ArrayList来说,可能插入也是很大的性能消耗。在JDK中使用一个数组来存储key,索引是根据Key的Hash值来确定,而每一个key对应数据单元是一个链表。用图表示效果如下:

![HaspMap的原理](/img/assets/22/01.png)

下面我们JDK的原理进行分析:

#### 存值

1. 首先定义一个数组,其类型是一个Key-Value类型

2. 根据key的Hash值来确定当前的索引

3. 根据索引值来判断当前是否有值,如果当前有值则把当前的值插入当前数据之前

#### 取值

1.根据key的Hash值来确定当前的索引,根据索引来找到链表的首节点

2.遍历链表,找到指定的Key对应的节点,取出当前值



具体的实现代码如下(可以利用上面的代码):

```Java
public class KeyValuePair<K,V> {

public K Key;
public V Value;
public KeyValuePair next;

public KeyValuePair getNext() {
return next;
}

public void setNext(KeyValuePair next) {
this.next = next;
}
public KeyValuePair(){

}
public KeyValuePair(K k, V v){
this.Key=k;
this.Value=v;
}
public K getKey() {
return Key;
}

public void setKey(K key) {
Key = key;
}

public V getValue() {
return Value;
}

public void setValue(V value) {
Value = value;
}
}

HashMap的实现:

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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
public class MyHashMap<K, V> {

private int defalutLength = 16;
private int size;
private KeyValuePair<K, V>[] arr;
public MyHashMap() {
arr = new KeyValuePair[defalutLength];
size = 0;
}

public V put(K k, V v) {
int index = findIndex(k);
//todo:find out of index
if (arr[index] == null) {
arr[index] = new KeyValuePair(k, v);
} else {
KeyValuePair tempPair = arr[index];
arr[index] = new KeyValuePair(k, v);
arr[index].setNext(tempPair);
}
size++;
return v;
}

private int findIndex(K key) {
int index=key.hashCode() % defalutLength;
return index>0?index:(-1)*index;
}

public V get(K k) {
int index = findIndex(k);
if (arr[index] == null) {
return null;
}
KeyValuePair<K, V> current = arr[index];
while (current.next != null) {
if (current.getKey().equals(k)) {
return current.getValue();
}
current = current.next;
}
return null;
}
public int size(){
return this.size;
}

}

```

同样我们修改测试的代码:
```Java
@Test
public void MapTest(){

long start=System.currentTimeMillis();
MyMap<String,String> map =new MyMap();
for (int i=0;i<10000;i++){
map.put("Key"+i,"value"+i);
}
for (int i=0;i<10000;i++){
map.get("Key"+i);
}
long end=System.currentTimeMillis();
System.out.println("耗时:"+(end-start));

start=System.currentTimeMillis();
Map<String,String> hashMap =new HashMap<>();
for (int i=0;i<10000;i++){
hashMap.put("Key"+i,"value"+i);
}
for (int i=0;i<10000;i++){
hashMap.get("Key"+i);
}
end=System.currentTimeMillis();
System.out.println("耗时:"+(end-start));



start=System.currentTimeMillis();
MyHashMap<String,String> myhashMap =new MyHashMap<>();
for (int i=0;i<10000;i++){
myhashMap.put("Key"+i,"value"+i);
}
for (int i=0;i<10000;i++){
myhashMap.get("Key"+i);
}
end=System.currentTimeMillis();
System.out.println("耗时:"+(end-start));

}
```

运行结果:

```cte
耗时:2337
耗时:26
耗时:337
```

我们看到我们使用的链表在插入数据的时候进行整理,极大的提高了Map的效率,但离Jdk的性能还有很大的差距。


### 优化散列算法
---------------------
对于Map的查找的性能的瓶颈主要在最后的链表的查找,我们可以把Key的数据进行扩大,让Key分布的更加平均,这样就能减少最后链表迭代次数,实现思路:

1. 添加一个报警百分比,当key的使用率长度大于当前的比例,我们对key的数组进行扩容

2. 扩容后对原来的Key进行重新散列

修改后代码如下:

```Java
public class MyHashMap<K, V> {

private int defalutLength = 16;
private final double defaultAlfa = 0.75;
private int size;
private int arrLength;
private KeyValuePair<K, V>[] arr;

public MyHashMap() {
arr = new KeyValuePair[defalutLength];
size = 0;
arrLength=0;
}

public V put(K k, V v) {
int index = findIndex(k);
//todo:find out of index
if(arrLength>defalutLength*defaultAlfa){
extentArr();
}
if (arr[index] == null) {
arr[index] = new KeyValuePair(k, v);
arrLength++;
} else {
KeyValuePair tempPair = arr[index];
arr[index] = new KeyValuePair(k, v);
arr[index].setNext(tempPair);
}
size++;
return v;
}

private int findIndex(K key) {

int index=key.hashCode() % defalutLength;
return index>0?index:(-1)*index;
}
private void extentArr(){
defalutLength=defalutLength*2;
KeyValuePair<K, V>[] newArr=new KeyValuePair[defalutLength];
for (int i=0;i<defalutLength/2;i++){
if(arr[i]!=null){
int index= findIndex(arr[i].getKey());
newArr[index]=arr[i];
}
}
arr=newArr;
}
public V get(K k) {
int index = findIndex(k);
if (arr[index] == null) {
return null;
}

KeyValuePair<K, V> current = arr[index];
while (current.next != null) {
if (current.getKey().equals(k)) {
return current.getValue();
}
current = current.next;
}
return null;
}
public int size(){
return this.size;
}

}

```

最终测试性能结果如下:
```cte
耗时:2263
耗时:23
耗时:33

性能已经很接近了,至于为什么有差异,可能jdk有其它更多的优化(比如当链表长度大于8时,使用红黑树),但本文就讨论到这里。

Java中的容器

容器的种类

为什么要使用容器? 因为数组不能够满足日常的开发需求,数组有以下弊端:

  1. 长度难以扩充
  2. 数据的类型必须相同
  3. 数组无法获得有多少个真实的数据,只能获得数组的长度。

在Java中有常用的三种类型的容器,分别是List 、Map、Set,基于这个三个基本的类型,派生出很多其它的类型,具体关系如下:

Relation

三者的区别:

  • Set(集):与list都是有Collection类的派生出来, 分辨各个元素的标识是HashCode,所以元素不能有重复
  • List(列表):是一个有序的列表,元素如果有重复,也会一一列出来。
  • Map(映射): Map是我们常说的键值对,有key和Value两个元素

使用方法:

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
 @Test
public void ContainerTest() {
String string[] = {"i", "am", "am", "xiao", "ming"};

List<String> list = new ArrayList<String>();
for (String s : string) {
list.add(s);
}
System.out.println("List执行结果:");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}

//
Set<String> set = new HashSet<String>();
for (String s : string) {
set.add(s);
}
Iterator iterator = set.iterator();
System.out.println("===================");
System.out.println("Set 执行结果:");
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}

```

运行结果:

```cte
List执行结果:
i
am
am
xiao
ming
===================
Set 执行结果:
ming
xiao
i
am

三者的区别可以表示如下图:

ListMapSet

各个容器的说明和使用


List


ArrayList

ArrayList是List一个派生类,非线安全,是基于Object数组实现的可动态扩展的容器,在调用Add的时候会判断当前的长度是否已经超过了Size.对应的Add方法:

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
  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);
}

LinkList与ArrayList区别是LinkList 是基于链表的结构设计 ,插入和删除的性能要高于ArrayList,查询的效率低于LinkList,使用方法基本一致,也是非线安全,下面看下性能测试代码:

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
 @Test
public void ListAdd() {
System.out.println("ArrayList ADD耗时:" + AddList(new ArrayList()));
System.out.println("LinkedList ADD耗时:" + AddList(new LinkedList()));

//测试Edit
final int N = 50000;
Integer vals[] = new Integer[N];
Random r = new Random();
for (int i = 0, currval = 0; i < N; i++) {
currval += r.nextInt(100) + 1;
vals[i] = new Integer(currval);
}
List lst = Arrays.asList(vals);
System.out.println("ArrayList Search耗时:" + SearchList(new ArrayList(lst)));
System.out.println("LinkedList Search耗时:" + SearchList(new LinkedList(lst)));
}
long AddList(List list) {
final int N = 50000;
long start = System.currentTimeMillis();
Object o = new Object();
for (int i = 0; i < N; i++)
list.add(0, o);
return System.currentTimeMillis() - start;
}

long SearchList(List lst) {
final int N = 50000;
long start = System.currentTimeMillis();
for (int i = 0; i < N; i++) {
int index = Collections.binarySearch(lst, lst.get(i));
if (index != i)
System.out.println("***错误***");
}
return System.currentTimeMillis() - start;
}
```

运行结果:

``` cte
ArrayList ADD耗时:396
LinkedList ADD耗时:9

ArrayList Search耗时:20
LinkedList Search耗时:8330
Vector

比arraylist多了个同步化机制(线程安全),用法相同,但效率比较低,不建议使用。

Map


HashMap 和 HashTable

二者在使用上功能差不多,区别是HashMap是线程不安全,允许多线程去同时访问,允许插入空值。 而HashTable是相反的,对于HapMap的使用,可以参考下面代码:

1
2
3
4
5
6
7
8
9
10
11
Map map=new HashMap();
map.put("key","abc");
map.put("key1","abc1");
map.put("key2","abc2");
System.out.println(map.get("key"));

map.remove("key");
System.out.println(map.values());
System.out.println(map.keySet());
System.out.println(map.entrySet());

运行结果如下:

1
2
3
4
5
abc
[abc1, abc2]
[key1, key2]
[key1=abc1, key2=abc2]

TreeMap

是一个有顺序的HaspMap

手工实现容器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
public class MyArrayList {

private Object[] _arr;
private int _size;

public MyArrayList() {
_arr = new Object[10];
this._size = 0;
}

public void Add(Object obj) {
if (this._size >= this._arr.length) {
Object[] newArray = new Object[2 * this._size + 1];

for (int i = 0; i < this._size; i++) {
newArray[i] = this._arr[i];
}
this._arr = newArray;
this._arr[this._size] = obj;
this._size++;
} else {
this._arr[this._size] = obj;
this._size++;
}
}

public int Size() {
return this._size;
}

public Object get(int index) {
if (index > this._size) {
throw new IndexOutOfBoundsException("索引超出界限");
}
return this._arr[index];
}
}

使用方法:


@Test
public void PrintMyArr(){
    MyArrayList list=new MyArrayList();
    for (int i=0;i<10000;i++){
        list.Add(i);
    }

    System.out.println(list.Size());
    System.out.println(list.get(503));
    System.out.println(list);
}
 
Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×