java提高性能优雅编码的操作

在这里插入图片描述

Java优雅编码

提高代码性能的操作

1. 需要 Map 的主键和取值时,应该迭代 entrySet()
2. 应该使用Collection.isEmpty()检测空
3. 不要把集合对象传给自己
4. 集合初始化尽量指定大小
5. 字符串拼接使用 StringBuilder
6. 判断链表还是数组
7. 频繁调用 Collection.contains 方法请使用 Set
8. 直接赋值常量值,禁止声明新对象
9. 当成员变量值无需改变时,尽量定义为静态常量
10. 尽量使用基本数据类型,避免自动装箱和拆箱
11. 如果变量的初值会被覆盖,就没有必要给变量赋初值
12. 协议方法参数值非空,避免不必要的空指针判断
13. 尽量减少方法的重复调用
14. 尽量使用移位来代替正整数乘除
15. 不要使用循环拷贝数组,尽量使用System.arraycopy拷贝数组
16. 集合转化为类型T数组时,尽量传入空数组T[0]
17. 不要使用循环拷贝集合,尽量使用JDK提供的方法拷贝集合
18. 尽量重复使用同一缓冲区
19. 在单线程中,尽量使用非线程安全类
20. 在多线程中,尽量使用线程安全类
21. 尽量减少同步代码块范围
22. 尽量使用线程池减少线程开销

让代码更优雅

1. 长整型常量后添加大写 L
2. 不要使用魔法值
3. 不要使用集合实现来赋值静态成员变量
4. 建议使用 try-with-resources 语句
5. 删除未使用的方法、参数、变量
6. 公有静态常量应该通过类访问
7. 使用String.valueOf(value)代替""+value
8. 过时代码添加 @Deprecated 注解
9. 尽量避免在循环中捕获异常

让代码远离 bug

1. 禁止使用构造方法 BigDecimal(double)
2. 返回空数组和空集合而不是 null
3. 优先使用常量或确定值来调用 equals 方法
4. 枚举的属性字段必须是私有不可变
5. 小心String.split(String regex)

提高代码性能的操作

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

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

public static void entrySet(){
        Map<String,String> map = new HashMap<>();
        map.put("key1","value1");
        map.put("key2","value2");
        map.put("key3","value3");
        //反例
        for (String key:map.keySet()) {
            String valus = map.get(key);
            System.out.println("反例   "+valus);
        }
        //正例
        for (Map.Entry<String,String> entry:map.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println("正例   "+key+"   "+value);
        }
    }
2. 应该使用Collection.isEmpty()检测空

如果需要还需要检测 null,可采用CollectionUtils.isEmpty(collection)和CollectionUtils.isNotEmpty(collection)。

public static void CollectionisEmpty(){
        Collection<String> strings = null;
        //反例
        if (strings.size() == 0){
            System.out.println("strings.size() == 0");
        }
        //正例
        if (strings.isEmpty()){
            System.out.println("strings.isEmpty()");
        }
    }
3. 不要把集合对象传给自己

反例

List<String> list = new ArrayList<>();
list.add("Hello");
list.add("World");
if (list.containsAll(list)) { // 无意义,总是返回true
    ...
}
list.removeAll(list); // 性能差, 直接使用clear()
4. 集合初始化尽量指定大小

Java集合初始化时都会指定一个默认大小,当默认大小不再满足数据需求时就会扩容,每次扩容的时间复杂度有可能是O(n)
反例

List<UserDO> userDOList = ...;
Set<Long> userSet = new HashSet<>();
Map<Long, UserDO> userMap = new HashMap<>();
List<UserVO> userList = new ArrayList<>();
for (UserDO userDO : userDOList) {
    userSet.add(userDO.getId());
    userMap.put(userDO.getId(), userDO);
    userList.add(transUser(userDO));
}

正例

List<UserDO> userDOList = ...;
int userSize = userDOList.size();
Set<Long> userSet = new HashSet<>(userSize);
Map<Long, UserDO> userMap = new HashMap<>((int) Math.ceil(userSize * 4.0 / 3));
List<UserVO> userList = new ArrayList<>(userSize);
for (UserDO userDO : userDOList) {
    userSet.add(userDO.getId());
    userMap.put(userDO.getId(), userDO);
    userList.add(transUser(userDO));
}
5. 字符串拼接使用 StringBuilder

正例

public static void stringBuilder(){
        String a = "a";
        String b = "b";
        String c = "c";
        String s = a + b + c; // 没问题,java编译器会进行优化
        System.out.println("s===  "+s);
        ArrayList<Object> objects = new ArrayList<>();
        objects.add("X");
        objects.add("Q");
        objects.add("D");
        StringBuilder sb = new StringBuilder(50); //尽量设置初始化大小
        for (Object object:objects) {
            sb.append(object);
        }
        System.out.println("StringBuilder拼接的sb的结果:"+sb);
    }
6. 判断链表还是数组

正例

// 调用别人的服务获取到list
List<Integer> list = otherService.getList();
if (list instanceof RandomAccess) {
    // 内部数组实现,可以随机访问
    System.out.println(list.get(list.size() - 1));
} else {
    // 内部可能是链表实现,随机访问效率低
}
7. 频繁调用 Collection.contains 方法请使用 Set
public static void collectionContanins(){
        //反例
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i<= Integer.MAX_VALUE;i++){
            //  时间复杂度 O(n)
            list.contains(i);
        }
        //正例
        Set<Integer> set = new HashSet<>(list);
        for (int i = 0; i<= Integer.MAX_VALUE;i++){
            //  时间复杂度 O(1)
            set.contains(i);
        }
    }
8. 直接赋值常量值,禁止声明新对象

直接赋值常量值,只是创建了一个对象引用,而这个对象引用指向常量值。

public void fuzhi(){
    //  反例
    Long i = new Long(1L);
    String s = new String("abc");
    // 正例
    Long ii = 1L;
    String ss = "abc";
}
9. 当成员变量值无需改变时,尽量定义为静态常量

在类的每个对象实例中,每个成员变量都有一份副本,而成员静态常量只有一份实例
反例

private final long timeout = 5L;

正例

private static final long TIMEOUT = 5L;
10. 尽量使用基本数据类型,避免自动装箱和拆箱

装箱和拆箱都是需要CPU和内存资源的,所以应尽量避免使用自动装箱和拆箱
反例

Integer sum = 0;
int[] values = ...;
for (int value : values) {
    sum += value; // 相当于result = Integer.valueOf(result.intValue() + value);
}

正例

int sum = 0;
int[] values = ...;
for (int value : values) {
    sum += value;
}
11. 如果变量的初值会被覆盖,就没有必要给变量赋初值

反例:

List<UserDO> userList = new ArrayList<>();
if (isAll) {
    userList = userDAO.queryAll();
} else {
    userList = userDAO.queryActive();
}

正例

List<UserDO> userList;
if (isAll) {
    userList = userDAO.queryAll();
} else {
    userList = userDAO.queryActive();
}
12. 协议方法参数值非空,避免不必要的空指针判断

协议编程,可以@NonNull和@Nullable标注参数
反例

public static boolean isValid(UserDO user) {
    if (Objects.isNull(user)) {
        return false;
    }
  return Boolean.TRUE.equals(user.getIsValid());
}

正例

public static boolean isValid(@NonNull UserDO user) {
  return Boolean.TRUE.equals(user.getIsValid());
}
13. 尽量减少方法的重复调用

反例

List<UserDO> userList = ...;
for (int i = 0; i < userList.size(); i++) {
    ...
}

正例

List<UserDO> userList = ...;
int userLength = userList.size();
for (int i = 0; i < userLength; i++) {
    ...
}
14. 尽量使用移位来代替正整数乘除

用移位操作可以极大地提高性能。对于乘除2^n(n为正整数)的正整数计算,可以用移位操作来代替
反例:

int num1 = a * 4;
int num2 = a / 4;

正例:

int num1 = a << 2;
int num2 = a >> 2;
15. 不要使用循环拷贝数组,尽量使用System.arraycopy拷贝数组

推荐使用System.arraycopy拷贝数组,也可以使用Arrays.copyOf拷贝数组

 public static void systemArraycopy(){
        // 反例
        int[] sources = new int[]{1,6,9,5,3};
        int[] targets = new int[sources.length];
        for (int i = 0; i < targets.length;i++){
            targets[i] = sources[i];
            System.out.println("反例 targets  ==  "+targets[i]);
        }
        // 正例
        int[] sourcess = new int[]{1,6,9,5,3};
        int[] targetss = new int[sources.length];
        System.arraycopy(sourcess,0,targetss,0,targetss.length);
        for (int a:targetss) {
            System.out.println("正例 targetss  ==  "+a);
        }
    }
16. 集合转化为类型T数组时,尽量传入空数组T[0]

将集合转换为数组有2种形式:toArray(new T[n])和toArray(new T[0])。在旧的Java版本中,建议使用toArray(new T[n]),因为创建数组时所需的反射调用非常慢。在OpenJDK6后,反射调用是内在的,使得性能得以提高,toArray(new T[0])比toArray(new T[n])效率更高。此外,toArray(new T[n])比toArray(new T[0])多获取一次列表大小,如果计算列表大小耗时过长,也会导致toArray(new T[n])效率降低

public static void toArray(){
        List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
        //反例
        Integer[] integers = integerList.toArray(new Integer[integerList.size()]);
        for (Integer i:integers) {
            System.out.println(i);
        }
        // 正例
        Integer[] integers1 = integerList.toArray(new Integer[0]);
        for (Integer integer:integers1) {
            System.out.println(integer);
        }
    }
17. 不要使用循环拷贝集合,尽量使用JDK提供的方法拷贝集合

JDK提供的方法可以一步指定集合的容量,避免多次扩容浪费时间和空间。同时,这些方法的底层也是调用System.arraycopy方法实现,进行数据的批量拷贝效率更高

public static void copyList(){
        //反例
        List<Object> list = new ArrayList<>();
        List<Object> list1 = new ArrayList<>();
        List<Object> objectList = new ArrayList<>(list.size()+list1.size());
        for (Object object:list) {
            objectList.add(object);
        }
        for (Object object:list1) {
            objectList.add(object);
        }
        //正例
        objectList.addAll(list);
        objectList.addAll(list1);
    }
18. 尽量重复使用同一缓冲区

针对缓冲区,Java虚拟机需要花时间生成对象,还要花时间进行垃圾回收处理。所以,尽量重复利用缓冲区
反例

StringBuilder builder1 = new StringBuilder(128);
builder1.append("update t_user set name = '").append(userName).append("' where id = ").append(userId);
statement.executeUpdate(builder1.toString());
StringBuilder builder2 = new StringBuilder(128);
builder2.append("select id, name from t_user where id = ").append(userId);
ResultSet resultSet = statement.executeQuery(builder2.toString());
...

正例

StringBuilder builder = new StringBuilder(128);
builder.append("update t_user set name = '").append(userName).append("' where id = ").append(userId);
statement.executeUpdate(builder.toString());
builder.setLength(0);
builder.append("select id, name from t_user where id = ").append(userId);
ResultSet resultSet = statement.executeQuery(builder.toString());
...
19. 在单线程中,尽量使用非线程安全类

反例

StringBuffer buffer = new StringBuffer(128);
buffer.append("select * from ").append(T_USER).append(" where id = ?");

正例

StringBuilder buffer = new StringBuilder(128);
buffer.append("select * from ").append(T_USER).append(" where id = ?");
20. 在多线程中,尽量使用线程安全类

反例

private volatile int counter = 0;
public void access(Long userId) {
    synchronized (this) {
        counter++;
    }
    ...
}

正例

private final AtomicInteger counter = new AtomicInteger(0);
public void access(Long userId) {
    counter.incrementAndGet();
    ...
}
21. 尽量减少同步代码块范围

在一个方法中,可能只有一小部分的逻辑是需要同步控制的,如果同步控制了整个方法会影响执行效率。所以,尽量减少同步代码块的范围,只对需要进行同步的代码进行同步
反例

private volatile int counter = 0;
public synchronized void access(Long userId) {
  counter++;
    ... // 非同步操作
}

正例

private volatile int counter = 0;
public void access(Long userId) {
    synchronized (this) {
        counter++;
    }
    ... // 非同步操作
}
22. 尽量使用线程池减少线程开销

多线程中两个必要的开销:线程的创建和上下文切换。采用线程池,可以尽量地避免这些开销
反例

public void executeTask(Runnable runnable) {
    new Thread(runnable).start();
}

正例

private static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(10);
public void executeTask(Runnable runnable) {
    executorService.execute(runnable);
}

让代码更优雅

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

反例

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

正例

long value = 1L;
long max = Math.max(1L, 5L);
2. 不要使用魔法值

反例

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 (count == MAX_COUNT) {
    ...
}
3. 不要使用集合实现来赋值静态成员变量

反例

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<>();
static {
    map.put("a", 1);
    map.put("b", 2);
};

private static List<String> list = new ArrayList<>();
static {
    list.add("a");
    list.add("b");
};
4. 建议使用 try-with-resources 语句

反例

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 e) {
                ...
            }
        }
    }
}

正例

private void handle(String fileName) {
    try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
        String line;
        while ((line = reader.readLine()) != null) {
            ...
        }
    } catch (Exception e) {
        ...
    }
}
5. 删除未使用的方法、参数、变量

反例

public class DoubleDemo1 {
    private int unusedField = 100;
    private void unusedMethod() {
        ...
    }
    public int sum(int a, int d, int d) {
    	int c = 100;
        return a + b;
    }
}

正例

public class DoubleDemo1 {
    public int sum(int a, int b) {
        return a + b;
    }
}
6. 公有静态常量应该通过类访问

反例

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;
7. 使用String.valueOf(value)代替""+value

反例

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

正例

int i = 1;
String s = String.valueOf(i);
8. 过时代码添加 @Deprecated 注解

正例:

/**
 * @deprecated 此方法效率较低,请使用{@link newSave()}方法替换它
 */
@Deprecated
public void save(){
    // do something
}
9. 尽量避免在循环中捕获异常

反例

public Double sum(List<String> valueList) {
    double sum = 0.0D;
    for (String value : valueList) {
        try {
            sum += Double.parseDouble(value);
        } catch (NumberFormatException e) {
            return null;
        }
    }
    return sum;
}

正例

public Double sum(List<String> valueList) {
    double sum = 0.0D;
    try {
        for (String value : valueList) {
            sum += Double.parseDouble(value);
        }
    } catch (NumberFormatException e) {
        return null;
    }
    return sum;
}

让代码远离 bug

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

反例

BigDecimal value = new BigDecimal(0.1D); // 0.100000000000000005551115...

正例

BigDecimal value = BigDecimal.valueOf(0.1D);; // 0.1
2. 返回空数组和空集合而不是 null

反例

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 (resultList != null) {
        for (Result result : resultList) {
            ...
        }
    }

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

正例

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> resultEntry : resultMap) {
        ...
    }
}
3. 优先使用常量或确定值来调用 equals 方法

反例:

public void isFinished(OrderStatus status) {
    return status.equals(OrderStatus.FINISHED); // 可能抛空指针异常
}

正例

public void isFinished(OrderStatus status) {
    return OrderStatus.FINISHED.equals(status);
}

public void isFinished(OrderStatus status) {
    return Objects.equals(status, OrderStatus.FINISHED);
}
4. 枚举的属性字段必须是私有不可变

反例

public enum UserStatus {
    DISABLED(0, "禁用"),
    ENABLED(1, "启用");

    public int value;
    private String description;

    private UserStatus(int value, String description) {
        this.value = value;
        this.description = description;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

正例

public enum UserStatus {
    DISABLED(0, "禁用"),
    ENABLED(1, "启用");

    private final int value;
    private final String description;

    private UserStatus(int value, String description) {
        this.value = value;
        this.description = description;
    }

    public int getValue() {
        return value;
    }

    public String getDescription() {
        return description;
    }
}
5. 小心String.split(String regex)

反例

"a.ab.abc".split("."); // 结果为[]
"a|ab|abc".split("|"); // 结果为["a", "|", "a", "b", "|", "a", "b", "c"]

正例

"a.ab.abc".split("\\."); // 结果为["a", "ab", "abc"]
"a|ab|abc".split("\\|"); // 结果为["a", "ab", "abc"]
发布了20 篇原创文章 · 获赞 14 · 访问量 7503

猜你喜欢

转载自blog.csdn.net/weixin_44467567/article/details/103666115