📘 Premium Read: Access my best content on Medium member-only articles — deep dives into Java, Spring Boot, Microservices, backend architecture, interview preparation, career advice, and industry-standard best practices.
✅ Some premium posts are free to read — no account needed. Follow me on Medium to stay updated and support my writing.
🎓 Top 10 Udemy Courses (Huge Discount): Explore My Udemy Courses — Learn through real-time, project-based development.
▶️ Subscribe to My YouTube Channel (172K+ subscribers): Java Guides on YouTube
Learn collections framework at https://www.javaguides.net/p/java-collections-tutorial.htmlI found the below few of the Java collections best practices very useful and we will discuss the same in this article.
- Choosing the right collection/map
- Code for Interface, not for Implementation
- Use generic type and diamond operator
- Prefer isEmpty() over a size()
- Return empty collections or arrays, not nulls
- Do not use the classic for loop
- Favor using forEach() with Lambda expressions
- Overriding equals() and hashCode() properly
- Implementing the Comparable interface properly
- Using Arrays and Collections utility classes
- Prefer concurrent collections over synchronized wrappers
- Use EnumSet instead of bit fields
Video
1. Choosing the right collection/map
- Do I need the order to remain?
- Will I have null keys/values? Dups?
- Will it be accessed by multiple threads
- Do I need a key/value pair
- Will I need random access?
- Does it allow accessing elements by index?
- Does it offer fast adding and fast removing elements?
2. Code for Interface, not for Implementation
1. Always use interface type as a reference type.
For example, use List, Set, and Map interfaces as a reference type.// Better
List<String> list = new ArrayList<>();
// Avoid
ArrayList<String> list = new ArrayList<>();
// Better
Set<String> set = new HashSet<>();
//Avoid
HashSet<String> employees = new HashSet<>();
// Better
Map<String,String> map = new HashMap<>();
//Avoid
HashMap<String,String> map = new HashMap<>();
List<String> list = new LinkedList<>();
2. Always use interface type as a return type
For example,public Collection listEmployees() {
List<Employee> employees = new ArrayList<>();
// add Employees to the list
return employees;
}
3. Always use Interface Types as a method argument
public void foo(Set<Integer> numbers) {
}
3. Use generic type and diamond operator
List<Employee> listEmployees = new ArrayList<Employee>();
List<Employee> listEmployees = new ArrayList<>();
Map<Integer, Map<String, Employee>> map = new HashMap<Integer, Map<String, Employee>>();
Map<Integer, Map<String, Employee>> map = new HashMap<>();
4. Prefer isEmpty() over a size()
if (listOfEmployees.size() > 0) {
// dos something if the list is not empty
}
if (!listOfEmployees.isEmpty()) {
// dos something if the list is not empty
}
5. Return empty collections or arrays, not nulls
If a method is designed to return a collection, it should not return null in case there’s no element in the collection.
public List<Student> findStudents(String className) {
List<Student> listStudents = null;
if (//students are found//) {
// add students to the lsit
}
return listStudents;
}
List<Student> listStudents = new ArrayList<>;
Collections.empty();In summary, never return null in place of an empty array or collection. It makes your API more difficult to use and more prone to error, and it has no performance advantages.
6. Do not use the classic for loop
for (int i = 0; i < listStudents.size(); i++) {
Student aStudent = listStudents.get(i);
// do something with aStudent
}
Iterator<Student> iterator = listStudents.iterator();
while (iterator.hasNext()) {
Student nextStudent = iterator.next();
// do something with next student
}
Now, it’s better to use the enhanced for loop like this:
for (Student aStudent : listStudents) {
// do something with aStudent
}
7. Favor using forEach() with Lambda expressions
List<String> fruits = Arrays.asList("Banana", "Lemon", "Orange", "Apple");
fruits.forEach(fruit -> System.out.println(fruit));
for (String fruit : fruits) {
System.out.println(fruit);
}
8. Overriding equals() and hashCode() properly
9. Implementing the Comparable interface properly
10. Using Arrays and Collections utility classes
Arrays
List<String> listFruits = Arrays.asList("Apple", "Banana", "Orange");
List<Integer> listIntegers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Double> listDoubles = Arrays.asList(0.1, 1.2, 2.3, 3.4);
// binary search algorithm. final String key = "abc"; String[] strArray = { "abc", "cdf", "pqr" }; int index = Arrays.binarySearch(strArray, key); System.out.println(" String key found at index : " + index); // Searches the specified array of ints for the specified value using // the binary search algorithm. int[] intArray = { 1, 2, 3, 4 }; index = Arrays.binarySearch(intArray, 3); System.out.println(" String key found at index : " + index); // Searches the specified array of bytes for the specified value using // the binary search algorithm. byte k = 1; byte[] byteArray = { 1, 2, 3, 4, 5 }; Arrays.binarySearch(byteArray, k);
Collections
List<String> list = new LinkedList<>(); list.add("element 2"); list.add("element 1"); list.add("element 4"); list.add("element 3"); // Sorts the specified list into ascending order, according to // the natural ordering of its elements. Collections.sort(list); for (String str : list) { System.out.println(" sort elements in ascending order --" + str); }
List<String> list = new LinkedList<>(); list.add("element 2"); list.add("element 1"); list.add("element 4"); list.add("element 3"); Collections.sort(list); for (String str : list) { System.out.println(" sort elements in ascending order --" + str); } int index = Collections.binarySearch(list, "element 4"); System.out.println("Element found at ::" + index);
11. Prefer concurrent collections over synchronized wrappers
It’s because the concurrent collections are designed to provide maximum performance in concurrent applications, by implementing different synchronization mechanisms like copy-on-write, compare-and-swap, and special locks. The following list shows you how to choose some concurrent collections (on the right) which are equivalent to the normal ones (on the left):
- HashMap -> ConcurrentHashMap
- ArrayList -> CopyOnWriteArrayList
- TreeMap -> ConcurrentSkipListMap
- PriorityQueue -> PriorityBlockingQueue
12. Use EnumSet instead of bit fields
What is the problem using bit fields?
If the elements of an enumerated type are used primarily in sets, it is traditional to use the int enum pattern, assigning a different power of 2 to each constant. For example:
// Bit field enumeration constants - OBSOLETE!
public static class Text {
public static final int STYLE_BOLD = 1 << 0; // 1
public static final int STYLE_ITALIC = 1 << 1; // 2
public static final int STYLE_UNDERLINE = 1 << 2; // 4
public static final int STYLE_STRIKE_THROUGH = 1 << 3; // 8
public Text() {
}
// parameter is bitwise OR of zero or more STYLE_ constants
public void applyStyles(int styles) {
// hard to interpret a bit field
// no easy way to iterate over all of the elements represented by a bit field
}
}
// Bit field enumeration constants - OBSOLETE!
public static class Text {
public static final int STYLE_BOLD = 1 << 0; // 1
public static final int STYLE_ITALIC = 1 << 1; // 2
public static final int STYLE_UNDERLINE = 1 << 2; // 4
public static final int STYLE_STRIKE_THROUGH = 1 << 3; // 8
public Text() {
}
// parameter is bitwise OR of zero or more STYLE_ constants
public void applyStyles(int styles) {
// hard to interpret a bit field
// no easy way to iterate over all of the elements represented by a bit field
}
}
What is the solution?
import java.util.EnumSet;
import java.util.Set;
public class ExampleEnumSet {
/** EnumSet - a modern replacement for bit fields. */
public static class TextEnumSet {
public enum Style {
BOLD, ITALIC, UNDERLINE, STRIKETHROUGH;
}
/** Any set can be passed but EnumSet is best. */
public void applyStyles(Set<Style> styles) {
}
}
public static void main(String[] args) {
TextEnumSet t2 = new TextEnumSet();
t2.applyStyles(EnumSet.of(TextEnumSet.Style.BOLD, TextEnumSet.Style.ITALIC));
}
}
Related Best Practices Posts
Complete Collection Framework Developer Guide: Java Collections Guide
This post is very informative and useful of Java Collection Framework best practices.
ReplyDeleteThanks, You should read this article http://www.javaguides.net/2018/06/guide-to-string-best-practices-in-java.html
DeleteIs really good and nice. I have questions about 11 position. We should always use concurentHashMap ? I'm not sure, whether in this case we must be aware that some data we can lost. Only synchronized gives us 100% data consistency. What you think about it ?
ReplyDelete