×
Community Blog 50 Efficient Code Samples for Java Programming

50 Efficient Code Samples for Java Programming

This article is a list of 50 efficient Java code samples.

There are only two qualities in the world: efficiency and inefficiency, and only two sorts of people: the efficient and the inefficient. -- Bernard Shaw

I would like to rephrase this quote of Bernard Shaw as follows: There are only two kinds of code: efficient code and inefficient code, and there are only two kinds of programmers in the world: programmers who write efficient code and programmers who write inefficient code. Every R&D team has to find a way to write efficient code. This article provides 50 efficient code samples based on the author's actual experience and extensive reading of reference materials. I hope this will enable all Java programmers to write efficient code.

1. Constants and Variables

1.1 Directly Assign a Constant Value, Without Declaring a New Object

When you directly assign a constant value, you only create an object reference that points to the constant value.

Bad code:

Long i = new Long(1L);
String s = new String("abc");

Good code:

Long i = 1L;
String s = "abc";

1.2 Define an Unchanged Member Variable Value as a Static Constant

In each object instance of a class, each member variable has a copy, and each member static constant has only one instance.

Bad code:

public class HttpConnection {
    private final long timeout = 5L;
    ...
}

Good code:

public class HttpConnection {
    private static final long TIMEOUT = 5L;
    ...
}

1.3 Use Elementary Data Types to Avoid Automatic Packing and Unpacking

The elementary data types in Java are double, float, long, int, short, char, and boolean, which correspond to the packing classes Double, Float, Long, Integer, Short, Character, and Boolean, respectively. Java virtual machines (JVMs) support automatic conversion between elementary data types and packing classes, which is called automatic packing and unpacking. Packing and unpacking consume CPU and memory resources, so you should avoid automatic packing and unpacking as much as possible.

Bad code:

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

Good code:

int sum = 0;
int[] values = ...;
for (int value : values) {
    sum += value;
}

1.4 Do Not Assign an Initial Value to a Variable Whose Initial Value May Be Overwritten

Bad code:

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

Good code:

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

1.5 Use Temporary Variables of the Elementary Type in a Function

Parameters and temporary variables of the elementary type in a function are stored in stacks and easy to access. For parameters and temporary variables of the object type, related references are stored in stacks, but content is stored in heaps. This lowers the access speed. All types of member variables in a class are stored in heaps and access is slow.

Bad code:

public final class Accumulator {
    private double result = 0.0D;
    public void addAll(@NonNull double[] values) {
        for(double value : values) {
            result += value;
        }
    }
    ...
}

Good code:

public final class Accumulator {
    private double result = 0.0D;
    public void addAll(@NonNull double[] values) {
        double sum = 0.0D;
        for(double value : values) {
            sum += value;
        }
        result += sum;
    }
    ...
}

1.6 Do Not Define Variables Outside a Loop

Earlier versions of the Java Development Kit (JDK) do not provide good support for defining variables outside a loop. However, later JDK versions have improved in this respect. An analysis of compiled bytecode indicates that code with intra-loop defined variables and code with extra-loop defined variables have the same runtime efficiency. According to the principle of minimizing the scope of local variables, intra-loop defined variables can avoid delayed recycling due to the prolonged lifecycle of large objects.

Bad code:

UserVO userVO;
List<UserDO> userDOList = ...;
List<UserVO> userVOList = new ArrayList<>(userDOList.size());
for (UserDO userDO : userDOList) {
    userVO = new UserVO();
    userVO.setId(userDO.getId());
    ...
    userVOList.add(userVO);
}

Good code:

List<UserDO> userDOList = ...;
List<UserVO> userVOList = new ArrayList<>(userDOList.size());
for (UserDO userDO : userDOList) {
    UserVO userVO = new UserVO();
    userVO.setId(userDO.getId());
    ...
    userVOList.add(userVO);
}

1.7 Use Non-thread Security Classes for Unchanged Static Constants

Non-thread security classes are applicable to unchanged static constants with support for multithread access.

Bad code:

public static final Map<String, Class> CLASS_MAP;
static {
    Map<String, Class> classMap = new ConcurrentHashMap<>(16);
    classMap.put("VARCHAR", java.lang.String.class);
    ...
    CLASS_MAP = Collections.unmodifiableMap(classMap);
}

Good code:

public static final Map<String, Class> CLASS_MAP;
static {
    Map<String, Class> classMap = new HashMap<>(16);
    classMap.put("VARCHAR", java.lang.String.class);
    ...
    CLASS_MAP = Collections.unmodifiableMap(classMap);
}

1.8 Use Non-thread Security Classes for Unchanged Member Variables

Non-thread security classes are applicable to unchanged member variables with support for multithread access.

Bad code:

@Service
public class StrategyFactory implements InitializingBean {
    @Autowired
    private List<Strategy> strategyList;
    private Map<String, Strategy> strategyMap;
    @Override
    public void afterPropertiesSet() {
        if (CollectionUtils.isNotEmpty(strategyList)) {
            int size = (int) Math.ceil(strategyList.size() * 4.0 / 3);
            Map<String, Strategy> map = new ConcurrentHashMap<>(size);
            for (Strategy strategy : strategyList) {
                map.put(strategy.getType(), strategy);
            }
            strategyMap = Collections.unmodifiableMap(map);
        }
    }
    ...
}

Good code:

@Service
public class StrategyFactory implements InitializingBean {
    @Autowired
    private List<Strategy> strategyList;
    private Map<String, Strategy> strategyMap;
    @Override
    public void afterPropertiesSet() {
        if (CollectionUtils.isNotEmpty(strategyList)) {
            int size = (int) Math.ceil(strategyList.size() * 4.0 / 3);
            Map<String, Strategy> map = new HashMap<>(size);
            for (Strategy strategy : strategyList) {
                map.put(strategy.getType(), strategy);
            }
            strategyMap = Collections.unmodifiableMap(map);
        }
    }
    ...
}

2. Objects and Classes

2.1 Do Not Use JSON to Convert Objects

JSON can convert between objects and JSON strings, so it may be used by some programmers to convert objects. Such object conversion works, but results in low performance.

Bad code:

List<UserDO> userDOList = ...;
List<UserVO> userVOList = JSON.parseArray(JSON.toJSONString(userDOList), UserVO.class);

Good code:

List<UserDO> userDOList = ...;
List<UserVO> userVOList = new ArrayList<>(userDOList.size());
for (UserDO userDO : userDOList) {
    UserVO userVO = new UserVO();
    userVO.setId(userDO.getId());
    ...
    userVOList.add(userVO);
}

2.2 Do Not Assign Values to Objects Through Reflection

You may write less code when using reflection to assign values to objects, but this lowers the code performance.

Bad code:

List<UserDO> userDOList = ...;
List<UserVO> userVOList = new ArrayList<>(userDOList.size());
for (UserDO userDO : userDOList) {
    UserVO userVO = new UserVO();
    BeanUtils.copyProperties(userDO, userVO);
    userVOList.add(userVO);
}

Good code:

List<UserDO> userDOList = ...;
List<UserVO> userVOList = new ArrayList<>(userDOList.size());
for (UserDO userDO : userDOList) {
    UserVO userVO = new UserVO();
    userVO.setId(userDO.getId());
    ...
    userVOList.add(userVO);
}

2.3 Replace Internal Anonymous Classes with Lambda Expressions

Many novice users of JDK 8 may consider lambda expressions to be the syntax of internal anonymous classes. In fact, lambda expressions are implemented by the invokeDynamic instruction on many virtual machines, so they are more efficient than internal anonymous classes.

Bad code:

List<User> userList = ...;
Collections.sort(userList, new Comparator<User>() {
    @Override
    public int compare(User user1, User user2) {
        Long userId1 = user1.getId();
        Long userId2 = user2.getId();
        ...
        return userId1.compareTo(userId2);
    }
});

Good code:

List<User> userList = ...;
Collections.sort(userList, (user1, user2) -> {
    Long userId1 = user1.getId();
    Long userId2 = user2.getId();
    ...
    return userId1.compareTo(userId2);
});

2.4 Do Not Define Subclasses Unless Necessary

Additional class loading is required once a class is added.

Bad code:

public static final Map<String, Class> CLASS_MAP =
    Collections.unmodifiableMap(new HashMap<String, Class>(16) {
    private static final long serialVersionUID = 1L;
    {
        put("VARCHAR", java.lang.String.class);
    }
});

Good code:

public static final Map<String, Class> CLASS_MAP;
static {
    Map<String, Class> classMap = new HashMap<>(16);
    classMap.put("VARCHAR", java.lang.String.class);
    ...
    CLASS_MAP = Collections.unmodifiableMap(classMap);
}

2.5 Specify the Final Modifier for Each Class

You can specify the final modifier for a class so that the class cannot be inherited. If a class is specified to be final, all methods of the class are final methods, and the Java compiler can inline all final methods. Inlining greatly improves Java runtime efficiency. For more information, see Java runtime optimization. On average, this can improve performance by 50%.

Bad code:

public class DateHelper {
    ...
}

Good code:

public final class DateHelper {
    ...
}

Note: When you add a dynamic proxy to beans in Spring aspect-oriented programming (AOP), an exception may occur if the final modifier is specified for the bean class.

3. Methods

3.1 Declare Methods Unrelated to Class Member Variables as Static Methods

A static method can be directly called without creating a class instance. A static method does not belong to an object, but to the class where it is located. A static method can be accessed by using its class name, avoiding resource consumption due to repeated object creation. Declare a private method in a class as a static method even if the method does not use any class member variables.

Bad code:

public int getMonth(Date date) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    return calendar.get(Calendar.MONTH) + 1;
}

Good code:

public static int getMonth(Date date) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    return calendar.get(Calendar.MONTH) + 1;
}

3.2 Use the Elementary Data Type as the Method Parameter Type

Using the Elementary data type as the method parameter type can help you avoid unnecessary packing, unpacking, and null pointer judgment.

Bad code:

public static double sum(Double value1, Double value2) {
    double double1 = Objects.isNull(value1) ? 0.0D : value1;
    double double2 = Objects.isNull(value2) ? 0.0D : value2;
    return double1 + double2;
}
double result = sum(1.0D, 2.0D);

Good code:

public static double sum(double value1, double value2) {
    return value1 + value2;
}
double result = sum(1.0D, 2.0D);

3.3 Use the Elementary Data Type as the Method Return Value Type

Using the Elementary data type as the method return value type can help you avoid unnecessary packing, unpacking, and null pointer judgment. Many of the JDK class library methods use the elementary data type for return values. This avoids unnecessary packing and unpacking as well as null pointer judgment for return values. Examples: Collection.isEmpty() and Map.size().

Bad code:

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

// Calls code.
UserDO user = ...;
Boolean isValid = isValid(user);
if (Objects.nonNull(isValid) && isValid.booleanValue()) { 
    ...
}

Good code:

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

// Calls code.
UserDO user = ...;
if (isValid(user)) {
    ...
}

3.4 Set Non-Null Parameter Values for Protocol Methods

Setting non-null parameter values for protocol methods can help you avoid unnecessary null pointer judgment. In protocol programming, @NonNull and @Nullable can be used to tag parameters. It is up to the caller to follow the tagging.

Bad code:

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

Good code:

public static boolean isValid(@NonNull UserDO user) {
    return Boolean.TRUE.equals(user.getIsValid());
}

3.5 Set Non-Null Return Values for Protocol Methods

Setting non-null return values for protocol methods can help you avoid unnecessary null pointer judgment. In protocol programming, @NonNull and @Nullable can be used to tag parameters. It is up to the implementer to follow the tagging.

Bad code:

// Defines an interface.
public interface OrderService {
    public List<OrderVO> queryUserOrder(Long userId);
}

// Calls code.
List<OrderVO> orderList = orderService.queryUserOrder(userId);
if (CollectionUtils.isNotEmpty(orderList)) {
    for (OrderVO order : orderList) {
        ...
    }
}

Good code:

// Defines an interface.
public interface OrderService {
    @NonNull
    public List<OrderVO> queryUserOrder(Long userId);
}

// Calls code.
List<OrderVO> orderList = orderService.queryUserOrder(userId);
for (OrderVO order : orderList) {
    ...
}

3.6 No Need for Null Judgment by the Calling Method When the Called Method Supports Null Judgment

Bad code:

UserDO user = null;
if (StringUtils.isNotBlank(value)) {
    user = JSON.parseObject(value, UserDO.class);
}

Good code:

UserDO user = JSON.parseObject(value, UserDO.class);

3.7 Avoid Unnecessary Function Encapsulation

Method calls may cause new data items to be pushed into or pulled from a stack, which consumes CPU and memory resources. Therefore, avoid unnecessary function encapsulation. However, the performance loss is worthwhile if method calls are added to make code more concise and clearer.

Bad code:

// Encapsulates the function.
public static boolean isVip(Boolean isVip) {
    return Boolean.TRUE.equals(isVip);
}

// Uses code.
boolean isVip = isVip(user.getVip());

Good code:

boolean isVip = Boolean.TRUE.equals(user.getVip());

3.8 Specify the Final Modifier for Each Method

You can prevent a method from being overwritten by specifying the final modifier for the method. The Java compiler may inline all final methods. Inlining greatly improves Java runtime efficiency. On average, this can improve performance by 50%.

Note: All private methods are implicitly specified with the final modifier, so you do not need to specify the final modifier for them.

Bad code:

public class Rectangle {
    ...
    public double area() {
        ...
    }
}

Good code:

public class Rectangle {
    ...
    public final double area() {
        ...
    }
}

Note: When you add a dynamic proxy to beans in Spring AOP, methods with the final modifier are not proxied.

4. Expressions

4.1 Minimize Repeated Calls to Methods

Bad code:

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

Good code:

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

4.2 Avoid Unnecessary Method Calls

Bad code:

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

Good code:

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

4.3 Use Shift Instead of Positive Integer Multiplication and Division

The shift operation can greatly improve performance. You can use the shift operation to calculate a positive integer multiplied or divided by 2^n (n is a positive integer).

Bad code:

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

Good code:

int num1 = a << 2;
int num2 = a >> 2;

4.4 Fetch Public Expressions to Avoid Repeated Calculation

Only one value is calculated and used repeatedly when a public expression is fetched.

Bad code:

double distance = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));

Good code:

double dx = x2 - x1;
double dy = y2 - y1;
double distance = Math.sqrt(dx * dx + dy * dy);
Or
double distance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));

4.5 Do Not Use ! in Conditional Expressions

If ! is used in the non-operator position, one more calculation is performed. Do not use ! unless it is necessary.

Bad code:

if (!(a >= 10)) {
    ... // Conditional processing 1
} else {
    ... // Conditional processing 2
}

Good code:

if (a < 10) {
    ... // Conditional processing 1
} else {
    ... // Conditional processing 2
}

4.6 Use a Switch Statement Instead of an If-Else Statement for Multi-constant Selection Branches

In an if-else statement, each if conditional statement performs calculation until the true if statement appears. A switch statement optimizes jumps and is executed by the tableswitch or lookupswitch instruction in Java. It can process multi-constant selection branches more efficiently than an if-else statement. According to experiments, an if-else statement is more efficient than a switch statement when processing less than five branches, whereas a switch statement is more efficient than an if-else statement when processing more than five branches, assuming that the occurrence probability of each branch is the same.

Bad code:

if (i == 1) {
    ... ; // Branch 1
} else if (i == 2) {
    ... ; // Branch 2
} else if (i == ...) {
    ... ; // Branch n
} else {
    ... ; // Branch n + 1
}

Good code:

switch (i) {
    case 1 :
        ... // Branch 1
        break;
    case 2 :
        ... // Branch 2
        break;
    case ... :
        ... // Branch n
        break;
    default :
        ... // Branch n + 1
        break;
}

Note: You can use Map to implement the strategy pattern for complex services.

5. Strings

5.1 Do Not Use Regular Expressions for Matching

Regular expression matching is inefficient. Use string matching instead.

Bad code:

String source = "a::1,b::2,c::3,d::4";
String target = source.replaceAll("::", "=");
Stringp[] targets = source.spit("::");

Good code:

String source = "a::1,b::2,c::3,d::4";
String target = source.replace("::", "=");
Stringp[] targets = StringUtils.split(source, "::");

Note: The StringUtils.split function does not retain empty strings.

5.2 Replace Strings with Characters

The string length is variable, whereas the character length is fixed to 1, so it is more efficient to query and match using characters.

Bad code:

String source = "a:1,b:2,c:3,d:4";
int index = source.indexOf(":");
String target = source.replace(":", "=");

Good code:

String source = "a:1,b:2,c:3,d:4";
int index = source.indexOf(':');
String target = source.replace(':', '=');

5.3 Use StringBuilder for String Concatenation

Strings belong to the final class and their content cannot be modified. Therefore, an object is created once strings are concatenated. During initialization, StringBuilder applies for a memory where subsequent strings are concatenated. No additional memory is applied for and no objects are created during the concatenation process.

Bad code:

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

Good code:

StringBuilder sb = new StringBuilder(128);
for (int i = 0; i < 10; i++) {
    if (i != 0) {
        sb.append(',');
    }
    sb.append(i);
}

5.4 Do Not Use + to Convert Strings

Using + to convert strings is convenient but inefficient. Use String.valueOf instead, which is much more efficient.

Bad code:

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

Good code:

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

5.5 Precompile Regular Expressions

The Pattern.compile method has a high performance overhead and may be hidden in methods considered convenient, such as the String.matches, String.replaceAll, and String.split functions. Example:

public String replaceAll(String regex, String replacement) {
    return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}

If you call these methods multiple times, you can precompile a regular expression to improve execution efficiency. You can also compile the optimized code based on the original code.

Bad code:

// The following code will be called N times.
String source = "a:1|b:2|c:3|d:4";
boolean isMatched = source.matches("^(\\w+:\\d+\\|)*(\\w+:\\d+)$"); // true
String target = source.replaceAll("(\\w+):(\\d+)", "$1=$2"); // a=1|b=2|c=3|d=4
String[] targets = source.split("\\|"); // ["a:1","b:2","c:3","d:4"]

Good code:

// Precompiles a regular expression.
final Pattern PATTERN1 = Pattern.compile("^(\\w+:\\d+\\|)*(\\w+:\\d+)$");
final Pattern PATTERN2 = Pattern.compile("(\\w+):(\\d+)");
final Pattern PATTERN3 = Pattern.compile("\\|");

// The following code will be called N times.
String source = "a:1|b:2|c:3|d:4";
boolean isMatched = PATTERN1.matcher(source).matches();
String target = PATTERN2.matcher(source).replaceAll("$1=$2");
String[] targets = PATTERN3.split(source);

6. Arrays

6.1 Use System.arraycopy Instead of Loops to Copy Arrays

Use System.arraycopy or Arrays.copyOf to copy arrays.

Bad code:

int[] sources = new int[] {1, 2, 3, 4, 5};
int[] targets = new int[sources.length];
for (int i = 0; i < targets.length; i++) {
    targets[i] = sources[i];
}

Good code:

int[] sources = new int[] {1, 2, 3, 4, 5};
int[] targets = new int[sources.length];
System.arraycopy(sources, 0, targets, 0, targets.length);

6.2 When Converting a Collection to a T-type Array, Pass in the Empty Array T[0]

You can convert a collection to an Array by using toArray(new T[n]) or toArray(new T[0]). In earlier Java versions, use toArray(new T[n]) because the reflection required to create an array is slow. In OpenJDK 6 and later versions with internal reflection, toArray(new T[0]) is more efficient and performs better than toArray(new T[n]). toArray(new T[n]) gets the list size one more time than toArray(new T[0]). toArray(new T[n]) is less efficient if it takes much time to calculate the list size.

Bad code:

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, ...);
Integer[] integers = integerList.toArray(new Integer[integerList.size()]);

Good code:

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, ...);
Integer[] integers = integerList.toArray(new Integer[0]); // Do not use new Integer[]{}.

Note: A toArray(Class\<T\> clazz) method must be provided for a collection to avoid useless empty array initialization (new T[0]).

6.3 Use toArray() to Convert a Collection to an Object Array

Use toArray() instead of toArray[new Object[0]] to convert a collection to an object array. toArray() is more efficient because it avoids type judgment and empty array application.

Bad code:

List<Object> objectList = Arrays.asList(1, "2", 3, "4", 5, ...);
Object[] objects = objectList.toArray(new Object[0]);

Good code:

List<Object> objectList = Arrays.asList(1, "2", 3, "4", 5, ...);
Object[] objects = objectList.toArray();

7. Collections

7.1 Specify the Size of the Collection to Be Initialized

When a Java collection is initialized, a default size is specified for this collection. Resizing is performed when the default size does not meet data requirements. The time complexity for the resize operation may be O(n). Specify the collection size if it is predictable to avoid resizing or reduce the number of resizes.

Bad code:

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

Good code:

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

7.2 Use JDK-provided Methods Instead of Loops to Copy Collections

JDK provides methods to specify the collection capacity in one step, which saves time and space by avoiding repeated resizing. These methods are executed by calling System.arraycopy and therefore support efficient batch data copy.

Bad code:

List<UserDO> user1List = ...;
List<UserDO> user2List = ...;
List<UserDO> userList = new ArrayList<>(user1List.size() + user2List.size());
for (UserDO user1 : user1List) {
    userList.add(user1);
}
for (UserDO user2 : user2List) {
    userList.add(user2);
}

Good code:

List<UserDO> user1List = ...;
List<UserDO> user2List = ...;
List<UserDO> userList = new ArrayList<>(user1List.size() + user2List.size());
userList.addAll(user1List);
userList.addAll(user2List);

7.3 Use Arrays.asList to Convert an Array to a List

For detail about why you should use Arrays.asList to convert an Array to a List, see section 7.2 of this post. Consider the example code as well.

Bad code:

List<String> typeList = new ArrayList<>(8);
typeList.add("Short");
typeList.add("Integer");
typeList.add("Long");

String[] names = ...;
List<String> nameList = ...;
for (String name : names) {
    nameList.add(name);
}

Good code:

List<String> typeList = Arrays.asList("Short", "Integer", "Long");

String[] names = ...;
List<String> nameList = ...;
nameList.addAll(Arrays.asList(names));

7.4 Directly Iterate Required Collections

This operation allows you to get data directly. No other operations are required.

Bad code:

Map<Long, UserDO> userMap = ...;
for (Long userId : userMap.keySet()) {
    UserDO user = userMap.get(userId);
    ...
}

Good code:

Map<Long, UserDO> userMap = ...;
for (Map.Entry<Long, UserDO> userEntry : userMap.entrySet()) {
    Long userId = userEntry.getKey();
    UserDO user = userEntry.getValue();
    ...
}

7.5 Use an isEmpty Instead of a Size Method to Detect Empty Values

Logically, it is reasonable to detect empty values by using a size method, but you can also use an isEmpty method to make your code more readable and perform better. The time complexity for implementing any isEmpty method is invariably O(1), whereas the time complexity for implementing some size methods may be O(n).

Bad code:

List<UserDO> userList = ...;
if (userList.size() == 0) {
    ...
}
Map<Long, UserDO> userMap = ...;
if (userMap.size() == 0) {
    ...
}

Good code:

List<UserDO> userList = ...;
if (userList.isEmpty()) {
    ...
}
Map<Long, UserDO> userMap = ...;
if (userMap.isEmpty()) {
    ...
}

7.6 Use Iteration Instead of Random Access to Traverse Non-random-Access Lists

Lists can be classified into random-access lists and non-random-access lists. You can determine the type of a list based on whether the RandomAccess interface is implemented. It is efficient to retrieve random-access lists by using the GET method. However, it is inefficient to retrieve non-random-access lists by using the GET method.

Bad code:

LinkedList<UserDO> userDOList = ...;
int size = userDOList.size();
for (int i = 0; i < size; i++) {
    UserDO userDO = userDOList.get(i);
    ...
}

Good code:

LinkedList<UserDO> userDOList = ...;
for (UserDO userDO : userDOList) {
    ...
}

Use iteration to traverse random-access and non-random-access lists.

7.7 Use HashSet to Determine Whether a Value Exists

In Java collection libraries, the time complexity of the contains method for List is typically O(n), whereas the time complexity of HashSet is O(1). Convert List to HashSet when you need to frequently call the contains method to search data.

Bad code:

List<Long> adminIdList = ...;
List<UserDO> userDOList = ...;
List<UserVO> userVOList = new ArrayList<>(userDOList.size());
for (UserDO userDO : userDOList) {
    if (adminIdList.contains(userDO.getId())) {
        userVOList.add(transUser(userDO));
    }
}

Good code:

Set<Long> adminIdSet = ...;
List<UserDO> userDOList = ...;
List<UserVO> userVOList = new ArrayList<>(userDOList.size());
for (UserDO userDO : userDOList) {
    if (adminIdSet.contains(userDO.getId())) {
        userVOList.add(transUser(userDO));
    }
}

7.8 Avoid Determining Whether Data Exists Before Retrieving the Data

You can directly retrieve data and determine whether the data is empty rather than determine whether the data exists before retrieving it. This avoids repeated searching.

Bad code:

public static UserVO transUser(UserDO user, Map<Long, RoleDO> roleMap) {
    UserVO userVO = new UserVO();
    userVO.setId(user.getId());
    ...
    if (roleMap.contains(user.getRoleId())) {
        RoleDO role = roleMap.get(user.getRoleId());
        userVO.setRole(transRole(role));
    }
}

Good code:

public static UserVO transUser(UserDO user, Map<Long, RoleDO> roleMap) {
    UserVO userVO = new UserVO();
    userVO.setId(user.getId());
    ...
    RoleDO role = roleMap.get(user.getRoleId());
    if (Objects.nonNull(role)) {
        userVO.setRole(transRole(role));
    }
}

8. Exceptions

8.1 Directly Capture Exceptions

Capture exceptions directly rather than using instanceof for judgment. This makes your code more concise and improves its runtime efficiency.

Bad code:

try {
    saveData();
} catch (Exception e) {
    if (e instanceof IOException) {
        log.error("An I/O exception occurred while saving data.", e);
    } else {
        log.error("Other exceptions occurred while saving data.", e);
    }
}

Good code:

try {
    saveData();
} catch (IOException e) {
    log.error("An I/O exception occurred while saving data.", e);
} catch (Exception e) {
    log.error("Other exceptions occurred while saving data.", e);
}

8.2 Do Not Capture Exceptions in a Loop

If the loop body throws an exception and you do not need to repeat the loop, you do not need to capture the exception in the loop body. This is because too many exceptions may reduce the program execution efficiency.

Bad code:

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

Good code:

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

8.3 Do Not Use Exceptions to Control Business Processes

Compared with conditional expressions, exceptions are less efficient in business process control.

Bad code:

public static boolean isValid(UserDO user) {
    try {
        return Boolean.TRUE.equals(user.getIsValid());
    } catch(NullPointerException e) {
        return false;
    }
}

Good code:

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

9. Buffers

9.1 Specify the Buffer Size During Initialization

During initialization, specify the expected buffer capacity to avoid wasting time and space due to repeated resizing.

Bad code:

StringBuffer buffer = new StringBuffer();
StringBuilder builder = new StringBuilder();

Good code:

StringBuffer buffer = new StringBuffer(1024);
StringBuilder builder = new StringBuilder(1024);

9.2 Reuse the Same Buffer

To maintain buffers, JVMs need to spend time on object creation and garbage collection. Therefore, reuse the same buffer if possible.

Bad code:

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());
...

Good code:

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());
...

You can use the setLength method to reset the buffer size to 0.

9.3 Design Buffer Reuse

Design buffer reuse to improve program runtime efficiency.

Bad code:

// Converts XML (UserDO).
public static String toXml(UserDO user) {
    StringBuilder builder = new StringBuilder(128);
    builder.append("<UserDO>");
    builder.append(toXml(user.getId()));
    builder.append(toXml(user.getName()));
    builder.append(toXml(user.getRole()));
    builder.append("</UserDO>");
    return builder.toString();
}
// Converts XML (Long).
public static String toXml(Long value) {
    StringBuilder builder = new StringBuilder(128);
    builder.append("<Long>");
    builder.append(value);
    builder.append("</Long>");
    return builder.toString();
}
...

// Uses code.
UserDO user = ...;
String xml = toXml(user);

Good code:

// Converts XML (UserDO).
public static void toXml(StringBuilder builder, UserDO user) {
    builder.append("<UserDO>");
    toXml(builder, user.getId());
    toXml(builder, user.getName());
    toXml(builder, user.getRole());
    builder.append("</UserDO>");
}
// Converts XML (Long).
public static void toXml(StringBuilder builder, Long value) {
    builder.append("<Long>");
    builder.append(value);
    builder.append("</Long>");
}
...

// Uses code.
UserDO user = ...;
StringBuilder builder = new StringBuilder(1024);
toXml(builder, user);
String xml = builder.toString();

Remove the buffer application in each conversion method and apply for a buffer for each conversion method. This reduces the time spent releasing buffer applications and saves temporary storage space in buffers.

9.4 Use Buffered Streams to Reduce I/O Operations

Buffered streams such as BufferedReader, BufferedWriter, BufferedInputStream, and BufferedOutputStream can greatly reduce I/O occurrences and improve the I/O speed.

Bad code:

try (FileInputStream input = new FileInputStream("a");
    FileOutputStream output = new FileOutputStream("b")) {
    int size = 0;
    byte[] temp = new byte[1024];
    while ((size = input.read(temp)) != -1) {
        output.write(temp, 0, size);
    }
} catch (IOException e) {
    log.error("An exception occurred while copying the file.", e);
}

Good code:

try (BufferedInputStream input = new BufferedInputStream(new FileInputStream("a"));
    BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream("b"))) {
    int size = 0;
    byte[] temp = new byte[1024];
    while ((size = input.read(temp)) != -1) {
        output.write(temp, 0, size);
    }
} catch (IOException e) {
    log.error("An exception occurred while copying the file.", e);
}

You can manually specify the buffered stream size as needed to maximize the buffering effect of buffered streams.

10. Threads

10.1 Use Non-Thread-Safe Classes in a Single Thread

Non-thread-safe classes can avoid unnecessary synchronization overhead.

Bad code:

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

Good code:

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

10.2 Use Thread-Safe Classes in Multithreading

Use thread-safe classes rather than synchronize code to make your code more concise and efficient.

Bad code:

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

Good code:

private final AtomicInteger counter = new AtomicInteger(0);
public void access(Long userId) {
    counter.incrementAndGet();
    ...
}

10.3 Minimize the Scope of Synchronized Blocks

In a method, only a small portion of the logic may require synchronous control. The code efficiency may be affected if the entire method is subject to synchronous control. Therefore, minimize the scope of synchronized blocks and only synchronize the target code.

Bad code:

private volatile int counter = 0;
public synchronized void access(Long userId) {
    counter++;
    ... // Non-synchronous operation
}

Good code:

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

10.4 Merge Multiple Synchronized Blocks into One

Synchronized blocks incur performance overhead. Merge multiple synchronized blocks into one if possible.

Bad code:

// Processes a single order.
public synchronized handleOrder(OrderDO order) {
    ...
}

// Processes all orders.
public void handleOrder(List<OrderDO> orderList) {
    for (OrderDO order : orderList) {
        handleOrder(order);
    }
}

Good code:

// Processes a single order.
public handleOrder(OrderDO order) {
    ...
}

// Processes all orders.
public synchronized void handleOrder(List<OrderDO> orderList) {
    for (OrderDO order : orderList) {
        handleOrder(order);
    }
}

10.5 Use a Thread Pool to Reduce Thread Overhead

Overhead is incurred in multithreading when a thread is created and when the context is switched. Such overhead can be avoided by using a thread pool.

Bad code:

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

Good code:

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

Summary

The author is an IT engineer who has been working on the frontlines of programming for many years at Alibaba. He has an interest in programming techniques that solve practical problems.

For more information about Java programming, see Java Programming Techniques - Data Structures.

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

Changyi

5 posts | 1 followers

You may also like

Comments

Changyi

5 posts | 1 followers

Related Products