过早的优化是万恶之源。
优化了的代码可读性变差,可改性可适应性变差,可维护性变差。
远离过度优化,优化是个无底洞,把主要精力放在代码逻辑上。
优化的代码是活在当下的,是严重依赖硬件的,不利于表达永恒的思想。
许多优化编译器已经做了,如果再做有可能适得其反,既然是高级语言还是要集中精力在逻辑上而不是运行效率上。

Java在计算密集型程序上运行效率高于C++,但只要涉及反复开辟释放空间,反复输入输出操作,Java就虚了。

一.减少值传递,多用引用传递(C++)

至于其中的原因,相信大家也很清楚,如果参数是int等语言自定义的类型可能能性能的影响还不是很大,但是如果参数是一个类的对象,那么其效率问题就不言而喻了。例如一个判断两个字符串是否相等的函数,其声明如下:

bool Compare(string s1, string s2)
bool Compare(string *s1, string *s2)
bool Compare(string &s1;, string &s2;)
bool Compare(const string &s1;, const string &s2;)
bool Compare(string s1, string s2)
bool Compare(string *s1, string *s2)
bool Compare(string &s1;, string &s2;)
bool Compare(const string &s1;, const string &s2;)

其中若使用第一个函数(值传递),则在参数传递和函数返回时,需要调用string的构造函数和析构函数两次(即共多调用了四个函数),而其他的三个函数(指针传递和引用传递)则不需要调用这四个函数。因为指针和引用都不会创建新的对象。如果一个构造一个对象和析构一个对象的开销是庞大的,这就是会效率造成一定的影响。
然而在很多人的眼中,指针是一个恶梦,使用指针就意味着错误,那么就使用引用吧!它与使用普通值传递一样方便直观,同时具有指针传递的高效和能力。因为引用是一个变量的别名,对其操作等同于对实际对象操作,所以当你确定在你的函数是不会或不需要变量参数的值时,就大胆地在声明的前面加上一个const吧,就如最后的一个函数声明一样。同时加上一个const还有一个好处,就是可以对常量进行引用,若不加上const修饰符,引用是不能引用常量的。

二.++i和i++,前置运算和后置运算

i++会开辟一个临时变量用来返回,++i直接更改数据。所以++i效率较高。

三.循环内定义变量和循环外定义变量

请看下面的两段代码:
代码1:

for(int i = 0; i < 100; ++i)
{
   CT = a;
   //do something
}
for(int i = 0; i < 100; ++i)
{
   CT = a;
   //do something
}

代码2:

for(int i = 0; i < 100; ++i)
{
   ClassTest CT = a;
   //do something
}
for(int i = 0; i < 100; ++i)
{
   ClassTest CT = a;
   //do something
}

你会觉得哪段代码的运行效率较高呢?代码1还是代码2?其实这种情况下,哪段代码的效率更高是不确定的,或者说是由这个类ClassTest本向决定的,分析如下:
对于代码1:需要调用ClassTest的构造函数1次,赋值操作函数(operator=)100次;对于代码2:需要调用(复制)构造函数100次,析构函数100次。
如果调用赋值操作函数的开销比调用构造函数和析构函数的总开销小,则第一种效率高,否则第二种的效率高。

四.一个大循环 or 多个小循环

如果循环过大,超过Cache的一页,则会不停地发生“未命中”。而若干个小循环每次都会命中。但是大循环程序跳转次数少。二者各有利弊。

五.局部变量 优于 静态变量:尽量使用局部变量

局部变量当然是存在于堆栈中,堆栈实际上也是一块内存空间。因为堆栈存放的位置比较集中,所以堆栈总是被频繁访问,于是内存中堆栈数据从内存加载到Cache中,所以访问堆栈速度快,因为总是轻易命中。而静态变量所在空间有可能并没有加载到Cache中。

六.多用final

  • 为类指定final,则类不可继承,这个类的全部方法也都是final的。
  • 为函数指定final,则派生类不可覆盖这个函数
  • 为变量指定final,若变量为引用类型,则变量引用不可更改;若变量为值类型,则变量相当于常量。

使用final的函数,有利于Java编译器进行内联,从而提高运行效率,内联对于Java运行效率意义重大,效果明显。所以多多使用final来修饰类、函数、变量

七.装箱拆箱会影响效率

使用ArrayList<Node>比使用ArrayList<Integer>效率高,因为Integer涉及到拆箱装箱。

  • 装箱:在值类型向引用类型转换时发生,在堆中分配。
  • 拆箱:拆箱在引用类型向值类型转换时发生。

可见,执行装箱操作时不可避免的要在堆上申请内存空间,并将堆栈上的值类型数据复制到申请的堆内存空间上,因此消耗内存和cpu资源。

八.尽量一次性申请大空间,不要频繁申请小空间

每次开辟空间都需要java虚拟机之外的事物来参与(其实就是操作系统参与),Java虚拟机内部速度很快,但一旦与外界打交道就变慢了。所以要尽量考虑少new 一些东西。比如ArrayList效果可能比LinkedList效果好,因为ArrayList不会每次插入删除都进行内存申请释放。
最典型的应用时“链式前向星”,它就是一次性开辟一个大的数组,来实现一个链表结构。
开辟大空间也是很浪费时间的,如下代码耗时4s

public class GetSpace {
    final int HASH_SZ = 4277777;
    final int[][] father = new int[HASH_SZ][10];
    final int[][] son = new int[HASH_SZ][10];

    public static void main(String[] args) {
        long startTiem = System.currentTimeMillis();
        new GetSpace();
        long endTime = System.currentTimeMillis();
        System.out.println((endTime - startTiem) / 1000);
    }
}
public class GetSpace {
    final int HASH_SZ = 4277777;
    final int[][] father = new int[HASH_SZ][10];
    final int[][] son = new int[HASH_SZ][10];

    public static void main(String[] args) {
        long startTiem = System.currentTimeMillis();
        new GetSpace();
        long endTime = System.currentTimeMillis();
        System.out.println((endTime - startTiem) / 1000);
    }
}

参考资料

http://chuansong.me/n/1013527https://zhuanlan.zhihu.com/p/23390311