×
Community Blog Quickly Learn How You Can Improve Your Java Coding

Quickly Learn How You Can Improve Your Java Coding

Learn how Confucianism helped Alibaba engineers understand why improving one's Java coding skills is important.

In Instructions for Practical Living and Other Neo-Confucian Writings, the greater Chinese statesman and philosopher Wang Yangming wrote the following words:

Lust grows day by day, like the dust on the floor. If you do not sweep the dust every day, it will accumulate. If you work hard, you will find that there is no end to the journey of life. The more you explore, the more there will be left to know. What is necessary is precision, clarity, and completeness.

Wang Yangming's wise words are still very much relevant to our daily lives today. Bad code, just like lust and dust, increases every day. If you do not constantly work to clean it, it will accumulate. But, if you work hard to clean bad code, you can improve your programming ability and make your code precise and clear, with no room for doubt. This article introduces three ways to improve your Java code based on the actual coding work of an Alibaba Cloud engineer, with bad code samples provided.

Improving Your Code Performance

Iterate entrySet() When the Primary Key and Value of Map Are Used

You should iterate entrySet() when the primary key and value are used. This is more efficient than iterating keySet() and then getting the value.

Bad code:

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

Good code:

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

Use Collection.isEmpty() to Detect Null Values

Compared with Collection.size(), Collection.isEmpty() is much more readable and provides better performance when it comes to detecting null values. The time complexity of Collection.isEmpty() is always O(1), but that of Collection.size() may be O(n).

Bad code:

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

Good code:

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

To detect null values, you can use CollectionUtils.isEmpty(collection) and CollectionUtils.isNotEmpty(collection).

Do Not Pass Collection Objects to the Collection Itself

Passing a collection as a parameter to the collection itself is an error or meaningless code.

For methods that require unchanged parameters during execution, an error may occur when you pass a collection to itself.

Bad code:

List<String> list = new ArrayList<>();
list.add("Hello");
list.add("World");
if (list.containsAll(list)) { // 无意义,总是返回true
    ...
}
list.removeAll(list); // 性能差, 直接使用clear()

Specify the Collection Size During Collection Initialization

The collection class of Java is easy to use, but the collection size is limited in source code. The time complexity of each scaling operation may be O(n). You can specify the predictable collection size whenever possible to reduce the occurrences of collection scaling.

Bad code:

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

Good code:

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

Concatenate Strings by Using StringBuilder

In Java, concatenated strings are tuned during compilation. However, strings that are concatenated in a cycle are not concatenated during compilation. In this case, concatenate strings by using StringBuilder.

Bad code:

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

Good code:

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
}

Access List Randomly

Random access to arrays is more efficient than that to linked lists. When a called method needs to randomly access data in the acquired List, without knowing whether an array or a linked list is internally implemented, you can check whether the RandomAccess operation is used.

Good code:

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

Use Set to Frequently Call the Collection.contains Method

In the collection class library of Java, the time complexity of the contains method for List is O(n). If you need to frequently call the contains method in the code to search for data, you can convert List into HashSet to reduce the time complexity to O(1).

Bad code:

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

Good code:

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

Making Your Code More Elegant

Append the Uppercase Letter "L" to Long Integer Constants

Append the uppercase letter L to long integer constants. Do not use the lowercase l, because it can easily be confused with the digit 1.

Bad code:

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

Good code:

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

Do Not Use Magic Numbers

Magic numbers may make your code very clear, but it will be difficult to debug. Therefore, magic numbers must be defined as readable constants. However, -1, 0, and 1 are not considered magic numbers.

Bad code:

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

Good code:

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

Do Not Assign Values to Static Member Variables by Using Collection Implementations

Assign values to static member variables of the collection type by using static code blocks rather than collection implementations.

Bad code:

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");
    }
};

Good code:

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");
};

Use the try-with-resources Statement

Java 7 introduced the try-with-resources statement, which is used to close related resources and makes program code simpler and more secure. It is better than the original try-catch-finally statement.

Bad code:

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

Good code:

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

Delete Unused Private Methods and Fields

Delete unused private methods and fields to make the code simpler and easier to maintain. You can recover deleted methods and fields from historical commits.

Bad code:

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

Good code:

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

Delete Unused Local Variables

Delete unused local variables to make the code simpler and easier to maintain.

Bad code:

public int sum(int a, int b) {
    int c = 100;
    return a + b;
}

Good code:

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

Delete Unused Method Parameters

Unused method parameters are misleading. Delete them to make the code simpler and easier to maintain. However, do not delete unused parameters for override methods that are defined based on the methods of parent classes or interface methods.

Bad code:

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

Good code:

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

Delete the Redundant Brackets of Expressions

The redundant brackets of expressions are considered unnecessary by some coders but helpful for code reading by other coders. For Java experts, these redundant brackets only make the code look complex.

Bad code:

return (x);
return (x + 2);
int x = (y * 3) + 1;
int m = (n * 4 + 2);

Good code:

return x;
return x + 2;
int x = y * 3 + 1;
int m = n * 4 + 2;

Mask Constructors for the Tool Class

The tool class is a collection of static fields and functions and must not be instantiated. In Java, an implicit public constructor is added to each class without constructor definition. If you are a Java novice, we recommend that you define an explicit private constructor to mask the implicit public constructor.

Bad code:

public class MathUtils {
    public static final double PI = 3.1415926D;
    public static int sum(int a, int b) {
        return a + b;
    }
}

Good code:

public class MathUtils {
    public static final double PI = 3.1415926D;
    private MathUtils() {}
    public static int sum(int a, int b) {
        return a + b;
    }
}

Delete and Throw Redundant Caught Exceptions

If exceptions caught by using the catch statement are thrown without processing, the result is the same as not catching the exceptions. To solve this problem, you can delete the related code block or add another processing method.

Bad code:

private static String readFile(String fileName) throws IOException {
    try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
        String line;
        StringBuilder builder = new StringBuilder();
        while ((line = reader.readLine()) != null) {
            builder.append(line);
        }
        return builder.toString();
    } catch (Exception e) {
        throw e;
    }
}

Good code:

private static String readFile(String fileName) throws IOException {
    try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
        String line;
        StringBuilder builder = new StringBuilder();
        while ((line = reader.readLine()) != null) {
            builder.append(line);
        }
        return builder.toString();
    }
}

Access Public Static Constants by Using Classes

Though public static constants can be accessed through class instances, this may lead to the misunderstanding that the instances of each class have a public static constant. We recommend that you access public static constants through classes.

Bad code:

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

User user = new User();
String nameKey = user.CONST_NAME;

Good code:

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

String nameKey = User.CONST_NAME;

Do Not Use NullPointerException to Determine Null Pointers

Prevent null pointer exceptions through coding (for example, no null values are detected) rather than through catching exceptions

Bad code:

public String getUserName(User user) {
    try {
        return user.getName();
    } catch (NullPointerException e) {
        return null;
    }
}

Good code:

public String getUserName(User user) {
    if (Objects.isNull(user)) {
        return null;
    }
    return user.getName();
}

Replace ""+value with String.valueOf(value)

Use String.valueOf(value) rather than ""+value to convert other objects or types into strings more efficiently.

Bad code:

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

Good code:

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

Add the @Deprecated Annotation to Outdated Code

If a segment of code is outdated but cannot be deleted for compatibility reasons, you can add the @Deprecated annotation to the code so that it will no longer be used. Add @deprecated to document comments to give an explanation and provide an alternative solution.

Good code:

/**
 * 保存
 *
 * @deprecated 此方法效率较低,请使用{@link newSave()}方法替换它
 */
@Deprecated
public void save(){
    // do something
}

Prevent Bugs in the Code

Do Not Use the Constructor Method BigDecimal(double)

BigDecimal(double) can cause accuracy losses and abnormal business logic during precise computation or value comparison.

Bad code:

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

Good code:

BigDecimal value = BigDecimal.valueOf(0.1D);; // 0.1

Return Null Arrays or Collections Rather Than Null Values

To return null values, require the caller to detect null values. Otherwise, a null pointer exception may be thrown. Returning null arrays or collections can prevent null pointer exceptions from being thrown when the caller does not detect null values. To simplify the code, you can delete the statement that instructs the caller to detect null values.

Bad code:

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

Good code:

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

Call the equals Method by Using Constants or Determined Values

The equals method for objects often throws null pointer exceptions. To solve this problem, call the equals method by using constants or objects with determined values. The best solution is to use the java.util.Objects.equals() method.

Bad code:

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

Good code:

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

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

Only Use Private and Unchangeable Enumerated Property Fields

Enumeration is often used in the same way as constants. If enumeration contains public property fields or field setting methods, the properties of these enumerated constants are prone to modification. Ideally, enumerated property fields are private and assigned values in private constructors. We recommend that you add the final modifier due to the lack of the Setter method.

Bad code:

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;
    }
}

Good code:

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;
    }
}

Pay Attention to String.split(String regex)

The string-specific split method passes a separator string which is a regular expression. Some keywords, such as .[]() \|, must be escaped.

Bad code:

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

Good code:

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

Summary

And that's it. I hope that you also learned a thing or two from Wang Yangming's wise words. If nothing else, I hope that our Java coding guide helps you write more effective and elegant code.

Are you eager to know the latest tech trends in Alibaba Cloud? Hear it from our top experts in our newly launched series, Tech Show!

0 0 0
Share on

Alibaba Cloud Native

208 posts | 12 followers

You may also like

Comments