1.选择正确的collection/map
请参照此图正确的使用集合
- 我需要排序吗?
- 我会有空的键/值?DUPS?
- 它会支持多个线程访问吗?
- 我需要使用键/值对吗?
- 我需要随机访问吗?
- 它允许按索引访问元素吗?
- 它提供快速添加和快速移除元素吗?
2.使用接口,而不是他的实现类作为类型
1.始终使用接口类型作为类型。
例如, 使用 List, Set, Map 等接口作为引用类型
// 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 <>();
当你的代码设计为依赖于List接口时,你可以轻松地在List的实现之间进行交换,而无需修改使用它的代码。
2.始终使用接口类型作为返回类型
例如
public Collection listEmployees() {
List<Employee> employees = new ArrayList<>();
// add Employees to the list
return employees;
}
3.始终使用接口类型作为方法参数
public void foo(Set < Integer > numbers){
}
在方法有参数的情况下,使用接口作为参数类型会更加的灵活。
3.使用通用类型和泛型操作符
// Avoid
List<Student> listStudents = new ArrayList<Student>();
// Better
List<Student> listStudents = new ArrayList<>();
// Avoid
Map<Integer, Map<String, Student>> map = new HashMap<Integer, Map<String, Student>>();
//Better
Map<Integer, Map<String, Student>> map = new HashMap<>();
4.集合判空首选isEmpty(),少用size()
避免这种方法判断集合是否为空:if (listOfEmployees.size() > 0) {
// dos something if the list is not empty
}
相反,你应该使用isEmpty()方法:
if (!listOfEmployees.isEmpty()) {
// dos something if the list is not empty
}
isEmpty()和size()之间没有性能差异。这是为了增强代码的可读性。
5.不要在返回集合的方法中返回null
如果一个方法被设计为返回一个集合,那么在集合中没有元素的情况下它不应该返回null。考虑以下方法:
public List<Student> findStudents(String className) {
List<Student> listStudents = null;
if (//students are found//) {
// add students to the lsit
}
return listStudents;
}
在这里,如果找不到学生,该方法返回null。这里的关键点是,不应使用空值来表示没有结果。最好的做法是,返回一个空集合来表示没有结果。上述代码可以通过初始化集合来轻松更正:
List<Student> listStudents = new ArrayList<>;
or
Collections.empty();
因此,请始终检查代码的逻辑,返回一个空的集合,而不是null。
6.不要使用经典的for循环
如果你编写代码来迭代像下面这样的列表集合,并没有什么不妥:
for (int i = 0; i < listStudents.size(); i++) {
Student aStudent = listStudents.get(i);
// do something with aStudent
}
但是,这并不是好的做法,因为如果在循环内某处改变,使用计数器变量I可能会导致潜在的错误。此外,这种循环不是面向对象的,因为每个集合都有自己的迭代器。因此建议使用类似以下代码的迭代器:
Iterator<Student> iterator = listStudents.iterator();
while (iterator.hasNext()) {
Student nextStudent = iterator.next();
// do something with next student
}
for (Student aStudent : listStudents) {
// do something with aStudent
}
正如你所看到的,因为增强型for循环在后台使用迭代器,所以更简洁易读。
7.推荐使用forEach()和Lambda表达式
从Java 8开始,每个集合都提供了将迭代代码封装在集合本身内部的forEach()方法(内部迭代),并且您只需将Lambda表达式传递给此方法即可。这使得迭代代码更加紧凑,更灵活,更强大。下面一个例子:
List<String> fruits = Arrays.asList("Banana", "Lemon", "Orange", "Apple");
fruits.forEach(fruit -> System.out.println(fruit));
这相当于以下增强的for循环:
for(String fruit : fruits){
System 。出去。的println(果实);
}
因此,我鼓励你使用该 forEach()
方法迭代集合,以帮助您专注于代码,而不是迭代。
8.正确的覆盖equals()和hashCode()方法
你必须在每个重写equals()的类里面重写hashCode()方法
。如果不这样做将导致违反Object.hashCode()的常规约定,这将阻止 你的类与所有基于散列的集合(hash-based collections)(包括HashMap,HashSet和Hashtable)一起正常运行。
9.正确实现Comparable接口
请记住,Comparable
这个接口,当你自定义类型的元素被添加到按自然顺序排序元素的集合中时,你的自定义类型会实现该接口,例如 TreeSet
和 TreeMap
。它还有助于根据元素的自然排序对列表集合中的元素进行排序。
10.使用数组和集合时,使用实用程序类(utility classes)
请注意,Java集合框架提供了两个实用程序类 Arrays
, Collections
它们为我们提供了许多有用的功能。例如, Arrays.asList()
方法返回包含给定元素的列表集合,如你所见,我在许多示例中使用了此方法:
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);
Collections类提供了各种有用的方法来 搜索,排序,修改 集合中的元素(几乎在列表中)。因此,在查找其他库或编写自己的代码之前,请记住查看这两个实用程序类的可重用方法。
11.优先使用concurrent collections ,少用 synchronized 块
当你必须在多线程应用程序中使用集合时,请考虑在java.util.concurrent
包中使用并发集合, 而不是使用Collections.synchronizedXXX()
方法生成的同步集合 。
这是因为并发集合旨在通过实现不同的同步机制(如写时复制,比较和交换以及特殊锁定),在并发应用程序中可以达到最好性能。以下列表显示了如何选择一些并发集合(右侧),它们与正常集合(左侧)相同:
- HashMap -> ConcurrentHashMap
- ArrayList -> CopyOnWriteArrayList
- TreeMap -> ConcurrentSkipListMap
- PriorityQueue -> PriorityBlockingQueue
本文翻译自谷歌Java社群,由于本人水平有限,可能翻译的不够准确,如有错误欢迎指正!
原文链接:http://java-developers-guide.blogspot.com/2018/06/guide-to-best-practices-to-java.html?spref=gp