来源于:http://thecodesample.com/
本文给出几个提升Java性能的基本方法:
不要在循环条件中计算
如果在循环 (如for循环、while循环)条件中计算,则循环一遍就要计算一次,这会降低系统的效率,如:
//每次循环都要计算 count *2
while(i < count *2) {
//Do something ...
}
应该替换为:
//只计算一遍
int total = count *2
while(i <total) {
//Do something ...
}
再如:
for (int i=0; i<size()*2; i++) {
//Do something
}
应该改成:
for (int i=0, stop=size()*2; i<stop; i++) {
//Do something
}
不要将相同的子表达式计算多次
if (birds.elementAt(i).isGrower()) ...
if (birds.elementAt(i).isPullet()) ...
将相同的子表达式提取为一个变量,如:
Bird bird = birds.elementAt(i);
if (bird.isGrower()) ...
if (bird.isPullet()) ...
减少数组访问次数,每次数组访问都需要检查一下索引,所以减少数组访问的次数是值得的。
double[] rowsum = new double[n];
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
rowsum[i] += arr[i][j];
改成如下形式:
double[] rowsum = new double[n];
for (int i = 0; i < n; i++) {
double[] arri = arr[i];
double sum = 0.0;
for (int j = 0; j < m; j++)
sum += arri[j];
rowsum[i] = sum;
}
尽可能把变量、方法声明为 final static 类型
如下例子用于返回给定年份给定月份所具有的天数。
public static int monthdays(int y, int m) {
int[] monthlengths = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
return m == 2 && leapyear(y) ? 29 : monthlengths[m-1];
}
上述方法每次调用都会重新生成一个monthlengths 数组,但必要要主要的是:数组不会改变,属于不变数组。在这种情况下,
把它声明为 final static 更加合适,在类加载后就生成该数组,每次方法调用则不再重新生成数组对象了,这有助于提高系统性能。
应该改成:
private final static int[] monthlengths = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
public static int monthdays(int y, int m) {
return m == 2 && leapyear(y) ? 29 : monthlengths[m-1];
}
稍微复杂一些的初始化可以使用静态初始化块static {… }来做,如:
private final static double[] logFac = new double[100];
static {
double logRes = 0.0;
for (int i=1, stop=logFac.length; i<stop; i++)
logFac[i] = logRes += Math.log(i);
}
public static double logBinom(int n, int k) {
return logFac[n] - logFac[n-k] - logFac[k];
}
缩小变量的作用范围
关于变量,能定义在方法内的就定义在方法内,能定义在一个循环体内的就定义在循环体内,能放置在一个try…catch块内的就放置在该块内,
其目的是加快GC的回收。
字符串频繁”+”操作使用StringBuilder或者StringBuffer
虽然String的联接操作(“+”)已经做了很多优化,但大量的追加操作上StringBuilder或者StringBuffer还是比”+”的性能好很多。
String s = "";
for (int i=0; i<n; i++) {
s += "#" + i;
}
应该改成:
StringBuilder sbuf = new StringBuilder();
for (int i=0; i<n; i++) {
sbuf.append("#").append(i);
}
String s = sbuf.toString();
此外,
String s = "(" + x + ", " + y + ")";
它将自动会编译为StringBuilder.append(…)。
覆写Exception的fillInStackTrace方法
fillInStackTrace方法是用来记录异常时的栈信息的,这是非常耗时的动作,如果在开发时不需要关注栈信息,则可以覆盖之,
如下覆盖fillInStackTrace的自定义异常会使性能提升10倍以上:
class MyException extends Exception {
public Throwable fillInStackTrace() {
return this;
}
}
延迟初始化
当你不知道一个对象是否需要的时候,可以选择延迟初始化:直到需要的时候才初始化,但只分配一次。
public class Car {
private Button button = new JButton();
public Car() {
... initialize button ...
}
public final JButton getButton() {
return button;
}
}
可以改为:
public class Car {
private Button button = null;
public Car() { ... }
public final JButton getButton() {
if (button == null) { // button not yet created, so create it
button = new JButton();
... initialize button ...
}
return button;
}
}
不要重复创建对象
比如,一个商品相关信息的更新,为了搜索,需要为其创建索引。
针对单个商品建立的索引如下:
public void index(Product product) {
if (product != null) {
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
fullTextEntityManager.index(product);
}
}
针对多个商品建立索引,可能会写出如下的代码,写一个for循环,直接调用单个商品索引建立的方法。
public void indexAll(List<Product> products) {
if (products != null) {
for (Product product : products) {
index(product);
}
}
}
这个方法比较直观,但是为每一个Product创建索引的时候,都会新创建一个FullTextEntityManager对象。其实我们只需要创建一次,代码如下:
public void indexAll(List<Product> products) {
if (products != null) {
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
for (Product product : products) {
fullTextEntityManager.index(product);
}
}
}
使用Collections.emptyXXX方法代替创建空的ArrayList,HashSet等
下面给出一个获取一个指定分类以及其所有父类的规格的方法
public Set<Specification> getAllSpecifications(ProductCategory productCategory) {
Set<Specification> specifications = new HashSet<Specification>();
if (null != productCategory) {
specifications.addAll(productCategory.getSpecifications());
specifications.addAll(productCategory.getParentSpecifications());
}
return specifications;
}
该方法中,不管productCategory是否为空,都会新建一个空的Set集合,而每次创建的空Set也会是不同的对象。我们可以先判断商品分类是否为空
,如果是空,则调用Collections的emptySet()方法,该方法将返回一个常量的空集,并不需要重新创建新的空集对象。
代码如下:
public Set<Specification> getAllSpecifications(ProductCategory productCategory) {
if(productCategory == null){
return Collections.<Specification>emptySet();
}
Set<Specification> specifications = new HashSet<Specification>();
specifications.addAll(productCategory.getSpecifications());
specifications.addAll(productCategory.getParentSpecifications());
return specifications;
}
去掉重复代码
下面的例子是根据一个指定分类,获取其子分类的方法, 分类结果以Map的形式返回,key为分类id,value为分类名称,这个主要用
于前台Ajax去构建分类的树形结构。如果指定的分类为null,则获取根分类。
public Map<Long, String> getSubCategoryTree(Long parentId) {
Map<Long, String> categoryOptions = new HashMap<Long, String>();
ProductCategory productCategory = productCategoryService.find(parentId);
if (null == productCategory) {
for (ProductCategory pc : productCategoryService.findRoots()) {
categoryOptions.put(pc.getId(), pc.getName());
}
} else {
for (ProductCategory pc : productCategory.getChildren()) {
categoryOptions.put(pc.getId(), pc.getName());
}
}
return categoryOptions;
}
在这个方法中,if-else语句中除了获取的集合 productCategoryService.findRoots()和 productCategory.getChildren()不同外,其余都是一样的。
我们完全可以将这个集合提取出来,代码块将不再包含if-else块,同时for循环也只需要一个。
帮助
public Map<Long, String> getSubCategoryTree(Long parentId) {
Map<Long, String> categoryOptions = new HashMap<Long, String>();
ProductCategory productCategory = productCategoryService.find(parentId);
Collection<ProductCategory> productCategories = (productCategory == null) ? productCategoryService
.findRoots() : productCategory.getChildren();
for (ProductCategory pc : productCategories) {
categoryOptions.put(pc.getId(), pc.getName());
}
return categoryOptions;
}
去掉不必要的else块
private int getAttributeValueId(String value) {
if (StringUtils.isEmpty(value)) {
return -1;
} else {
String[] values = value.split(ATTRIBUTE_ID_SEPARATOR);
String id = values[values.length - 1];
return Integer.parseInt(id);
}
}
可以改成
private int getAttributeValueId(String value) {
if (StringUtils.isEmpty(value)) {
return -1;
}
String[] values = value.split(ATTRIBUTE_ID_SEPARATOR);
String id = values[values.length - 1];
return Integer.parseInt(id);
}
在不需要再用时关闭连接资源
下面是一个简单的查询数据库的代码,执行完查询操作之后,判断连接资源是否为null,如不为null,则调用相应的close()方法关闭资源。
Statement stmt = null;
ResultSet rs = null;
Connection conn = getConnection();
try{
stmt = conn.createStatement();
rs = stmt.executeQuery(sqlQuery);
progressResult(rs);
}catch(SQLException e){
//forward to handler
}finally {
if(rs !=null){
rs.close();
}
if(stmt != null){
stmt.close();
}
if(conn !=null){
conn.close();
}
}
在这个关闭的过程中,存在着风险,比如rs在调用close()方法时发生异常,则stmt和conn将不会关闭。
为了能够使资源都得到释放,应改为:
Statement stmt = null;
ResultSet rs = null;
Connection conn = getConnection();
try{
stmt = conn.createStatement();
rs = stmt.executeQuery(sqlQuery);
progressResult(rs);
}catch(SQLException e){
//forward to handler
}finally {
try{
if(rs !=null){
rs.close();
}
}catch(SQLException e){
//forward to handler
}finally{
try{
if(stmt !=null){
stmt.close();
}
}catch(SQLException e){
//forward to handler
}finally{
try{
if(conn !=null){
conn.close();
}
}catch(SQLException e){
//forward to handler
}
}
}
}
如果使用的是JDK1.7后的版本, 也可以使用Java SE 7中支持的使用try-with-resource的方式。
try(
Connection conn = getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sqlQuery)){
progressResult(rs);
}catch(SQLException e){
//forward to handler
}