Skip to content

Commit d25af0d

Browse files
committed
adjust dir
1 parent 2082f3d commit d25af0d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+6692
-11
lines changed

maketest.py

-5
This file was deleted.

note/BST.md

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
####Tree
2+
对于大量的数据,线性表的访问速度太慢,
3+
4+
A
5+
/ \
6+
/ \
7+
B C
8+
/ \ / \
9+
D E F G
10+
中序遍历:它首先遍历左子树,然后访问根节点,最后遍历右子树(左根右),上图按中序遍历的结果是:**DBEAFCG**
11+
12+
后序遍历:首先遍历左子树,然后遍历右子树,最后遍历根节点(左右根),上图按后序遍历的结果是:**DEBFGCA**
13+
14+
前序遍历:首先访问根节点,然后遍历左子树,最后遍历右子树(根左右),上图按前序遍历的结果是:**ABDECFG**
15+
16+
前中后主要看根节点,根节点在中间就是中序遍历,依此类推。
17+
18+
####术语
19+
节点的度:一个节点含有的子树的个数称为该节点的度,例如A节点的度为:2
20+
树的度:一颗树中,最大节点的度称为树的度,最大节点的度为2,所以上图树的度为2
21+
树的深度/高度:树的层数称为树的高度,根节点的层次为1,上图树的高度为3
22+
23+
#####二叉树
24+
二叉树的每个节点至多有两颗子树,(即二叉树中不存在度大于2的节点)。因此二叉树有如下几种型情。
25+
+ 空二叉树
26+
+ 仅有根节点的二叉树
27+
+ 右子树为空的二叉树
28+
+ 左子树为空的二叉树
29+
+ 左右子树均非空的二叉树
30+
#####二叉树的性质
31+
1. 在二叉树的第i层,至多有2的(i-1)次方个节点
32+
2. 深度为k的二叉树至多有2的k次方-1个节点
33+
3. 任意一颗二叉树T,如果终端节点数为n,度为2的节点数为m,那么n = m+1
34+
35+
深度为k且有2的k次方-1个节点的二叉树称为**满二叉树**,它的特点是每一层上的非叶子节点都有2个节点
36+
**完全二叉树**:深度为k的,有n个节点的二叉树,当且仅当每一个节点都与深度为k的满二叉树中编号从1至n的节点一一对应时,称之为完全二叉树
37+
38+

note/git/git.md

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
git 常用命令解析
2+
==================
3+
###git merge 合并分支
4+
merge 的功能就是做分支合并,假如分支结构如下:
5+
6+
master
7+
/
8+
C0 ---- C1 ---- C2 ---- C4
9+
\
10+
C3 ---- C5
11+
\
12+
issueFix
13+
14+
切换到master分支
15+
16+
$git checkout master
17+
18+
把issueFix分支的内容合并(merge)到当前分支(master)
19+
20+
$git merge issueFix
21+
22+
统计代码行数
23+
24+
git ls-files | xargs wc -l
25+
统计python代码行数
26+
27+
git ls-files | grep .py | xargs wc -l
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
注:为了更好理解本文,请结合原文阅读
2+
####JDBC批处理Select语句
3+
4+
在网络上开销最昂贵的资源就是客户端与服务器往返的请求与响应,JDBC中类似的一种情况就是对数据库的调用,如果你在做数据插入、更新、删除操作,你可以使用executeBatch()方法减少数据库调用次数,如:
5+
6+
Statement pstmt = conn.createStatement();
7+
pstmt.addBatch("insert into settings values(3,'liu')");
8+
pstmt.addBatch("insert into settings values(4,'zhi')");
9+
pstmt.addBatch("insert into settings values(5,'jun')");
10+
pstmt.executeBatch();
11+
12+
但不幸的是对于批量查询,JDBC并没有内建(built-in)的方法,而且JDBC执行批处理的时候也不能有SELECT语句,如:
13+
14+
Statement pstmt = conn.createStatement();
15+
pstmt.addBatch("select * from settings");
16+
pstmt.executeBatch();
17+
18+
会抛出异常:
19+
20+
Exception in thread "main" java.sql.BatchUpdateException: Can not issue SELECT via executeUpdate().
21+
at com.mysql.jdbc.Statement.executeBatch(Statement.java:961)
22+
at test.SelectBatchTest.test2(SelectBatchTest.java:49)
23+
at test.SelectBatchTest.main(SelectBatchTest.java:12)
24+
25+
假设你想从一系列指定的id列表中获取名字,逻辑上,我们要做的事情看起来应该是:
26+
27+
PreparedStatement stmt = conn.prepareStatement(
28+
"select id, name from users where id in (?)");
29+
stmt.setString("1,2,3");
30+
31+
但是这样做并不能得到预期的结果,JDBC只允许你用单个的字面值来替换“?” JDBC之所以这么做是有必要的,因为如果SQL自身可以改变的话,JDBC驱动就没法预编译SQL语句了,另一方面它还能防止SQL注入攻击。
32+
33+
但有四种可替代的实现方法可供选择:
34+
35+
1. 分别对每个id做查询
36+
2. 一个查询做完所有事
37+
3. 使用存储过程
38+
4. 选择批处理
39+
40+
#####方法一: 分别对每个id做查询
41+
42+
假设有100个id,那么就有100次数据库调用:
43+
44+
PreparedStatement stmt = conn.prepareStatement(
45+
"select id, name from users where id = ?");
46+
for ( int i=0; i < 100; i++ ) {
47+
stmt.setInt(i); // or whatever values you are trying to query by
48+
49+
// execute statement and get result
50+
}
51+
这种方法写起来非常简单,但是性能非常慢,数据库往返要处理100次。
52+
53+
#####方法二:一个查询完成所有事
54+
55+
在运行时,你可以使用一个循环来构建如下SQL语句:
56+
57+
PreparedStatement stmt = conn.prepareStatement(
58+
"select id, name from users where id in (?, ?, ?)");
59+
stmt.setInt(1);
60+
stmt.setInt(2);
61+
stmt.setInt(3);
62+
63+
这种方案从代码相比第一种方法算是第二简单的,它解决了来回多次请求数据库的问题,但是如果每次请求参数的个数不一样时预处理语句就必须重新编译,由于每次SQL字面值不匹配,因此如果分别用10个id、3个id、100个,这样会在缓存中产生三个预处理语句。除了重新编译预处理语句之外,先前缓存池中的预处理语句将被移除(受限于缓存池大小),进而导致重新编译已编译过的语句。最后,这种查询方式在内存溢出或磁盘分页操作时查询会占用很长时间。
64+
65+
该方案的另一种变体就是在SQL语句中硬编码:
66+
67+
PreparedStatement stmt = conn.prepareStatement(
68+
"select id, name from users where id in (1, 2, 3)");
69+
70+
这样方式甚至更差,而且没有任何机会对SQL语句重用,至少用“?”还可以对使用相同数量参数的SQL语句进行重用。
71+
72+
PreparedStatement stmt = conn.prepareStatement(
73+
"select id, name from users where id in (?) ; "
74+
+ "select id, name from users where id in (?); "
75+
+ "select id, name from users where id in (?)");
76+
stmt.setInt(1);
77+
stmt.setInt(2);
78+
stmt.setInt(3);
79+
80+
这种方法的优点就是每次查询模版语句都一样,数据库不需要每次计算执行路径。然而,从数据库驱动的角度来说SQL每次都不一样,预处理语句每次必须预处理保存在缓存中。而且不是所有数据库系统都支持分号间隔的多个SQL语句的
81+
82+
#####方法三:使用存储过程
83+
存储过程执行在数据库系统中,因此它可以做很多查询而不需要太多网络负载,存储过程可以收集所有结果一次性返回。这是一种速度很快的解决方案。但是它对数据库的依赖比较强,不能随意的切换数据库系统,否则需要重写存储过程而且需要你分离应用服务器与数据库服务器之间的逻辑。如果应用架构已经使用了存储过程,无疑这是只最佳方案。
84+
85+
#####方法四:批量查询
86+
87+
批量查询是方案一和方案二的折衷选择,它预先确定一批查询参数的常量,然后用这些参数构建一批查询。因为这只会涉及到有限个查询,所以它有预处理语句的优势(预编译不会与缓存中的预处理发生碰撞)。批处理多个值在相同的查询保留了服务器来回请求最小化的优势。最后你可以通过控制批处理的上限来避免大查询的内存问题。如果你有很关键的查询对性能方面有要求又不想用存储过程,那么这是一种很好的解决办法,现在我们通过一个例子说明:
88+
89+
public static final int SINGLE_BATCH = 1;
90+
public static final int SMALL_BATCH = 4;
91+
public static final int MEDIUM_BATCH = 11;
92+
public static final int LARGE_BATCH = 51;
93+
94+
第一件要做的事是你要衡量有多少批处理以及每个批处理的大小。(注意:在真实的代码中,这些值应该写在一个配置文件中而不是采取硬编码的形式,也就是说,你可以在运行时试验并改变批处理的大小)不管真正的批处理大小是多大,你总需要一个单个的批处理---大小为1的批处理(SINGLE_BATTCH)。这样如果有人请求的就是一个值或者在一个很大的查询中最后有遗留下来的单个值都能派上用场。对于批处理的大小,使用素数会更好些。换句话说,大小不应该可以相互的整除或者被相同的数整除。请求数的最大值将有最少的服务器往返。批处理的大小的数量和真正的大小是基于配置变化的。需要注意的是:大的批处理大小不应该太大否则你将遇到内存麻烦。同时最小批处理的大小应该很小,你可能会使用这个来做很多次的查询。
95+
96+
while ( totalNumberOfValuesLeftToBatch > 0 ) {
97+
98+
按如下方式重复操作直到推出循环。
99+
100+
int batchSize = SINGLE_BATCH;
101+
if ( totalNumberOfValuesLeftToBatch >= LARGE_BATCH ) {
102+
batchSize = LARGE_BATCH;
103+
} else if ( totalNumberOfValuesLeftToBatch >= MEDIUM_BATCH ) {
104+
batchSize = MEDIUM_BATCH;
105+
} else if ( totalNumberOfValuesLeftToBatch >= SMALL_BATCH ) {
106+
batchSize = SMALL_BATCH;
107+
}
108+
totalNumberOfValuesLeftToBatch -= batchSize;
109+
110+
这种方案在这里是查找到最大的批处理大小,可能这个最大值比我们实际要查询的值稍大。举例说明:假设查询有75个参数,那么首先选择51个元素(LARGE_BATCH),现在还剩24个待查询,然后接着用11个元素的查询(MEDIUM_BATCH)。现在还有13个值,因为仍然大于11,再做一次11个元素的查询,现在只剩下2个值,它少于那个最小的批处理4(SMALL_BATCH),所以做两次单查询。总共5次往返用了3次预处理在缓存中。这是一个很重要的改进比单独地坐75次单查询。
111+
112+
113+
StringBuilder inClause = new StringBuilder();
114+
boolean firstValue = true;
115+
for (int i=0; i < batchSize; i++) {
116+
inClause.append('?');
117+
if ( firstValue ) {
118+
firstValue = false;
119+
} else {
120+
inClause.append(',');
121+
}
122+
}
123+
PreparedStatement stmt = conn.prepareStatement(
124+
"select id, name from users where id in (" + inClause.toString() + ')');
125+
126+
现在已经构建了一个真实的预处理语句,由于一直用相同的方式构建的查询,驱动注意到SQL是相同的。(注意:如果你还没有用Java5,使用StringBuffer替换StringBuilder才能正常编译),返回id很重要这样有利于查找哪个名字对应哪个id。
127+
128+
for (int i=0; i < batchSize; i++) {
129+
stmt.setInt(i); // or whatever values you are trying to query by
130+
}
131+
132+
设置合适的值数量去查询,包括其他搜索条件查询。仅仅只要把这些参数在之举参数之后。在这种情况你可以最终当前的索引。
133+
134+
从这点来看,你仅仅只是执行查询返回了结果,在第一次尝试的时候,你应该关注一下性能的提升,根据具体情况调整优化批处理的大小(batch size)。
135+
136+
*正如那句名言所说:“过早的优化是万恶之源”,批处理应该是用于解决性能问题。*
137+
138+
原文:[Batching Select Statements in JDBC](http://www.javaranch.com/journal/200510/Journal200510.jsp#a2)

note/java/Generic.md

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
在没有泛型前,从集合中读取的元素都必须进行强制转换,比如:
2+
3+
List foos = new ArrayList();
4+
foos.add(new Foo());
5+
Foo = (Foo)foos.get(0); //必须强制转换
6+
7+
否则连编译没法通过,但是有了泛型之后,编译器可以在插入数据的时候自动帮你转换,编译时告知是否插入了错误类型。
8+
9+
List<Foo> foos = new ArrayList<Foo>();
10+
foos.add(new Foo());
11+
Foo = foos.get(0); //无需转换
12+
13+
####使用泛型的需要注意的陷进
14+
15+
首先需要知道几个术语:
16+
17+
+ **泛型**:声明的时候,拥有一个或多个类型参数的类或者接口,称之为泛型类或泛型接口,两者又统称为泛型(generic type) 如:List<String>就声明了一个String类型的参数
18+
19+
+ **参数化类型(parameterized type)**:参数化类型包含一个类或者接口名称C,以及实际的类型参数列表<T1,...Tn>
20+
21+
+ **原生类型(raw type)**:原生类型是满足以下条件之一:
22+
1. 使用的泛型类型声明的名称,而没有任何伴随的实际类型的参数
23+
2. 元素的类型为原生类型的数组类型
24+
3. 原生类型R的任何非静态类型成员,且它不是从R的超类或者超接口派生而来的
25+
26+
举例:
27+
28+
public class MyType<E> {
29+
class Inner { }
30+
static class Nested { }
31+
32+
public static void main(String[] args) {
33+
MyType mt; // warning: MyType is a raw type
34+
MyType.Inner inn; // warning: MyType.Inner is a raw type
35+
36+
MyType.Nested nest; // no warning: not parameterized type
37+
MyType<Object> mt1; // no warning: type parameter given
38+
MyType<?> mt2; // no warning: type parameter given (wildcard OK!)
39+
}
40+
}
41+
这里,`mt``inn`就是原生类型的,而`ntst`不是,因为他是静态类型成员,`mt1``mt2`属于参数化类型。
42+
43+
####陷进一: 不要使用原生态类型
44+
本质上,原生类型的行为方法和泛型没什么两样,就像如下代码:
45+
46+
List names = new ArrayList(); // warning: raw type!
47+
names.add("John");
48+
names.add("Mary");
49+
names.add(Boolean.FALSE); // not a compilation error!
50+
51+
代码没有任何问题,但是,
52+
53+
for (Object o : names) {
54+
String name = (String) o;
55+
System.out.println(name);
56+
} // throws ClassCastException!
57+
// java.lang.Boolean cannot be cast to java.lang.String
58+
59+
60+
如上代码在运行的时候,就会抛出`ClassCastException`的异常。
61+
62+
有了泛型,编译器就能帮你完成类型检测的工作.
63+
64+
List<String> names = new ArrayList<String>();
65+
names.add("John");
66+
names.add("Mary");
67+
names.add(Boolean.FALSE); // compilation error!
68+
69+
####原生类型List与List<Object>参数化类型的区别
70+
71+
原生类型List躲避了泛型检查,参数化类型List<Object>告知编译器,它能装任意类型的对象,虽然你可以将List<String>传递给类型List的参数,但是不能将它传递给类型List<Object>的参数。为什么呢?这是子类型化(subtyping)的规则。
72+
73+
####子类型化(subtyping)
74+
75+
常规类中,如果类B继承A,那么类B就是类A的子类,但是这条规则并不能适用于泛型中。
76+
77+
class A {}
78+
class B extends A {}
79+
80+
List<B> lb = new ArrayList<>();
81+
List<A> la = lb; //compile-time error
82+
[generics-listParent.gif]
83+
尽管Integer是Number的子类,但是List<Integer>并不是List<Number>的子类,两者没有任何关系,而他两的共同父类是List<?>.
84+
85+
为了通过List<Integer>的元素来访问Number的方法,可以使用向上通配符:
86+
87+
List<? extends Integer> intList = new ArrayList<>();
88+
List<? extends Number> numList = intList //Ok,List<? extends Integer> is a subtype of List<? extends Number>
89+
90+
91+
92+
http://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it
93+
http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.8
94+
http://docs.oracle.com/javase/tutorial/java/generics/subtyping.html
95+

0 commit comments

Comments
 (0)