- 性能考虑,数组是首选
在Java中数组确实没有List、Set、Map这些集合类用起来方便,但在基本类型处理方面,数组还是占优势的,而且集合类的底层也都是通过数组实现的。
- 若有必要,使用变长数组
Java中的数组是定长的,一旦经过初始化声明就不可改变长度。可以通过对数组扩容来解决该问题:
public static <T> T[] expandCapacity(T[] datas,int newLen){
newLen = newLen < 0 ? 0:newLen;
return Arrays.copyOf(datas,newLen);
}
上述代码采用的是Arrays.copyOf方法,产生了一个newLen长度的新数组,并把原有的值拷贝了进去。通过这样的处理方式,曲折地解决了数组的变长问题。
开发中如果确实需要变长的数据集,数组也是在考虑范围之内的,不能因固定长度而将其否定之。
- 警惕数组的浅拷贝
通过Arrays.copyOf方法产生的数组是一个浅拷贝。
- 在明确的场景下,为集合指定初始容量
ArrayList是一个大小可变的数组,其底层必然要进行长度的扩展:
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0) newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
新数组长度的计算方法:
int newCapacity = oldCapacity + (oldCapacity >> 1);
参考:https://blog.csdn.net/zymx14/article/details/78324464
- 多种最值算法,适时选择
一批数据,找出其中的最大值或最小值。 - 自行实现,快速查找最大值
public static int max(int[] data){
int max = data[0];
for(int i : data){
max = max > i ? max : i;
}
return max;
}
2、先排序,后取值
public static int max(int[] data){
Arrays.sort(data.clone());
return data[data.length-1];
}
如果要查找仅次于最大值的元素(第二大),该如何处理?(注意,数组元素是可以重复的)
一个思路:先剔除重复数据,再排序。
数组不能剔除重复数据,Set集合可以,Set的子类TreeSet还能自动排序。
public static int getSecond(Integer[] data){
List<Integer> dataList = Arrays.asList(data);
TreeSet<Integer> ts = new TreeSet<Integer>(dataList);
return ts.lower(ts.last());
}
剔除重复元素并升序排列,这都是由TreeSet类实现的。
最值计算时使用集合最简单,使用数组性能最优。
- 避开基本类型数组转换列表陷阱
int[] data = {1,2,3,4,5};
List list = Arrays.asList(data);
System.out.println(list.size());
输出结果:1 ???
看一下Arrays.asList()方法:
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
入参是一个泛型变长参数(基本类型是不能泛型化的),数组是可以泛型化的。
上面的代码把一个int类型的数组作为了T的类型,转换后在list中就只有一个类型为int数组的元素了。
简单的修改一下:
Integer[] data = {1,2,3,4,5};
List list = Arrays.asList(data);
System.out.println(list.size());
输出:5
原始类型数组不能作为asList的输入参数,否则会引起程序逻辑混乱
66.asList方法产生的List对象不可更改
enum WEEK {SUN,MON,TUE,WED,THU,FRI,SAT}
public static void main(String[] args){
WEEK[] workDays = {WEEK.MON,WEEK.TUE,WEEK.WED,WEEK.THU,WEEK.FRI};
List<WEEK> list = Arrays.asList(workDays);
list.add(WEEK.SAT);
}
运行结果:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at java.util.AbstractList.add(AbstractList.java:108)
看一下Arrays.asList()方法源码:
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
问题出在ArrayList类上,此ArrayList类是Arrays工具类的一个内置类。
/**
* @serial include
*/
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a;
ArrayList(E[] array) {
a = Objects.requireNonNull(array);
}
@Override
public int size() {
return a.length;
}
@Override
public Object[] toArray() {
return a.clone();
}
/* 其他方法省略 */
}
这里的ArrayList是一个静态私有内部类,除了Arrays能访问外,其他类都不能访问。但该类没有提供add方法,其父类AbstractList的add方法为:
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
- 不同的列表选择不同的遍历方法
ArrayList:for循环(数组下标)
LinkedLsit:foreach / 迭代器
- 频繁的插入和删除时使用LinkedList
69.列表相等只需关心元素数据
ArrayList中的equals方法
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof List))
return false;
ListIterator<E> e1 = listIterator();
ListIterator<?> e2 = ((List<?>) o).listIterator();
while (e1.hasNext() && e2.hasNext()) {
E o1 = e1.next();
Object o2 = e2.next();
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return !(e1.hasNext() || e2.hasNext());
}
只是要求实现了List接口,不关心List的具体实现类。只要所有的元素相等,并且长度也相等就表明两个List是相等的,与具体的容量类型无关。
- 子列表只是原列表的一个视图
subList产生的列表只是一个视图,所有的修改动作直接作用于原列表
71. 推荐使用subList处理局部列表
100个元素,如果要删除索引位于20~30的元素,
可以用subList先取出一个子列表,然后清空。
list.subList(20,30).clear();
因为subList返回的List是原始列表的一个视图,删除这个视图中的所有元素,最终都会反映到原始字符串上。
- 生成子列表后不要再操作原列表
subList生成子列表后,保持原列表的值读状态。
对于子列表操作,因为视图是动态生成的,生成子列表后再操作原列表,必然会导致“视图”的不稳定,最有效的方法是通过 Collections.unmodifiableList()
方法设置列表为只读状态。
-
使用Comparator进行排序
-
不推荐使用binarySearch对列表进行检索
使用binarySearch首先要考虑排序问题,从这点来看indexOf比binarySearch简单得多
- 集合中的元素必须做到compareTo和equals同步
- indexOf()依赖equals()查找,binarySearch()依赖compareTo()方法查找
- equals()是判断元素是否相等,compareTo()是判断元素在排序中的位置是否相同
- compareTo()决定排序位置,equals()决定相等,应该保证当排序位置相同时,其equals也相同,否则会产生逻辑混乱。
- 集合运算时使用更优雅的方式
- 使用shuffle打乱列表
Collections.shuffle();
public static void shuffle(List<?> list) {
Random rnd = r;
if (rnd == null) r = rnd = new Random();
// harmless race.
shuffle(list, rnd);
}
- 减少HashMap中元素的数量
尽量让HashMap中的元素少量并简单
79.集合中的哈希码不要重复
-
多线程考虑使用Vector或HashTable
-
非稳定排序推荐使用List
-
集合大家族
List:
- ArrayList 动态数组
- LinkedList:双向链表
- Vector:线程安全的动态数组
- Stack:对象栈 先进先出
......
Set:
- EnumSet
- HashSet
- TreeSet
......
Map:
- TreeMap
- HashMap
- HashTable
- Properties
- EnumMap
......
Queue:
- ArrayBlockingQueue
- PriorityBolockingQueue
- LinkedBlockingQueue
- ArrayDeque
- LinkedBlockngDeque
- LinkedList
《编写高质量代码:改善Java程序的151个建议》