java实用技巧

1. 需要Map的主键和取值时,应该迭代entrySet()

当循环中只需要Map的主键时,迭迭代keySet()时正确的。但是,当需要主键和取值时,迭代entrySet()才是更高效的做法,比迭代keySet()后再去get取值性能更佳。

反例:

		Map<String, String> map = ...;
		for (String key : map.keySet()) {
			String valueString = map.get(key);
			...
		}

正例:

 	Map<String, String> map = ...;
 	for (Map.Entry<String, String> entry : map.entrySet()) {
			String key = entry.getKey();
			String value = entry.getValue();
			...
		}

2.应该实用Collection.isEmpty()检测空

使用 Collection.size()来检测空逻辑上没有问题,但是使用Collection.isEmpty()使得代码更易读,并且可以获得更好的性能。任何Collection.isEmpty()实现的时间复杂度都是O(1),但是某些Collection.size()实现的时间复杂度可能是O(n)。

反例:

		if (collection.size() == 0) {
			...
		}

正例:

     if (collection.isEmpty()) {
			...
		}

如果还需要检测null ,可采用:

  • CollectionUtils.isEmpty(collection)
  • CollectionUtils.isNotEmpty(collection)

3.不要把集合对象传给自己

将集合作为参数传递给集合自己的方法要么是一个错误,要么是无意义的代码。
此外,由于某些方法要求参数在执行期间保持不变,因此将集合传递给自身可能会导致异常行为。

反例:

		List<String> list=new ArrayList<String>();
		list.add("hello");
		list.add("world");
		if (list.contains(list)) {//无意义,总返回true
			
		}
		list.remove(list);//性能差,直接使用clear

4.集合初始化尽量指定大小

java 的集合类用起来十分方便,但是看源码可知,集合也是有大小限制的。每次库容的时间复杂度很可能是O(n),所以尽量制定可预知的集合大小,能减少集合的扩容次数。

反例:

        int[] arr = new int[] { 1, 2, 3 };
		List<Integer> list2 = new ArrayList<>();
		for (int i : arr) {
			list2.add(i);
		}

正例:

		int[] arr = new int[] { 1, 2, 3 };
		List<Integer> list2 = new ArrayList<>(arr.length);
		for (int i : arr) {
			list2.add(i);
		}

5.频繁调用Collection.contains方法请使用Set

在java集合类库中,List的Contains方法普遍时间按复杂度是O(n),如果在代码中需要频繁调用contains方法查找数据,可以先将list转换成HashSet实现,将O(n)的时间复杂度降为O(1)。

反例:

		List<Integer> list =...;
		for (int i = 0; i < Integer.MAX_VALUE; i++) {
			// 时间复杂度O(n)
			list.contains(i);
		}

正例:

 		List<Integer> list = ...;
		Set<Integer> set=new HashSet<Integer>();
		for (int i = 0; i < Integer.MAX_VALUE; i++) {
			// 时间复杂度O(n)
			set.contains(i);
		}

6. 返回空数组和空集而不是null

返回null,需要调用方强制检测null,否则就会抛出空指针异常。返回空数组或空集合,有效地避免了调用方因未检测null而抛出空指针异常。返回空数组或空集合,有效地避免了调用方因未检测null而抛出空指针异常,还可以删除调用方检测null的语句使代码更简洁。

反例:

    class Result {

	}

	public static Result[] getResults() {
		return null;
	}

	public static List<Result> getResultList() {
		return null;
	}

	public static Map<String, Result> getResultMap() {
		return null;
	}

	public static void main(String[] args) {
		Result[] results = getResults();
		if (results != null) {
			for (Result result : results) {
                  ...
			}

		}

		List<Result> resultList = getResultList();
		if (results != null) {
			for (Result result : resultList) {
                 ...
			}

		}

		Map<String, Result> resultMap = getResultMap();
		if (resultMap != null) {
			for (Map.Entry<String, Result> entry : resultMap.entrySet()) {
                  ...
			}

		}
	}

正例:

    class Result {

	}

	public static Result[] getResults() {
		return new Result[0];
	}

	public static List<Result> getResultList() {
		return Collections.emptyList();
	}

	public static Map<String, Result> getResultMap() {
		return Collections.emptyMap();
	}

	public static void main(String[] args) {
		Result[] results = getResults();
		for (Result result : results) {

		}

		List<Result> resultList = getResultList();
		for (Result result : resultList) {

		}

		Map<String, Result> resultMap = getResultMap();
		for (Map.Entry<String, Result> entry : resultMap.entrySet()) {

		}
	}

7.List的随机访问

数组和链表的区别:数组的随机访问效率更高。当调用方法获取到List后,如果想随机访问其中的数据,并不知道该数组内部实现是链表还是数组,怎么办呢?可以判断它是否实现RandomAccess接口。

正例:

        // 调用别人的服务获取到list
		List<Integer> list = otherService.getList();
		if (list instanceof RandomAccess) {
			// 内部数组实现,可以随机访问
			System.out.println(list.get(list.size() - 1));
		} else {
			// 内部可能是链表实现,随机访问效率低
		}

8.字符串拼接使用StringBuilder

一般的字符串拼接在编译期java会进行优化,但是在循环中字符串拼接,java编辑器无法做到优化,所以需要使用StringBuilder进行替换。

反例:

        String string = "";
		for (int i = 0; i < 10; i++) {
			string += i;
		}

正例:

        String a = "a";
 		String b = "b";
		String c = "c";
		String s = a + b + c;// 没问题,java编译器会进行优化
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < 10; i++) {
			sb.append(i);//循环中,java编译器无法进行优化,所以要手动使用StringBuilder
		}

9.长整型常量后添加大写L

在使用长整型常量值时,后面需要添加L,必须是大写的L,不能是小写l,小写l容易跟数字1混淆而造成误解。

反例:

        long value = 1l;
		long max = Math.max(1L, 6);

正例:

        long value = 1L;
		long max = Math.max(1L, 6L);

10.不要使用魔法值

当你编写一段代码时,使用魔法值肯那个看起来很明确,但在调试时它们却不显得那么明确了。这就是为什么需要把魔法值定义为可读取常量的原因。但是,-1 、0 和 1不被视为魔法值。

反例:

	    for (int i = 0; i < 100; i++) {
              ...
		}
		if (a == 100) {
              ...
		}

正例:

        private static final int MAX_COUNT=100;
		
		for (int i = 0; i < MAX_COUNT; i++) {

		}
		if (a == MAX_COUNT) {

		}

不要使用集合实现来赋值静态成员变量

对于集合类型的静态成员变量,不要使用用集合实现来赋值,应该使用静态代码快复制。

反例:

    private static Map<String, Integer> map = new HashMap<String, Integer>() {
		{
			put("a", 1);
			put("b", 2);
		}
	};
	private static List<String> list = new ArrayList<String>() {
		{
			add("a");
			add("b");
		}
	};

正例:

    private static Map<String, Integer> map = new HashMap<String, Integer>();
	static {
		map.put("a", 1);
		map.put("b", 2);
	}

	private static List<String> list = new ArrayList<String>();
	static {
		list.add("a");
		list.add("b");
	}

建议使用try-with-resources语句

java 7 中引入try-with-resources语句,该语句能保证将相关资源关闭,优于原来的try-catch-finally语句,并且使程序 代码更安全更简洁。

反例:

     private void handle(String fileName) {
		BufferedReader reader = null;
		try {
			String line;
			reader = new BufferedReader(new FileReader(fileName));
			while ((line = reader.readLine()) != null) {
				  ...
			}
		} catch (Exception e) {
			  ...
		} finally {
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e2) {
				    ...
				}
			}
		}

	}

正例:

    try (BufferedReader reader = new BufferedReader( new FileReader(fileName))) {
   		String line;
   		while ((line = reader.readLine()) != null) {
   			 ...
   		}
   	} catch (Exception e) {
   		 ...
   	}

工具类应该屏蔽构造函数

工具类是一堆静态字段函数的集合,不应该被实例化。但是,Java为每个没有明确定义构造函数的类添加了隐式公有构造函数。所以,为了避免别有使用有误,应该显示定义私有构造函数来屏蔽这个隐式公有构造函数。

反例:

	public class MathUtils {
		public static final double PI = 3.1415926D;

		public static int sum(int a, int b) {
			return a + b;
		}
	}

正例:

	public class MathUtils {
		private MathUtils() {}
		
		public static final double PI = 3.1415926D;

		public static int sum(int a, int b) {
			return a + b;
		}
	}

公有静态常量应该通过类访问

虽然通过类的实例访问共有静态常量是允许的,但是容易让人误认为每个类的实例都有一个共有静态常量。所以,公有静态常量应该直接通过类访问。

反例:

 public class User{
	public static final String CONST_NAME="name";
}
	
User user=new User();
String nameKey=user.CONST_NAME;

正例:

public class User{
   public static final String CONST_NAME="name";
}

String nameKey=User.CONST_NAME;

使用String.valueOf(value)代替""+value

当要把其他对象或类型转化为字符串时,使用String.valueOf(value)""+value的效率更高。

反例:

int i = 1;
String s="" + i;

正例:

int i = 1;
String s=String,valueOf(i);

禁止使用构造方法BigDecimal(double)

BigDecimal(double)存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。

反例:

BigDecimal value=new BigDecimal(0.1D);

正例:

BigDecimal value=BigDecimal.valueOf(0.1D);

优先使用常量或确定值来调用equals方法

对象的equals方法容易抛空指针异常,应使用常量或确定有值得对象来调用equals方法。
当然,使用java.util.Objects.equals()方法是最佳实践。

你可能感兴趣的:(Java)