Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言。Java 技术具有卓越的通用性、高效性、平台移植性和安全性,广泛应用于PC、数据中心、游戏控制台、科学超级计算机、移动电话和互联网,同时拥有全球最大的开发者专业社群。
给你学习路线:html-css-js-jq-javase-数据库-jsp-servlet-Struts2-hibernate-mybatis-spring4-springmvc-ssh-ssm
小编推荐一个学Java的学习裙【 六五零,五五四,六零七 】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!
1。用StringBuilder(技术篇)
StingBuilder应该在我们的java代码默认使用,应避免使用运算符。也许你会对StringBuilder的语法糖的不同意见,如:
字符串x =“”+ args.length +“B”;
它将被编译成:
0新java.lang.stringbuilder [ 16 ]
3杯
4最不发达国家[ 18 ]
6 java.lang.stringbuilder invokespecial(java。lang.string)[ 20 ]
9 aload_0 [参数]
10获取数组长度
11 java.lang.stringbuilder.append虚调用(int):java.lang.stringbuilder [ 23 ]
14最不发达国家[ 27 ]
16 java.lang.stringbuilder.append虚调用(java。lang.string):java.lang.stringbuilder [ 29 ]
19 java.lang.stringbuilder.tostring虚调用():java.lang.string [ 32 ]
22 astore_1 [X]
不过,这是怎么回事呢?您需要使用以下部分来改进字符串吗?
字符串x =“”+ args.length +“B”;
如果(args.length = 1)
x = x + args [ 0 ];
现在使用的第二StringBuilder,这个StringBuilder不在堆消耗更多的内存,但它带来的压力,气相色谱法。
StringBuilder x =新StringBuilder(“”);
X.append(args。长度);
X.append(“B”);
如果(args.length = 1);
X.append(args [ 0 ]);
总结
在上面的例子中,如果你依靠java编译器创建的实例,编译的效果几乎没有是否使用StringBuilder实例。请记住:在N.O.P.E分公司,每个CPU周期时间花在GC或StringBuilder分配默认的空间,我们浪费了N x O X P的时间。
在一般情况下,使用StringBuilder的效果优于运算符的使用。如果可能的话,请选择StringBuilder当你需要通过参考多个方法,因为字符串消耗额外的资源。jooq采用这样一种方式来生成复杂的SQL语句。只有一个StringBuilder是整个AST抽象语法树(树)中使用SQL传递。
更悲剧的是,如果你还在使用StringBuffer,取代StringBuilder StringBuffer。毕竟,对同步字符串并不需要太多。
2。避免使用正则表达式(好的技术文本)
正则表达式给人的印象是快速而简单。但在N.O.P.E分公司使用正则表达式将是最坏的决定。如果必须使用正则表达式,但在代码中计算密集型,至少会缓存模式,避免重复编译模式。
静态模式heavy_regex =
pattern.compile (((((((((((( x)*)* Z *”);
如果只使用一个简单的正则表达式如下:
字符串[]部分= ipaddress.split(“。”);
这是使用普通字符数组或基于索引的操作的最好方法。例如,较低的可读代码实际上扮演相同的角色。
int length = ipaddress.length();
int偏移量= 0;
int部分= 0;
对于(int = i 0;i <长度;i + +){
如果(我= =长1 | |
ipaddress.charat(i + 1)= =和# 39){。与# 39;;
零件[零件]
IpAddress.substring(偏移,我+ 1);
零件+;
偏移量=i + 2;
}
}
上面的代码也表明过早的优化是没有意义的。尽管与拆分()方法相比,此代码的可维护性相对较差。
挑战:聪明的小伙伴能想出更快的算法吗?欢迎留言。
总结
正则表达式非常有用,但它们在使用时也要付出代价。特别是,在n.o.p.e分支的深度,你应该避免使用任何代码来避免正则表达式。同时对各种JDK字符串方法中使用正则表达式的小心,如string.replaceall()或String.split()。您可以选择使用更受欢迎的开发库(如Apache公共资源库)来进行字符串操作。
三.不要使用迭代器()方法
这个建议并不适用于一般的情况,只适用于一个在N.O.P.E分行深景。尽管如此,还是应该有所了解的。java 5格式的方便,我们可以忘记内循环的方法,如:
对于(字符串值:字符串){
这里有用的东西
}
当代码运行到这个循环的每一次,如果变量是一个字符串,程序会自动创建迭代器的实例。如果ArrayList使用,虚拟机会自动分配3的整数类型大小的内存堆上的对象。
私有类ITR实现迭代器{
int指针;
国际lastret = - 1;
国际expectedmodcount = modcount;
/ /…
还可以用下面的等效循环代替上面的for循环。这只是堆栈上的“浪费”。
int的大小= strings.size();
对于(int = i 0;i <大小;i + +){
字符串值:strings.get(我);
这里有用的东西
}
如果循环中字符串的值没有改变,则可以使用一个数组来实现循环。
对于(字符串值:stringarray){
这里有用的东西
}
总结
迭代器,Iterable接口,和Foreach循环是非常好的,无论是从容易写的角度,还是从API的设计角度。但以使用它们为代价,为堆中的每一个循环创建一个对象。如果循环要多次执行,请注意避免产生无意义的实例。这是更好地使用基本的指针而不是迭代器,迭代器接口和foreach循环。
讨论
一些反对上述观点(尤其是代替迭代器指针操作)在Reddit讨论详细。
4。不要调用开销过高的方法。
有些方法很贵。以n.o.p.e分行为例,我们不提叶相关的方法,但这是可以。我们假设的JDBC驱动程序需要克服一切困难,计算resultset.wasnull(返回值的方法)。我们自己的SQL框架的实现可能如下所示:
如果(type =整数类){
结果=(t)wasnull(RS,
integer.valueof(rs.getint(指数)));
}
和/ /然后…
静态T wasnull(连接数据库,T值)
抛出SQLException {
返回rs.wasnull()?空值;
}
在上述逻辑的resultset.wasnull()方法被调用,每一次的int值从获得结果集,但是方法getInt()定义为:
返回类型:变量值;如果SQL查询结果为null,返回0。
因此,一个简单而有效的改进方法如下:
静态wasnull(T
连接数据库,T值
)
抛出SQLException {
返回值= = null | |(
((价值。intvalue)= = 0和rs.wasnull()))
空值;
}
这是一件容易的事。
总结
方法调用被缓存以替换叶节点中的高开销方法,或者在方法约定允许时避免调用高开销方法。
5。使用原始类型和堆栈
大量的仿制药在使用的例子从jooq,致使字节,短,int的封装类的使用,和长。但在推广专业的java 10或瓦尔哈拉的项目,它不应该是一个限制的代码。因为下面的方法可以用下面的方法来代替:
/存储在堆中
整数i=817598;
......如果这是用这种方式写的:
/存储在堆栈中
int = i 817598;
使用数组时情况可能变得更糟。
/在堆上生成三个对象
整数[ [] = { 1337, 424242 };
......如果这是用这种方式写的:
仅在生成对象的堆上
int([]){ { 1337, 424242 };
总结
当我们在n.o.p.e.分支的深处,我们应该尽量避免使用包装类。做这件事的一件坏事是它给GC带来了很大的压力。GC将太忙,无法清除包装器类生成的对象。
因此,一种有效的优化方法是使用基本数据类型,固定长度数组,并使用一系列的分割变量来确定数组中对象的位置。
trove4j,遵循LGPL协议,是一个java集合类的库,它为我们提供了比整形数组int []性能更好的实现。
例外
以下是此规则的例外情况:由于布尔和字节类型都不足以让JDK为他们提供一个缓存的方法。我们可以写这个:
布尔= true;//语法糖:…
布尔A2 = boolean.valueof(真的);
字节B1(字节)= 123;//语法糖:…
字节B2 = byte.valueof((字节)123);
其他整数的基本类型也有类似的情况,如char、短、int和长。
不自动包装或调用thetype.valueof()方法这些基本类型调用构造函数的方法。
不要在包装器类上调用构造函数,除非您希望获得堆中未创建的实例。
小编推荐一个学Java的学习裙【 六五零,五五四,六零七 】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!
6。避免递归(真正的技术文本)
现在,像斯卡拉这样的函数式编程语言被鼓励使用递归。因为递归通常意味着尾递归(尾递归),可以分解为单个的个体优化。如果使用编程语言,最好能支持它。但即使如此,也需要注意的是,算法的微调将使尾递归成为普通递归。
我们希望编译器能自动检测到这一点。否则,我们将浪费大量的堆栈帧,而我们只需要使用几个局部变量。
总结
有什么要说的这部分,除了迭代而不是递归的n.o.p.e使用分支。
7。使用子查询()
当我们想要遍历一个以键值对形式保存的映射时,我们必须为下面的代码找到一个好的理由:
为(K键:map.keyset()){
V值:map.get(关键);
}
更不用说以下文字了:
对于(进入< k,v >输入:map.entryset()){
K键= entry.getkey();
V值= entry.getvalue();
}
我们应该小心,当我们使用n.o.p.e.分支图。因为许多看似复杂的O(1)访问操作是由一系列操作组成的。这次访问本身并不是免费的。至少,如果你要使用地图,然后使用entrySet()方法进行迭代!这样,我们所要访问的map.entry例。
总结
的entrySet()方法必须使用当你需要迭代的核心价值以地图的形式。
8。使用EnumMap(真EnumSet或技术)
例如,在某些情况下,当使用配置映射时,我们可能知道预先存储在map中的键值。如果这个关键是非常小的,我们应该考虑使用EnumSet或EnumMap代替我们共同的好或HashMap。下面的代码给出了一个明确的解释:
私人瞬态对象[ ]瓦尔斯;
公共v(k键,v值){
/ /…
指数= key.ordinal();
瓦尔斯[索引] = masknull(价值);
/ /…
}
上层代码的主要实现是使用数组而不是哈希表。特别是,当你插入一个新的值到一个地图,你要做的是得到一个恒定的序列号生成的编译器为每个枚举类型。如果有一个全球地图的配置(例如,只有一个实例),EnumMap将获得更优异的性能比HashMap如果访问速度提高。原因是使用EnumMap堆内存小于HashMap(位),和HashMap()调用hashCode方法和equals()在每一个关键值的方法。
总结
枚举和EnumMap是亲密的小伙伴。当我们使用类似的枚举的键值(枚举等)的结构,我们应该考虑这些关键值被声明为枚举类型和使用它们作为EnumMap键。
9、习惯的hasCode优化()方法和equals()方法(技术篇)
至少,hashCode()=()方法进行优化时,EnumMap是没有用的。一个好的hashCode()方法是必要的因为它可以防止不必要的调用开销大,等于()方法。
在每个类的继承结构中,都很容易接受简单的对象。让我们来看看如何jooq的org.jooq.table实施?
实施hashCode的最简单和最快的方式()()如下:
基于一般的表/ abstracttable实施:
@Override
public int hashCode(){
与标准queryparts / [ 1938 ] #相比,这是一个更有效的hashCode(实现)
返回name.hashcode();
}
名称是表的名称。我们甚至不需要考虑模式或其他表属性,因为表名在数据库中通常是唯一的。和变量名是一个字符串,它本身已经缓存的hashCode()值。
在这段代码中的注释非常重要,因为继承了abstractquerypart的abstracttable是基本实现任何抽象语法树元。普通的抽象语法树的元素没有任何属性,所以有关于hashCode的优化没有幻想()方法的实现。hashCode()方法后内容如下:
abstractquerypart /通用的抽象语法树的实现:
@Override
public int hashCode(){
这是一个默认的实现。
/具体实现子类应覆盖改进性能的方法。
返回创建()。RenderInlined(这)HashCode();
}
换句话说,触发整个SQL渲染工作流(呈现工作流)来计算普通抽象语法树元素的哈希代码。
等号()方法更有趣:
/ / abstracttable一般表实施的基础:
@Override
公共布尔值等于(对象{){
如果(= =){
返回true;
}
电话/ [ 2144 ] #高开销abstractquerypart.equals()方法,
可能知道物体不等于。
如果(这是abstracttable){
如果(stringutils.equals(名称)
((((( abstracttable <?>)
返回super.equals(,);
}
返回false;
}
返回false;
}
首先,不要使用equals()方法的太早(不仅在n.o.p.e.),如果:
这个=参数
不兼容:参数
注意:如果我们使用instanceof测试兼容型早期,后者的情况实际上包含参数= = null。
通过以上案例的比较,我们可以得出一些结论。jooq的table.equals()方法,例如,用于比较两个表是否是相同的。不管具体的实现类型是什么,它们都必须具有相同的字段名。例如,以下两个元素不能相同:
com.example.generated.tables.my_table
DSL.tableByName(“my_other_table”)
如果我们可以很容易地判断传入参数是否等于实例本身(这个),那么当结果为false时,我们可以放弃操作。如果返回结果是真的,我们可以进一步判断父类(超级)实现。当大多数对象不同时,我们可以尽可能快地结束方法,以节省CPU的执行时间。
有些物体的相似性比其他物体的相似性高。
在jooq,大多数的表的情况下jooq代码生成器生成的,和equals()这些实例方法已经深入优化。哪种类型的衍生工具(派生表)(表、表值函数(表值、函数))(数组、表)数组表表(连接表)、连接PivotTable(枢轴表)、公共表表达式(公共表表达式)、相等()是维护基本实现方法。
10。考虑使用set而不是单个元素(技术文本)
最后,还有一种情况,可以应用到所有的语言,不仅与java。此外,该n.o.p.e.分支,我们的研究也将有助于了解从O(N3)为O(n log n)。
不幸的是,许多程序员使用简单的本地算法来考虑问题。他们习惯于有条不紊地解决这个问题。这是“命令”形式的函数式编程风格。这种编程风格很容易从单纯的命令式编程到面向程序设计的函数式编程。但是,这些样式只在SQL和R语言中丢失。
声明式编程。
在SQL中,我们可以声明数据库的效果而不考虑算法的效果。数据库可以根据不同类型的数据,例如约束、密钥和索引来选择最佳算法。
从理论上讲,我们最初在SQL和关系演算之后有一个基本概念。在实践中,SQL厂商实现架空型高效优化器CBO(基于成本的优化器)在过去的几十年里。然后,在2010版中,我们终于发掘了SQL的所有潜力。
但是我们不必在集合中实现SQL。所有语言和库都支持集合、集合、包和列表。使用set的主要优点是,我们可以使代码简洁明了。例如,以下是书面的:
someotherset索美塞相交
而不是
8 / / java之前写的
设置结果=新好();
(对象为候选人:索美塞)
如果(someotherset.contains(候选人))
result.add(候选人);
即使有/ java 8不是很有帮助
someset.stream()
。过滤器(someotherset::包含)
收集(Collectors.toSet());
有些人可能对函数式编程和java 8种不同的意见帮助我们编写更简单、更简洁的算法。但这种观点并不一定正确。我们可以把命令式java 7环路到java 8流的集合,但我们仍然使用相同的算法。但是SQL样式的表达式是不同的:
someotherset索美塞相交
上面的代码可以在不同引擎上实现1000种不同的实现。我们今天学习的是,在交叉操作时,两套自动转换为EnumSet。我们甚至可以做平行交叉作业而不需要调用底层的Stream.parallel()方法。
小编推荐一个学Java的学习裙【 六五零,五五四,六零七 】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!
总结
在这篇文章中,我们讨论的n.o.p.e.分支优化。例如,深入研究算法的复杂度。作为开发商的jooq,我们很高兴能优化SQL生成。
每一个查询是一个独特的StringBuilder产生。
模板引擎实际上处理字符而不是正则表达式。
尽可能多地选择数组,尤其是当您遍历侦听器时。
通往jdbc的路很远。
等待。
jooq是在“食物链”的底部,因为它是最后一个API调用的计算机程序,当它的叶子JVM DBMS。在食物链的底部意味着任何线需要n x O X P时jooq执行,所以我想尽快优化。
我们的业务逻辑可能不n.o.p.e.复杂。但底层框架可能非常复杂(本地SQL框架、本地库等)。所以我们需要检查它与java任务控制或其他工具,按照原则,我们确认是否有一个地方是优化今天提到。