打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
编写高质量代码:改善Java程序的151个建议

java中通用的方法和准则

1.不要让常量和变量中出现易混淆的字母

比如: long i = 1l;

别人很难一下子看清楚是11还是1l,所以应该这样写1L。

命名规范:
1.包名全部小写
2.类名首字母大写
3.方法名称,首字母小写,后面单词首字母大写
4.常量要用大写,并且用下划线隔开
5.变量要用小写

2.莫让常量蜕变成变量

    interface Const {        public static final int RANDOM_NUM = new Random().nextInt(10);    }        for (int j = 0; j < 5; j++) {            Log.i("clp", "num =" + Const.RANDOM_NUM);        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

上面的代码,每次运行值都会不一样。

RE:只有在值固定的时候,才考虑用final。否则轻易不要用。 static有时候在临时保存数据的时候会用到。

3.三元操作符的类型务必一致

        int i = 80;        String s1 = String.valueOf(i < 100 ? 90 : 100);        String s2 = String.valueOf(i < 100 ? 90 : 100.0);        Log.i("clp", "s1=" + s1);        Log.i("clp", "s2=" + s2);I/clp: s1=90I/clp: s2=90.0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

规则:
1.如果2个类型不能相互转换,就返回Object类型
2.如果是明确类型的表达式(比如变量),则int类型转为long,long类型转为float
…还有其他规则

4.避免带有边长参数的方法重载

    public int getCount(int type, int i, int j) {        return i + j;    }    public int getCount(int type, int... value) {        int count = 0;        for (int i : value) {            count *= i;        }        return count;    }        Log.i("clp", "count=" + getCount(1, 3, 4));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

上面的代码,很容易搞混,可能不分析具体原因,不知道会调用那个。
RE:其实会调用int参数的那个,因为编译器从最简单的开始。

I/clp: count=7

5.不要让null或者空值影响到边长方法

    public void methodA(String name, Integer... value) {    }    public void methodA(String name, String... value) {    }        methodA("a", 1, 2);        methodA("b", "e", "e");        methodA("c");//编译器报错        methodA("c", null);//编译器报错
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

上面解决的办法是传入空的String。
RE:我感觉最好的办法是,避免这种写法,遇到这种情况,不要去重载。

基本类型

27.谨慎包装类型的大小比较

Integer i = new Integer(2);Integer j = new Integer(2);//比较封装对象的大小,只能使用i.compareTo(j)。 //i==j,i>j,j<j都返回false。  i==j是返回对象的引用地址。i>j,j<j是值的比较。
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

28.优先使用整形池

        int input = 127;//128 555        Integer ii = new Integer(input);        Integer jj = new Integer(input);        Log.i("clp", "new 产生的对象:" + (ii == jj));//都为false        ii = input;        jj = input;        Log.i("clp", "基本类型产生的对象" + (ii == jj));//127为true,128 555为false        ii = Integer.valueOf(input);        jj = Integer.valueOf(input);        Log.i("clp", "valueOf 产生的对象" + (ii == jj));//127为true,128 555为false
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

1.new产生的对象比较,肯定为false
2.自动装箱和Integer.valueOf一样(装箱时通过valueOf实现)
3.valueOf的实现方式,在-128和127之间,使用的是缓冲。超过的话,直接new

    public static Integer valueOf(int i) {        final int offset = 128;        if (i > -128 && i < 127) {            return IntegerCache.cache[i + offset];        }        return new Integer(i);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

29.优先使用基本类型

        int i = 14;        f(i);        f(Integer.valueOf(i));    public static void f(long i) {        Log.i("clp", "基本类型的方法被调用");    }    public static void f(Long i) {        Log.i("clp", "包装的方法被调用");    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

输出结果:
Log.i(“clp”, “基本类型的方法被调用”);
Log.i(“clp”, “基本类型的方法被调用”);

解析:1.自动装箱只有在赋值时才有关系,重载可以编译通过
2.f(i)不会报错,因为编辑器会自动把i加宽,变为long
3.f(Integer.valueOf(i))不会报错:基本类型,可以先加宽在包装。但是不能直接包装。所以1.先转变为Integer对象,然后调用方法的时候发现没有,自动转换为int基本类型 3.发现没有,自动加宽变为long
4.如果把f(long i)方法给注释掉,代码是编译不通过的

30.不要随便设置随机种子

Random random = new Random(1000);        for (int i = 0; i < 4; i++) {            CldLog.i("clp", "第" + i + "次的随机数为:" + random.nextInt());        }
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

RE:按照上面的代码写,每次运行都会得到想通的随机数。通过Random random = new Random(),则不能固定范围
04-15 11:35:44.565 15244-15244/cld.navi.mainframe I/clp: 第0次的随机数为:-1244746321 @initControls:CldModeA.Java(72)
04-15 11:35:44.565 15244-15244/cld.navi.mainframe I/clp: 第1次的随机数为:1060493871 @initControls:CldModeA.java(72)
04-15 11:35:44.565 15244-15244/cld.navi.mainframe I/clp: 第2次的随机数为:-1826063944 @initControls:CldModeA.java(72)
04-15 11:35:44.565 15244-15244/cld.navi.mainframe I/clp: 第3次的随机数为:1976922248 @initControls:CldModeA.java(72)

正确写法如下:

        Random random = new Random();        for (int i = 0; i < 4; i++) {            CldLog.i("clp", "第" + i + "次的随机数为:" + random.nextInt(20));        }
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

类、对象及方法

31.接口中不要存在实现代码

RE:这个都知道

32.静态变量一定要先声明后赋值

    static {        i = 100;    }    public static int i = 1;    //输出结果,i等于1    public static int i = 1;    static {        i = 100;    }    //输出结果,i等于100
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

1.静态变量是类加载的时候分配到存储区的
2.静态变量在类初始化的时候首先被加载,分配地址空间,还没赋值
3.按照静态类和静态块的先后顺序赋值,所以谁的位置在后面,谁有最终的决定权

33.不要复写静态方法

public class Base {    public static void doBaseStatic() {        Log.i("clp", "doBaseStatic");    }    public void doBase() {        Log.i("clp", "doBase No Static");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
public class Sub extends Base {    public static void doBaseStatic() {        Log.i("clp", "do Sub Static");    }    public void doBase() {        Log.i("clp", "do Sub No Static");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
        Base mBase = new Sub();        mBase.doBase();//do Sub No Static        Base.doBaseStatic();//doBaseStatic        mBase.doBaseStatic();//doBaseStatic        //Android Studio用上面的写法会提示        Sub.doBaseStatic();//do Sub Static
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

1.mBase.doBaseStatic()实际上调用的是父类的方法。因为静态方法不依赖实例对象,是通过类名访问的

34.构造函数尽量简化

public abstract class Server {    public static int DEFAULT_PORT = 4000;    public Server() {        Log.i("clp", "Server");        int port = getPort();        Log.i("clp", "port=" + port);//        Log.i();    }    protected abstract int getPort();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
public class ServerIml extends Server {    public int port = 100;    public ServerIml(int port) {        Log.i("clp", "ServerIml");        this.port = port;    }    @Override    protected int getPort() {//        return port;//值为0        return DEFAULT_PORT;//返回值4000    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
ServerIml serverIml = new ServerIml(80);
  • 1
  • 1

这个跟构造方法的执行顺序有关。
04-15 14:35:54.027 7381-7381/com.example.demo I/clp: Server
04-15 14:35:54.027 7381-7381/com.example.demo I/clp: port=4000
04-15 14:35:54.027 7381-7381/com.example.demo I/clp: ServerIml

执行顺序如下:
1.子类的构造函数接收int类型的参数(没有赋值)
2.父类初始化常量,也就是DEFAULT_PORT,并赋值为4000
3.执行父类无参的构造方法
4.调用int port = getPort();获取端口号 ,调用子类端口号的实现方法。此时DEFAULT_PORT已经有值,但是port子类的
构造函数还没有执行,所以没有赋值,返回0
5.父类初始化完毕,开始初始化子类
6.子类被赋值为100
7.执行子类的构造函数,重新赋值为80

35.避免在构造函数中初始化其他类

public class Son extends Father {    public void doSomeThing() {        Log.i("clp", "doSomeThing");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
public class Father {    Father() {        new Other();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
public class Other {    Other() {        new Son();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
Son son = new Son();        son.doSomeThing();
  • 1
  • 2
  • 1
  • 2

上面的代码,直接回导致程序死掉。陷入死循环。

36.使用构造代码块精炼程序

代码块:
1.使用static{}的静态代码块。类创建的时候加载
2.方法后面{}的普通代码块,必须通过调用方法名称使用
3.synchronize修饰的同步代码块
4.构造代码块,在类中没有任何修饰,直接使用{}

public class CldGouzaoUtil {    {        Log.i("clp", "构造代码块");    }    public CldGouzaoUtil() {        Log.i("clp", "构造方法");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

04-15 14:59:55.597 22529-22529/com.example.demo I/clp: 构造代码块
04-15 14:59:55.597 22529-22529/com.example.demo I/clp: 构造方法

构造代码块,不同于static代码块,它是依赖于构造方法执行。所以用途主要为
1.初始化实例变量
2.初始化实例环境
RE:2个都是在构造方法执行前执行一些初始化、检测的操作。

37.构造代码块会想你所想

public class CldGouzaoUtil {    protected static int numOfNew = 0;    {        numOfNew++;    }    public CldGouzaoUtil() {    }    public CldGouzaoUtil(String mDes) {        this();    }    public static int getNumOfNew() {        return numOfNew;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
        new CldGouzaoUtil();        new CldGouzaoUtil("haha");        new CldGouzaoUtil("haha");        Log.i("clp", "初始化的次数为:" + CldGouzaoUtil.getNumOfNew());
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

04-15 15:31:37.737 19985-19985/com.example.demo I/clp: 初始化的次数为:3

new CldGouzaoUtil(“haha”)这种构造函数调用this的,构造代码块也只会执行一次。而且只要不杀死程序,次数会一直保留。

38.使用静态内部类提高封装性

//使用静态内部类加强类的封装性和提高代码的可读性public class CldInnerStatic {    //提高封装性-比如这个代表home是外部类的子行为    //提高代码的可读性    //形似内部,神似外部---可以脱离外部存在    String name;    public Home home;    private static int outer = 100;    public CldInnerStatic(String name) {        this.name = name;    }    public void setHome(Home home) {        this.home = home;    }    //不持有外部类的引用    //静态内部类不依赖外部类    public static class Home {//静态内部类        public String addr;//地址        public int tel;//家庭电话        public Home(String addr, int tel) {            this.addr = addr;            this.tel = tel;//            name = "哈哈";//报错-不能访问外部类的变量            outer = 80;//静态内部类可以访问外部类static的变量        }    }    public class Company {//        public static int comAddr = "";//报错-普通内部类不能声明static的方法和变量        //报错//        public static void setComName() {//普通内部类不能声明static的方法和变量////        }    }
  • 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
  • 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
CldInnerStatic innerStatic = new CldInnerStatic("张三");innerStatic.setHome(new CldInnerStatic.Home("上海", 02166666666));
  • 1
  • 2
  • 1
  • 2

39.使用匿名内部类的构造函数

        List<String> mList = new ArrayList<>();        List<String> mList2 = new ArrayList<>() {        };        List<String> mList3 = new ArrayList<>() {            {//代码块            }        };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

第二种和第三种都是相当于匿名内部类List mlist2 = new Sub();

40.匿名类的构造函数很特殊

public class CldCalculator {    {//在这里,计算出来的值就为0,在使用的地方就是正确的        setOperator();        Log.i("clp", "setOperator");    }    private int i, j, result;    public CldCalculator() {    }    public CldCalculator(int i, int j) {        Log.i("clp", "CldCalculator");        this.i = i;        this.j = j;    }    protected void setOperator() {        result = i + j;    }    public int getResult() {        return result;    }}
  • 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
  • 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
public class SubCal extends CldCalculator {    {//在这里,计算出来的值就为0,在使用的地方就是正确的        setOperator();        Log.i("clp", "setOperator");    }    public SubCal(int i, int j) {    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
        CldCalculator calculator = new CldCalculator(1, 2) {//            {//                setOperator();//这里使用,值是正确的//                Log.i("clp", "setOperator");//            }        };        Log.i("clp", "result=" + calculator.getResult());        CldCalculator calculator1 = new SubCal(1, 2);        Log.i("clp", "calculator1=" + calculator.getResult());//结果为0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

两个结果不一样,因为匿名内部类的特殊处理机制。一般的类都是默认调用父类的无参构造。但是匿名类没有名字,直接由
构造代码块代替,直接调用了父类的同参构造方法。然后再调用自己的构造代码块。

41.让多继承成为现实

使用内部类,巧妙的实现多继承

public interface Father {    int Strong();//强壮指数}public interface Mother {    int kind();//温柔指数}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
public class FatherIml implements Father {    @Override    public int Strong() {        return 8;    }}public class MotherIml implements Mother {    @Override    public int kind() {        return 8;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
public class Son extends FatherIml implements Mother {    @Override    public int Strong() {        return super.Strong() + 2;//比付清还要强壮    }    @Override    public int kind() {        return new MotherIml() {            @Override            public int kind() {                return super.kind() - 2;//温柔比母亲少了2个点            }        }.kind();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
        Son son = new Son();        Log.i("clp", "strong =" + son.Strong() + ";kind=" + son.kind());        //结果:04-15 17:32:13.267 14610-14610/? I/clp: strong =10;kind=6
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

42.让工具类不可实例化

工具类的正确构建方式:1.不能实例化 2.不能继承

public final class CldUtil {    private CldUtil() {        throw new Error("不要实例化我");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

43.避免对象的浅拷贝

public class Person implements Cloneable {    //名称    private String name;    //父亲    private Person father;    public Person(String name) {        this.name = name;    }    public Person(String name, Person father) {        this.name = name;        this.father = father;    }    public void setName(String name) {        this.name = name;    }    public void setFather(Person father) {        this.father = father;    }    public String getName() {        return name;    }    public Person getFather() {        return father;    }    @Override    public Person clone() {//对象的拷贝        Person p = null;        try {            p = (Person) super.clone();            p.setFather(new Person(p.getFather().getName()));//如果没有这句话,就是浅拷贝,加了这句话,就是深拷贝        } catch (CloneNotSupportedException e) {            e.printStackTrace();        }        return p;    }}
  • 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
  • 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
        Person father = new Person("父亲");        Person p1 = new Person("大儿子", father);        Person p2 = p1.clone();        p2.setName("小儿子");//        p2.setFather(new Person("干爹"));        p2.getFather().setName("干爹");        Log.i("clp", "fist name=" + p1.getName() + ";father =" + p1.getFather().getName());        Log.i("clp", "sec name=" + p2.getName() + ";father =" + p2.getFather().getName());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

使用浅拷贝
04-15 17:55:30.827 2810-2810/com.example.demo I/clp: fist name=大儿子;father =干爹
04-15 17:55:30.827 2810-2810/com.example.demo I/clp: sec name=小儿子;father =干爹
使用p2.setFather(new Person(“干爹”));或者深拷贝
04-15 17:58:27.197 2810-2810/com.example.demo I/clp: fist name=大儿子;father =父亲
04-15 17:58:27.197 2810-2810/com.example.demo I/clp: sec name=小儿子;father =干爹

44.推荐使用序列化实现对象的拷贝

public class Person implements Serializable {    private static final long serialVersionUID = 201704161112L;    //名称    public String name;    //父亲    public Person person;    public Person(String name) {        this.name = name;    }    public Person(String name, Person person) {        this.name = name;        this.person = person;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Person getPerson() {        return person;    }    public void setPerson(Person person) {        this.person = person;    }}
  • 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
  • 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 static <T extends Serializable> T clone(T obj) {        //final和static的序列化问题会被引入到对象的拷贝中???        //transient变量也会影响到拷贝的结果???        T cloneObj = null;        try {            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();            ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);            objectOutputStream.writeObject(obj);//如果不实现序列化,则会抛出序列化异常            objectOutputStream.close();            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());            ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);            cloneObj = (T) objectInputStream.readObject();            objectInputStream.close();        } catch (Exception e) {            e.printStackTrace();        }        return cloneObj;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

01-04 19:27:44.620 17104-17104/old.pkg.com.myapplicati I/clp: 名称:小儿子;父亲为:父亲
01-04 19:27:44.620 17104-17104/old.pkg.com.myapplicati I/clp: 名称:大儿子;父亲为:干爹

45.复写equals方法时不要识别不出自己

public class Person {    //名称    public String name;    public Person(String name) {        this.name = name;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public boolean equals(Object obj) {        if (obj instanceof Person) {            Person person = (Person) obj;            //equalsIgnoreCase为忽略大小写            return name.equalsIgnoreCase(person.getName());//            return name.equalsIgnoreCase(person.getName().trim());//            01-04 19:41:01.360 30991-30991/old.pkg.com.myapplicati I/clp: p1 is equals =false////            return name.equalsIgnoreCase(person.getName());//            01-04 19:41:48.420 32459-32459/old.pkg.com.myapplicati I/clp: p1 is equals =true        }        return super.equals(obj);    }}
  • 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
  • 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
        Person p1 = new Person("张三  ");        Log.i("clp", "p1 is equals =" + p1.equals(p1));
  • 1
  • 2
  • 1
  • 2

使用:
return name.equalsIgnoreCase(person.getName().trim());
01-04 19:41:01.360 30991-30991/old.pkg.com.myapplicati I/clp: p1 is equals =false

return name.equalsIgnoreCase(person.getName());
01-04 19:41:48.420 32459-32459/old.pkg.com.myapplicati I/clp: p1 is equals =true

46.equals应该考虑null值情景

接上面的例子,如果按照下面的方式判断,就会报错

        Person p1 = new Person("张三  ");        Person p2 = new Person(null);        Log.i("clp", "p1 p2 is equals =" + p2.equals(p1));
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

所以,应该按照下面的写:

    @Override    public boolean equals(Object obj) {        if (obj instanceof Person) {            Person person = (Person) obj;            if (person.getName() == null || name == null) {                return false;            }            //equalsIgnoreCase为忽略大小写            return name.equalsIgnoreCase(person.getName());//            return name.equalsIgnoreCase(person.getName().trim());//            01-04 19:41:01.360 30991-30991/old.pkg.com.myapplicati I/clp: p1 is equals =false////            return name.equalsIgnoreCase(person.getName());//            01-04 19:41:48.420 32459-32459/old.pkg.com.myapplicati I/clp: p1 is equals =true        }        return super.equals(obj);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

47.在equals中使用getClass进行类型判断

接上面例子,如果有一个子类,代码如下:

public class ManPerson extends Person {    public int id;    public ManPerson(String name, int id) {        this.name = name;        this.id = id;    }    @Override    public boolean equals(Object obj) {        if (obj instanceof ManPerson) {            ManPerson person = (ManPerson) obj;            return super.equals(obj) && person.id == id;        }        return super.equals(obj);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

然后进行判断:

        Person p1 = new Person("张三");        ManPerson manPerson = new ManPerson("张三", 100);        Log.i("clp", "p1 manPerson is equals =" + p1.equals(manPerson));
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

结果是true。

这是因为p1为父类,所以使用了父类的equals方法进行判断。关键就是equals方法中使用了instanceof。故修改如下:

    @Override    public boolean equals(Object obj) {        if (obj.getClass() != null && obj.getClass() == this.getClass()) {            Person person = (Person) obj;            if (person.getName() == null || name == null) {                return false;            }            //equalsIgnoreCase为忽略大小写            return name.equalsIgnoreCase(person.getName());//            return name.equalsIgnoreCase(person.getName().trim());//            01-04 19:41:01.360 30991-30991/old.pkg.com.myapplicati I/clp: p1 is equals =false////            return name.equalsIgnoreCase(person.getName());//            01-04 19:41:48.420 32459-32459/old.pkg.com.myapplicati I/clp: p1 is equals =true        }        return super.equals(obj);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

48.覆写equals方法必须覆写hashCode方法

        Map<Person, Object> map = new HashMap<Person, Object>() {            {                put(new Person("张三"), new Object());            }        };        Log.i("clp", "map has key=" + map.containsKey(new Person("张三")));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

上面方法,如果不覆写hashCode,会返回false。

因为map是根据hashCode值决定数组的下标(HashMap底层是通过数组保存的)。如果不覆写,每一个对象都有一个hasCode,自然会找不到

所以Person类中添加如下方法:

    @Override    public int hashCode() {        return name.hashCode();    }
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

49.推荐覆写toString()

原因是本身,此此方法返回的是对象引用的地址值。
01-04 21:29:51.320 27893-27893/old.pkg.com.myapplicati I/clp: person =newpkg.pkg.com.myapplication2.algorithm.Person@bd2e9

        Person person = new Person("张三");        Log.i("clp", "person =" + person.toString());
  • 1
  • 2
  • 1
  • 2

01-04 21:29:12.490 26591-26591/old.pkg.com.myapplicati I/clp: person =name =张三

50.使用package-info类为包服务

作用:
1.声明友好类和包内访问常量
2.为在包上标注注解提供便利
3.提供包的整体注释说明

51.不要主动进行垃圾回收

因为System.gc会停止所有的相应,去进行检测内存中是否有可回收的对象。可能会比较耗时,10S或者20S也说不准。

字符串

52.推荐使用String直接量赋值

        String s1 = "张三";        String s2 = " 张三 ";        String s3 = new String("张三");        String s4 = s3.intern();        Log.i("clp", " s1 s2 is equals=" + (s1 == s2));        Log.i("clp", " s1 s3 is equals=" + (s1 == s3));        Log.i("clp", " s1 s4 is equals=" + (s1 == s4));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

01-04 21:51:49.220 19875-19875/old.pkg.com.myapplicati I/clp: s1 s2 is equals=false
01-04 21:51:49.220 19875-19875/old.pkg.com.myapplicati I/clp: s1 s3 is equals=false
01-04 21:51:49.220 19875-19875/old.pkg.com.myapplicati I/clp: s1 s4 is equals=true

Java为了避免产生大量的对象,内部有一个字符串池。创建字符串时,会先判断池子中是否有这个字符串,有就直接返回,
如果没有,就创建一个,丢到池子当中,并返回。
使用new 创建,肯定是新建了一个对象。
intern方法会检查当前的对象,在池子中是否有字面值相等的引用对象,如果有就返回池子中的对象,没有就放置到池子中,并返回。

会不会有线程安全问题?
String是一个不可变对象。
1.不可能有子类
2.String中提供的方法中的返回值,都会新建一个对象,不会对原对象进行修改。

要不要考虑垃圾回收?
字符串池,是保存子JVM的常量池当中,不会进行垃圾回收。

53.注意参数中传递的参数要求

    //删除字符串    public static String remove(String orgin, String sub) {        return orgin.replaceAll(sub, "");    }
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4
        Log.i("clp", "result =" + CldStringUtil.remove("好是好", "好"));        Log.i("clp", "result =" + CldStringUtil.remove("$是$", "$"));
  • 1
  • 2
  • 1
  • 2

结果:
01-04 22:08:33.220 6359-6359/old.pkg.com.myapplicati I/clp: result =是
01-04 22:08:33.220 6359-6359/old.pkg.com.myapplicati I/clp: result =

replaceAll要求第一个参数是正则表达式。$这个符号在正则表达式中是字符串的结束位置。

修改如下:

    //删除字符串    public static String remove(String orgin, String sub) {        while (orgin.contains(sub)) {            orgin = orgin.replace(sub, "");        }        return orgin;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

54.正确使用String、StringBuffer、StringBuilder

1.String一旦创建,不可变
2.StringBuffer是线程安全的,StringBuilder是线程不安全的。
所以StringBuffer的性能要比StringBuilder低。
3.性能方法,因为String每次都会新建,所以会远慢于StringBuffer和StringBuilder

55.注意字符串的位置

        String s1 = 1 + 2 + "abc";        String s2 = "abc" + 1 + 2;        Log.i("clp", "s1=" + s1);        Log.i("clp", "s2=" + s2);
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

01-05 06:40:30.540 28819-28819/old.pkg.com.myapplicati I/clp: s1=3abc
01-05 06:40:30.540 28819-28819/old.pkg.com.myapplicati I/clp: s2=abc12

1.从左到右依次拼接
2.只要遇到String,则所有数据都转换为String类进行拼接
3.如果是原始数据,则直接进行拼接,如果是对象,则调用toString()
4.在+表达式中,String具有最高的优先级

56.自由选择字符串拼接方法

    String s = "";    long time = System.currentTimeMillis();    for (int i = 0; i < 5000; i++) {        s += "a";    }    long timeend = System.currentTimeMillis() - time;    Log.i("clp", "time1 =" + timeend);    String a = "";    long time2 = System.currentTimeMillis();    for (int i = 0; i < 5000; i++) {        s = s.concat("a");    }    long timeend2 = System.currentTimeMillis() - time2;    Log.i("clp", "time1 =" + timeend2);    StringBuilder b = new StringBuilder();    long time3 = System.currentTimeMillis();    for (int i = 0; i < 5000; i++) {        b.append("a");    }    long timeend3 = System.currentTimeMillis() - time3;    Log.i("clp", "time1 =" + timeend3);    StringBuffer c = new StringBuffer();    long time4 = System.currentTimeMillis();    for (int i = 0; i < 5000; i++) {        c.append("a");    }    long timeend4 = System.currentTimeMillis() - time4;    Log.i("clp", "time1 =" + timeend4);

01-05 06:53:02.410 3080-3080/old.pkg.com.myapplicati I/clp: time1 =1160
01-05 06:53:04.070 3080-3080/old.pkg.com.myapplicati I/clp: time1 =1668
01-05 06:53:04.080 3080-3080/old.pkg.com.myapplicati I/clp: time1 =5
01-05 06:53:04.090 3080-3080/old.pkg.com.myapplicati I/clp: time1 =8

书上说concat执行时间比直接添加少一倍。但是在我手机上测试为啥还要高???

57.推荐在复杂字符串操作中使用正则表达式

//使用正则表达式计算单词的数量    public static int getCount(String word) {        //正在表达式        Pattern pattern = Pattern.compile("\\b\\w+\\b");        Matcher matcher = pattern.matcher(word);        int count = 0;        while (matcher.find()) {            count++;        }        return count;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
        String s = "How are you !Good!";        Log.i("clp", "count=" + CldStringUtil.getCount(s));
  • 1
  • 2
  • 1
  • 2

01-05 07:10:17.220 8910-8910/old.pkg.com.myapplicati I/clp: count=4

58.强烈建议使用UTF编码

文件格式为UTF-8

        try {            String s = "张三";            byte[] b = s.getBytes("GBK");            Log.i("clp", "string is =" + new String(b));            Log.i("clp", "string is =" + new String(b, "GBK"));        } catch (Exception e) {        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

输出结果:
01-05 07:20:10.370 11249-11249/old.pkg.com.myapplicati I/clp: string is =????
01-05 07:20:10.370 11249-11249/old.pkg.com.myapplicati I/clp: string is =张三

59.对字符串排序持一种宽容的态度

1.使用Arrays.sort肯定不行,它是根据UNICODE代码表来排序的。

2.使用Collator,但是只能包含常用的7000多个汉字。

        String[] sortName = {"张三", "李四", "王五"};        Comparator c = Collator.getInstance(Locale.CHINA);        Arrays.sort(sortName, c);        for (String str : sortName) {            Log.i("clp", "name =" + str);        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

原因:java使用UNICODE编码。
GB2312->GB18030->UNICODE

数组和集合

60.性能考虑,数组是首选

    //基本类型是在栈内操作的。而对象是在堆内操作的。    //栈的特点是:速度快、容量小   堆的特点是:速度慢,容量大。    public static int getCountArray(int[] list) {        int sum = 0;        long time = System.currentTimeMillis();        for (int i = 0; i < list.length; i++) {            sum += list[i];        }        long timeend = System.currentTimeMillis() - time;        Log.i("clp", "array time is =" + timeend);        return sum;    }    public static int getCountList(List<Integer> list) {        int sum = 0;        long time = System.currentTimeMillis();        for (int i = 0; i < list.size(); i++) {            sum += list.get(i);//做了一次拆箱操作。Integer通过intValue方法自动转换为了int基本类型        }        long timeend = System.currentTimeMillis() - time;        Log.i("clp", "data time is =" + timeend);        return sum;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
        int[] data = new int[100000];        for (int i = 0; i < data.length; i++) {            data[i] = 2;        }        CldArrayUtil.getCountArray(data);        List<Integer> list = new ArrayList<Integer>();        for (int j = 0; j < 100000; j++) {            list.add(2);        }        CldArrayUtil.getCountList(list);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

01-05 08:13:19.740 21326-21326/old.pkg.com.myapplicati I/clp: array time is =4
01-05 08:13:19.880 21326-21326/old.pkg.com.myapplicati I/clp: data time is =35
数组的效率是集合的10倍。这个说法是对的。

61.若有必要,使用变长数组

    public static int[] expandArray(int[] data, int length) {//指定新的数组长度        if (length < 0) {            length = 0;        }        return Arrays.copyOf(data, length);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
        int[] data = new int[20];        for (int i = 0; i < data.length; i++) {            data[i] = 2;        }        data = CldArrayUtil.expandArray(data, 50);        Log.i("clp", "length =" + data.length);        Log.i("clp", "data[2]=" + data[2]);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

01-05 08:26:57.310 23679-23679/old.pkg.com.myapplicati I/clp: length =50
01-05 08:26:57.310 23679-23679/old.pkg.com.myapplicati I/clp: data[2]=2

62.警惕数组的浅拷贝

Arrays.copyOf为数组的浅拷贝,如果是基本类型,直接拷贝值。如果是对象,拷贝的是引用地址。
所以如果改变拷贝后的对象的值,拷贝前的值也会发生变化。

解决方式:重新生成对象。

63.在明确的场景下,为集合指定初始容量

1.例如ArrayList,如果不指定,初始容量为10.每次如果长度不够,就自动扩容到1.5倍。
所以,如果要指定一个长度为50的List,最好直接指定容量

        List<String> mlist = new ArrayList<>(50);        for (int i = 0; i < 60; i++) {//列表长度为50,添加60个元素            mlist.add("2");        }        Log.i("clp", "list size =" + mlist.size());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

01-05 19:23:08.320 4367-4367/old.pkg.com.myapplicati I/clp: list size =60

Vector、HashMap、Stack类似。

64.多种最值算法,适时选择

        int[] data = new int[100000];        for (int i = 0; i < data.length; i++) {            data[i] = new Random().nextInt(10000);        }        //使用数组排序        int max = data[0];        long time = System.currentTimeMillis();        for (int i : data) {            max = max > data[i] ? max : data[i];        }        long timeEnd = System.currentTimeMillis() - time;        Log.i("clp", "timeEnd=" + timeEnd);        Log.i("clp", "max data = " + max);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

01-05 19:35:47.690 8007-8007/old.pkg.com.myapplicati I/clp: timeEnd=6
01-05 19:35:47.690 8007-8007/old.pkg.com.myapplicati I/clp: max data = 9999

        int[] data = new int[100000];        for (int i = 0; i < data.length; i++) {            data[i] = new Random().nextInt(10000);        }        //使用Arrays排序        int max = data[0];        long time = System.currentTimeMillis();        Arrays.sort(data.clone());        long timeEnd = System.currentTimeMillis() - time;        Log.i("clp", "timeEnd=" + timeEnd);        Log.i("clp", "max data = " + data[data.length - 1]);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

01-05 19:37:36.810 9440-9440/old.pkg.com.myapplicati I/clp: timeEnd=172
01-05 19:37:36.810 9440-9440/old.pkg.com.myapplicati I/clp: max data = 9281

排序10万个数据,才有细微的差别。所以,小数据性能影响不大。用Arrays.sort排序代码可读性高一点。

求第二大元素,正常通过数据循环查找可以搞定,下面使用TreeSet实现(过滤掉相同数据).

        long time = System.currentTimeMillis();        List<Integer> mList = Arrays.asList(data);        TreeSet<Integer> ts = new TreeSet<Integer>(mList);        int max = ts.lower(ts.last());        long timeEnd = System.currentTimeMillis() - time;        Log.i("clp", "timeEnd=" + timeEnd);        Log.i("clp", "sec max data = " + max);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

01-05 19:42:18.750 10551-10551/old.pkg.com.myapplicati I/clp: timeEnd=1092
01-05 19:42:18.750 10551-10551/old.pkg.com.myapplicati I/clp: sec max data = 9998

65.避开基本类型数组转换列表陷进

        int[] data = new int[100000];        for (int i = 0; i < data.length; i++) {            data[i] = new Random().nextInt(10000);        }        List mList = Arrays.asList(data);        Log.i("clp", "list size =" + mList.size());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

01-05 19:50:27.710 13585-13585/old.pkg.com.myapplicati I/clp: list size =1

asList需要输入一个泛型化的边长参数,基本类型是不能泛型化的,但是数组可以。所以上面就把数组泛型化为只有一个数组的。

mList.get(0)数据如下:

其他7个基本类型也有这个问题,所以如果要用,请使用包装类型。

66.asList方法产生的List对象不可更改

        Integer[] data = new Integer[5];        for (int i = 0; i < 5; i++) {            data[i] = new Random().nextInt(10);        }        List<Integer> mList = Arrays.asList(data);        mList.add(2);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
java.lang.RuntimeException: Unable to start activity ComponentInfo{old.pkg.com.myapplicati/newpkg.pkg.com.myapplication2.MainActivity}: java.lang.UnsupportedOperationExceptionat android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2279)at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2329)at android.app.ActivityThread.access$1100(ActivityThread.java:141)at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1242)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

因为通过asList产生的List不同于正常使用的List,不能使用add remove方法。
只有size toArray get set contains五个方法可以用。

List mList = Arrays.asList(1, 2, 3);这种方式生成数组有巨大的隐患。

67.不同的列表选择不同的遍历方法

        new Thread(new Runnable() {            @Override            public void run() {                List<Integer> mList = new ArrayList<>();                for (int i = 0; i < 80 * 100; i++) {                    mList.add(new Random().nextInt(200));                }                long start = System.currentTimeMillis();                int average = CldArrayUtil.average(mList);                Log.i("clp", "time =" + (System.currentTimeMillis() - start) + "ms");                Log.i("clp", "average=" + average);                List<Integer> mList2 = new LinkedList<>();                for (int i = 0; i < 80 * 100; i++) {                    mList2.add(new Random().nextInt(200));                }                long start2 = System.currentTimeMillis();                int average2 = CldArrayUtil.average(mList2);                Log.i("clp", "time2 =" + (System.currentTimeMillis() - start2) + "ms");                Log.i("clp", "average2=" + average2);            }        }).start();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

使用fori循环

    //求平局数    public static int average(List<Integer> list) {        int sum = 0;        for (int i = 0; i < list.size(); i++) {            sum += list.get(i);        }        return sum / list.size();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

01-05 21:07:22.310 2140-2188/old.pkg.com.myapplicati I/clp: time =8ms
01-05 21:07:22.310 2140-2188/old.pkg.com.myapplicati I/clp: average=98
01-05 21:07:23.280 2140-2188/old.pkg.com.myapplicati I/clp: time2 =929ms
01-05 21:07:23.280 2140-2188/old.pkg.com.myapplicati I/clp: average2=98

8000个就是快1S,80万个就是100S!!!!

    //求平局数    public static int average(List<Integer> list) {        int sum = 0;//        for (int i = 0; i < list.size(); i++) {//            sum += list.get(i);//        }        for (Integer i : list) {            sum += i;        }        return sum / list.size();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

01-05 21:09:06.990 3137-3185/old.pkg.com.myapplicati I/clp: time =5ms
01-05 21:09:06.990 3137-3185/old.pkg.com.myapplicati I/clp: average=99
01-05 21:09:07.030 3137-3185/old.pkg.com.myapplicati I/clp: time2 =9ms
01-05 21:09:07.030 3137-3185/old.pkg.com.myapplicati I/clp: average2=98

在手机上测试,使用foreach方法,消耗的时间差不多???

ArrayList实现了随机读取(RandomAcess),而LinkedList实现了迭代器。

书上建议通过判断是否有RandomAcess

    //求平局数    public static int average(List<Integer> list) {        int sum = 0;        if (list instanceof RandomAccess) {//针对ArrayList可以随机读取的            for (int i = 0; i < list.size(); i++) {                sum += list.get(i);            }        } else {//针对LinkedList这种            for (Integer i : list) {                sum += i;            }        }        return sum / list.size();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

01-05 21:11:53.070 4250-4304/old.pkg.com.myapplicati I/clp: time =4ms
01-05 21:11:53.070 4250-4304/old.pkg.com.myapplicati I/clp: average=98
01-05 21:11:53.130 4250-4304/old.pkg.com.myapplicati I/clp: time2 =12ms
01-05 21:11:53.130 4250-4304/old.pkg.com.myapplicati I/clp: average2=98

68.频繁插入和删除时用LinkedList

ArrayList和LinkedList性能比较

添加:

        new Thread(new Runnable() {            @Override            public void run() {                List<String> mArrayList = new ArrayList<String>();                for (int i = 0; i < 10; i++) {                    mArrayList.add("a");                }                long start = System.currentTimeMillis();                for (int i = 0; i < 100000; i++) {                    mArrayList.add(2, "a");                }                Log.i("clp", "array list time =" + (System.currentTimeMillis() - start) + "ms");                List<String> mLinkedList = new LinkedList<String>();                for (int i = 0; i < 10; i++) {                    mLinkedList.add("a");                }                long start2 = System.currentTimeMillis();                for (int i = 0; i < 100000; i++) {                    mLinkedList.add(2, "a");                }                Log.i("clp", "linked list time =" + (System.currentTimeMillis() - start2) + "ms");            }        }).start();
  • 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
  • 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

01-05 21:34:13.060 10127-10170/old.pkg.com.myapplicati I/clp: array list time =15847ms
01-05 21:34:13.170 10127-10170/old.pkg.com.myapplicati I/clp: linked list time =116ms

10万个数据的话,相差100倍。

删除:

        new Thread(new Runnable() {            @Override            public void run() {                List<String> mArrayList = new ArrayList<String>();                for (int i = 0; i < 10 * 10000; i++) {                    mArrayList.add("a");                }                long start = System.currentTimeMillis();                while (mArrayList.size() > 0){                    mArrayList.remove(0);                }                Log.i("clp", "array list time =" + (System.currentTimeMillis() - start) + "ms");                List<String> mLinkedList = new LinkedList<String>();                for (int i = 0; i < 10 * 10000; i++) {                    mLinkedList.add("a");                }                long start2 = System.currentTimeMillis();                while (mLinkedList.size() > 0) {                    mLinkedList.remove(0);                }                Log.i("clp", "linked list time =" + (System.currentTimeMillis() - start2) + "ms");            }        }).start();
  • 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
  • 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

01-05 21:37:58.600 11330-11372/old.pkg.com.myapplicati I/clp: array list time =15108ms
01-05 21:37:58.700 11330-11372/old.pkg.com.myapplicati I/clp: linked list time =13ms

删除,性能相差1000倍。

修改:

        new Thread(new Runnable() {            @Override            public void run() {                List<String> mArrayList = new ArrayList<String>();                for (int i = 0; i < 10 * 10000; i++) {                    mArrayList.add("a");                }                long start = System.currentTimeMillis();                for (int i = 0; i < 10 * 10000; i++) {                    mArrayList.set(10000, "b");                }                Log.i("clp", "array list time =" + (System.currentTimeMillis() - start) + "ms");                List<String> mLinkedList = new LinkedList<String>();                for (int i = 0; i < 10 * 10000; i++) {                    mLinkedList.add("a");                }                long start2 = System.currentTimeMillis();                for (int i = 0; i < 10 * 10000; i++) {                    mLinkedList.set(10000, "b");                }                Log.i("clp", "linked list time =" + (System.currentTimeMillis() - start2) + "ms");            }        }).start();
  • 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
  • 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

01-05 21:40:26.700 12467-12523/old.pkg.com.myapplicati I/clp: array list time =28ms
01-05 21:40:55.580 12467-12523/old.pkg.com.myapplicati I/clp: linked list time =28672ms

总结:频繁的删除和插入,使用LinkedList。 如果频繁的获取和修改。使用ArrayList

69.列表相等只需关系元素数据

判断集合是否相等时,只需要判断元素是否相等即可。

        ArrayList<String> mList = new ArrayList<String>();        mList.add("a");        Vector<String> mVList = new Vector<String>();        mVList.add("a");        Log.i("clp", "is equals =" + mList.equals(mVList));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

01-06 06:56:50.610 25692-25692/old.pkg.com.myapplicati I/clp: is equals =true

equals是在AbstractList中实现的,代码如下:

    public boolean equals(Object o) {        if (o == this)            return true;        if (!(o instanceof List))            return false;        ListIterator<E> e1 = listIterator();        ListIterator e2 = ((List) o).listIterator();        while (e1.hasNext() && e2.hasNext()) {            E o1 = e1.next();            Object o2 = e2.next();            if (!(o1==null ? o2==null : o1.equals(o2)))                return false;        }        return !(e1.hasNext() || e2.hasNext());    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

70.子列表只是原列表的一个视图

        List<String> mlist = new ArrayList<String>();        mlist.add("A");        mlist.add("B");        ArrayList<String> mBList = new ArrayList<>(mlist);        List<String> mCList = mlist.subList(0, mlist.size());        mCList.add("C");        Log.i("clp", "mlist mBlist is equals =" + mlist.equals(mBList));        Log.i("clp", "mlist mCList is equals =" + mlist.equals(mCList));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

01-06 07:18:39.990 29976-29976/old.pkg.com.myapplicati I/clp: mlist mBlist is equals =false
01-06 07:18:39.990 29976-29976/old.pkg.com.myapplicati I/clp: mlist mCList is equals =true

subList是在原数组上进行操作。

71.推荐使用subList处理局部列表

        List<String> mList = new ArrayList<String>();        for (int i = 0; i < 100; i++) {            mList.add("a");        }        //删除第20~30之间的元素        mList.subList(20, 30).clear();        Log.i("clp", "mList size =" + mList.size());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

01-06 07:28:51.470 32234-32234/old.pkg.com.myapplicati I/clp: mList size =90

72.生成子列表之后不要再操作原列表

        List<String> mList = new ArrayList<String>();        for (int i = 0; i < 100; i++) {            mList.add("a");        }        List<String> mSubList = mList.subList(20, 30);        mList.add("d");        Log.i("clp", "mlist size =" + mList.size());        Log.i("clp", "mSubList size =" + mSubList.size());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

java.lang.RuntimeException: Unable to start activity ComponentInfo
{old.pkg.com.myapplicati/newpkg.pkg.com.myapplication2.MainActivity}:
java.util.ConcurrentModificationException

操作子列表也有可能导致报错,如下:

        List<String> mList = new ArrayList<String>();        for (int i = 0; i < 100; i++) {            mList.add("a");        }        List<String> mSubList = mList.subList(20, 30);        List<String> mSubList2 = mList.subList(10, 30);        mSubList.add("d");//        mSubList2.add("e");        Log.i("clp", "mlist size =" + mList.size());        Log.i("clp", "mSubList size =" + mSubList.size());        Log.i("clp", "mSubList2 size =" + mSubList2.size());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

73.使用Comparator进行排序

public class Person implements Comparable<Person> {    public int id;    public String name;    public Person(int id, String name) {        this.id = id;        this.name = name;    }    @Override    public int compareTo(Person person) {        if (person == null) {            return -1;        }        return Integer.valueOf(id).compareTo(Integer.valueOf(person.id));    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
        List<Person> mList = new ArrayList<Person>();        mList.add(new Person(2, "张二"));        mList.add(new Person(1, "张一"));        mList.add(new Person(4, "张四"));        mList.add(new Person(3, "张三"));        Collections.sort(mList);        for (int i = 0; i < mList.size(); i++) {            Log.i("clp", "index =" + i + ";name =" + mList.get(i).name);        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

使用Comparator可以拓展排序。

        List<Person> mList = new ArrayList<Person>();        mList.add(new Person(2, 440300, "张二"));        mList.add(new Person(1, 440322, "张一"));        mList.add(new Person(4, 440310, "张四"));        mList.add(new Person(3, 440200, "张三"));        mList.add(new Person(6, 440200, "张六"));        mList.add(new Person(5, 440200, "张五"));        Collections.sort(mList, new PerComparator());        for (int i = 0; i < mList.size(); i++) {            Log.i("clp", "index =" + i + ";cityId=" + mList.get(i).cityId + ";name =" + mList.get(i).name);        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
    //先按照cityId进行排序,如果相等,就按照id排序    public class PerComparator implements Comparator<Person> {        @Override        public int compare(Person person, Person t1) {            if (person.cityId > t1.cityId) {//1的画,是排后面                return 1;            } else if (person.cityId == t1.cityId) {//如果城市相等,按照id排序                if (person.id > t1.id) {                    return 1;                } else if (person.id == t1.id) {                    return 0;                } else {                    return -1;                }            } else {                return -1;            }        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

如果想实现倒序,直接使用:
Collections.sort(mList, Collections.reverseOrder(new PerComparator()));

    Collections.reverse(mList);---对排序之后的,进行倒序

Comparable实现默认排序。Comparator可以实现拓展排序。

74.不推荐使用binarySearch对列表进行检索

1.indexOf是通过for循环进行排序查找第一个符合条件的
2.binarySearch是通过二分查找,但是前提也是必须先排序

所以,如果已经排序的,使用binarySearch性能会高很多。

75.集合中的元素必须做到compartTo和equals同步

public class Person implements Comparable<Person> {    public int id;    public String name;    public Person(int id, String name) {        this.id = id;        this.name = name;    }    @Override    public int compareTo(Person person) {        if (person == null) {            return -1;        }        return person.name.compareTo(this.name);    }    @Override    public boolean equals(Object o) {        if (o == null) {            return false;        }        if (o == this) {            return true;        }        if (this.getClass() != o.getClass()) {            return false;        }        Person p = (Person) o;        return Integer.valueOf(id).compareTo(Integer.valueOf(p.id)) == 0;    }}
  • 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
  • 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
        List<Person> mList = new ArrayList<Person>();        mList.add(new Person(1, "张二"));        mList.add(new Person(1, "张一"));        Person p = new Person(1, "张一");        int index1 = mList.indexOf(p);        int index2 = Collections.binarySearch(mList, p);        Log.i("clp", "index1=" + index1);        Log.i("clp", "index2=" + index2);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

04-18 11:51:12.192 12423-12423/com.example.demo I/clp: index1=0
04-18 11:51:12.192 12423-12423/com.example.demo I/clp: index2=1

indexOf是通过对象中的equals来判断是否相等,
binarySearch是通过compartTo来判断是否相等。所以2个方法应该同步。

76.集合运算中使用更优雅的方式

        List<String> mList1 = new ArrayList<String>();        mList1.add("a");        mList1.add("b");        List<String> mList2 = new ArrayList<String>();        mList2.add("a");        mList2.add("c");        //并集//        mList1.addAll(mList2);//        Log.i("clp", "并集 size=" + mList1.size());//        //04-18 12:07:46.552 12423-12423/com.example.demo I/clp: 并集 size=4        //交集//        mList1.retainAll(mList2);//        Log.i("clp", "交集 size=" + mList1.size());        //04-18 12:08:55.022 12423-12423/com.example.demo I/clp: 交集 size=1        //差集//        mList1.removeAll(mList2);//        Log.i("clp", "差集 size=" + mList1.size());        //04-18 12:09:44.782 12423-12423/com.example.demo I/clp: 差集 size=1        //无重复的并集        mList2.removeAll(mList1);        mList1.addAll(mList2);        Log.i("clp", "无重复的并集 size=" + mList1.size());//        04-18 12:10:34.862 12423-12423/com.example.demo I/clp: 无重复的并集 size=3
  • 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
  • 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

无重复的并集,不能使用HashSet,因为会把本来重复的元素合并掉。

77.使用shuffle打乱列表

        int count = 10;        List<String> mList = new ArrayList<>(10);        for (int i = 0; i < 10; i++) {            mList.add(i + "");        }//        for (int i = 0; i < 10; i++) {//            int random = new Random().nextInt(10);//            Collections.swap(mList, i, random);//        }        Collections.shuffle(mList);//打乱顺序        for (int i = 0; i < 10; i++) {            Log.i("clp", "value" + mList.get(i));        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

78.减少HashMap中元素的数量

1.比ArrayList多了Entry对象,如果有40万个数据,就是多了40万个对象
2.扩容机制不一样,HashMap是2倍扩容,ArrayList是1.5倍
3.HashMap有一个加载因子,意思是到达容量的0.75就开始扩容了

所以大量数据的时候,HashMap比ArrayList先内存溢出。

79.集合中的哈希码尽量不要重复

HashMap是通过哈希值,实现比ArrayList快速的查找,如果哈希码是一样的,性能就和链表的性能是一样的。

80.多线程使用Vector和HashTable

        final Vector<String> mVList = new Vector<String>();        for (int i = 0; i < 10000; i++) {            mVList.add("火车票" + i);        }        for (int i = 0; i < 10; i++) {            new Thread(new Runnable() {                @Override                public void run() {                    while (true) {                        String mR = mVList.remove(0);                        Log.i("clp", "cur thread id=" + Thread.currentThread().getId() + ";remove =" + mR);                    }                }            }).start();        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

多个线程同步,但是不能实现,一个线程读的时候,另一个线程在写。

81.非稳定序列推荐使用List

        SortedSet<Person> sortSet = new TreeSet<Person>();        sortSet.add(new Person(15, "张三"));        sortSet.add(new Person(10, "张四"));        for (Person per : sortSet) {            Log.i("clp", "id=" + per.id + ";name=" + per.name);        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

04-18 15:44:55.202 31018-31018/com.example.demo I/clp: id=10;name=张四
04-18 15:44:55.202 31018-31018/com.example.demo I/clp: id=15;name=张三

        SortedSet<Person> sortSet = new TreeSet<Person>();        sortSet.add(new Person(15, "张三"));        sortSet.add(new Person(10, "张四"));        sortSet.first().id = 20;        for (Person per : sortSet) {            Log.i("clp", "id=" + per.id + ";name=" + per.name);        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

04-18 15:45:49.532 31018-31018/com.example.demo I/clp: id=20;name=张四
04-18 15:45:49.532 31018-31018/com.example.demo I/clp: id=15;name=张三

SortedSet只有在插入的时候,进行排序。

    sortSet = new TreeSet<Person>(new ArrayList<Person>(sortSet));

可以通过new 重新构建。

基本类型,建议使用SortedSet,如果是自己创建的对象,可以使用Colletions.sort

82.集合大家族

一、List
实现List的接口主要有:ArrayList-动态数组 LinkedList-双向链表 Vector-线程安全的动态数组 Stack-对象栈,先进后出

二、Set-不包含重复元素的集合:
EnumSet-枚举类型的专用Set,所有的元素都是枚举类型
HashSet-以哈希码决定元素位置的Set,与HashMap类似,提供快速的插入和查找
TreeSet-自动排序的Set,实现了SortedSet接口

三、Map-分为排序的Map和非排序Map
TreeMap-根据Key值自动排序
HashMap
HashTable
Properties:HashTable的子类,主要从Property文件中加载数据,并提供方便的读写。
EnumMap
WeakHashMap:可以被垃圾回收的HashMap

四、Queue队列-分为阻塞和非阻塞队列

五、数组
所有的集合底层存储的都是数组,数据可以存储基本类型,集合不行。

六、工具类
数组的工具类:Array和Arrays
集合的工具类:Collections

如果需要精确排序,则需要使用开源项目。如pinyin4j。

枚举和注解

83.推荐使用枚举定义常亮

public enum SeaSon {    Sping, Summer, Autumn, Winner;    public static SeaSon getCommonSeason() {        return Sping;    }}        for (SeaSon s : SeaSon.values()) {            Log.i("clp", "s values =" + s);        }        Log.i("clp", "common =" + SeaSon.getCommonSeason());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

04-19 10:26:24.608 14196-14196/com.example.demo I/clp: s values =Sping
04-19 10:26:24.608 14196-14196/com.example.demo I/clp: s values =Summer
04-19 10:26:24.608 14196-14196/com.example.demo I/clp: s values =Autumn
04-19 10:26:24.608 14196-14196/com.example.demo I/clp: s values =Winner
04-19 10:26:24.608 14196-14196/com.example.demo I/clp: common =Sping

84.使用构造函数协助描述枚举项

public enum SeaSon {    Sping("春"), Summer("夏"), Autumn("秋"), Winner("冬");    private String desc;    SeaSon(String desc) {        this.desc = desc;    }    public String getDesc() {        return desc;    }    public static SeaSon getCommonSeason() {        return Sping;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
        for (SeaSon s : SeaSon.values()) {            Log.i("clp", "s values =" + s.getDesc());        }        Log.i("clp", "common =" + SeaSon.getCommonSeason());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

构造函数还可以比这个更复杂,传递对象等。

85.小心switch带来的空值异常

    public void doSwSeaSon(SeaSon seaSon) {        switch (seaSon) {            case Sping:                Log.i("clp", "Sping");                break;            default:                Log.i("clp", "其他类型");                break;        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
doSwSeaSon(null);
  • 1
  • 1

java.lang.NullPointerException: Attempt to invoke virtual method ‘int com.cp.demo.java.SeaSon.ordinal()’ on a null object reference

case的其实是:SeaSon.Sping.ordinal(),所以要做对象的空值判断。

86.在switch的default代码块中增加AssertionError错误

因为枚举类型和case没有强制关系,如果后续增加了一个,没有case,就会导致switch漏掉这个枚举项。

87.枚举使用valueOf前必须进行校验

否则会抛出异常

        List<String> mList = Arrays.asList("Sping", "sss");        for (int i = 0; i < mList.size(); i++) {            SeaSon sea = SeaSon.valueOf(mList.get(i));            if (sea == null) {                Log.i("clp", "没有此枚举值");            } else {                Log.i("clp", "有值");            }        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

java.lang.IllegalArgumentException: sss is not a constant in com.cp.demo.java.SeaSon

两种方案:
1.捕获异常

        List<String> mList = Arrays.asList("Sping", "sss");        for (int i = 0; i < mList.size(); i++) {            try {                SeaSon sea = SeaSon.valueOf(mList.get(i));                Log.i("clp", "有值");            } catch (IllegalArgumentException e) {                Log.i("clp", "没有此枚举值");            }        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

04-19 11:35:20.148 13538-13538/com.example.demo I/clp: 有值
04-19 11:35:20.148 13538-13538/com.example.demo I/clp: 没有此枚举值

2.拓展枚举类

    public static boolean contains(String name) {        for (SeaSon sen : values()) {            if (sen.name().equals(name)) {                return true;            }        }        return false;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
        List<String> mList = Arrays.asList("Sping", "sss");        for (int i = 0; i < mList.size(); i++) {            boolean sea = SeaSon.contains(mList.get(i));            if (sea) {                Log.i("clp", "有值");            } else {                Log.i("clp", "没有此枚举值");            }        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

88.用枚举实现工厂方法模式更简洁

1.非静态方法实现。

public interface Car {    public void drive();}public class FortCar implements Car {    @Override    public void drive() {        Log.i("clp", "fort drive");    }}public class BuickCar implements Car {    @Override    public void drive() {        Log.i("clp", "buick drive");    }}public enum CarFactory {    FortCar, BuickCar;    public Car create() {        switch (this) {            case FortCar:                return new FortCar();            case BuickCar:                return new BuickCar();            default:                Log.i("clp", "无效参数");        }        return null;    }}
  • 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
  • 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

04-19 11:55:34.988 1940-1940/com.example.demo I/clp: fort drive

2.通过抽象方法生成

public enum CarFactory {    FortCar {        @Override        public Car create() {            return new FortCar();        }    }, BuickCar {        @Override        public Car create() {            return new BuickCar();        }    };    public abstract Car create();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

89.枚举项的数量限制的64个以内

long e = -1L >>> -65;//value=1long e = -1L >>> -1;//value=1long e = -1L >>> 63;//value=1long e = -1L >>> 1;//9223372036854775807long e = -1L >>> 62;//value=3int e = -1 >>> 31;//value=1int e = -1 >>> 1;//2147483647
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

小于等于64和大于64处理的机制是不一样的。

        Enum[] universe = getUniverse(elementType);        if (universe == null)            throw new ClassCastException(elementType + " not an enum");        if (universe.length <= 64)            return new RegularEnumSet<>(elementType, universe);        else            return new JumboEnumSet<>(elementType, universe);RegularEnumSet:    void addAll() {        if (universe.length != 0)            elements = -1L >>> -universe.length;    }JumboEnumSet:    void addAll() {        for (int i = 0; i < elements.length; i++)            elements[i] = -1;        elements[elements.length - 1] >>>= -universe.length;        size = universe.length;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

所以内部是以64位拆分进行分组。

90.小心注解继承

91.枚举和注解结合使用威力很大

92.注意@Overrig不同版本的区别

如果是Java1.6版本的程序移植到1.5版本环境中,就需要删除实现接口方法上的@Override注解

泛型和反射

93.Java的泛型是类型擦除的

java的编译期和运行期解读:
http://www.360doc.com/content/14/0218/23/9440338_353675002.shtml

编译期:源码->class文件的过程
运行期:在JVM中运行的过程

    public void initList(List<Integer> mList) {    }    public void initList(List<String> mList) {    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

按照上面这种方式写,报错。
initList(List)’ clashes with ‘initList(List)’; both methods have same erasure

        List<String> mList = new ArrayList<String>();        List<Integer> mIList = new ArrayList<Integer>();        Log.i("clp", "is equals=" + (mList.getClass() == mIList.getClass()));
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

04-25 15:49:13.356 2760-2760/old.pkg.com.myapplicati I/clp: is equals=true

他们擦除后的类型都是List

Java泛型-类型擦除:
http://blog.csdn.net/caihaijiang/article/details/6403349

94.不能初始化泛型参数和数组

        T t = new T();//报错        T[] mTArray = new T[];//报错        List<T> mList = new ArrayList<T>();//报错
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

以上3个写法,在AS上面都报错,需要在运行期指定T类型。

95.强制声明泛型的实际类型

List<Integer> mList = CldCalMiniUtil.asList();//正确List<Integer> mList = CldCalMiniUtil.<Integer>asList();//正确List<Number> mList2 = CldCalMiniUtil.asList(1, 2.2);//报错List<Number> mList2 = CldCalMiniUtil.<Number>asList(1, 2.2);//正确--这里使用了强制声明
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
反射demo(二)
18 this关键字
StringBuffer和StringBuilder的区别
C#自定义PropertyGrid属性
泛型继承
静态块与非静态块的区别
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服