1. 警惕数组的浅拷贝
public static void main(String[] arg){
Balloon [] a = new Balloon[5];
for(int i = 0;i < 5; i++){
a[i] = new Balloon(i,Color.values()[i]);
a[i].setId(i);
a[i].setColor(Color.values()[i]);
}
for(int i = 0;i < 5; i++){
System.out.println("a:"+a[i].getColor());
}
Balloon[] b = Arrays.copyOf(a,a.length);
//修改b数组最后一个元素的属性
b[4].setColor(Color.Black);
for(int i = 0;i < 5; i++){
System.out.println("a:"+a[i].getColor()+"--b:"+b[i].getColor());
}
}
enum Color{
Red,Blue,Yellow,Black,White;
}
static class Balloon{
private int id;
private Color color;
public Balloon(int id, Color color) {
this.id = id;
this.color = color;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
}
a:Red
a:Blue
a:Yellow
a:Black
a:White
a:Red--b:Red
a:Blue--b:Blue
a:Yellow--b:Yellow
a:Black--b:Black
a:Black--b:Black
为什么修改了b数组最后一个元素的颜色属性,a数组的最后一个元素颜色属性也发生了变化?原因如下:
通过copyOf方法产生的数组是一个浅拷贝,这与序列的浅拷贝完全相同,基本类型拷贝值,其它都是拷贝引用地址。
2. 避开基本类型数组转换列表陷阱
看下面一段代码:
int[] data = {1,2,3,4,5,6};
List<int[]> ints = Arrays.asList(data);
System.out.println(ints.size());
ints的长度为什么变成了1,而不是6呢?查看asList的源码你会发现:
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
asList的输入参数是一个泛型变长参数,而基本类型是不能泛型化的,除非使用对应的包装类型,那为什么传递int型数组编译没有报错呢?
在Java中,数组是一个对象,它是可以被泛型化的,上面的代码中把int数组作为了T的类型,因此转化后list中只有一个int数组的元素。修改后代码如下:
Integer[] data = {1,2,3,4,5,6};
List<Integer> integers = Arrays.asList(data);
System.out.println(integers.size());
3. asList产生的对象不可更改
Integer[] data = {1,2,3,4,5,6};
List<Integer> integers = Arrays.asList(data);
integers.add(9);
System.out.println(integers.size());
结果:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at java.util.AbstractList.add(AbstractList.java:108)
at com.hummer.personal.mdm.MdmController.main(MdmController.java:133)
为什么在使用add方法时会抛出异常呢?
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
查看源码发现asList返回的ArrayList类是Arrays工具类内置类
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();
}
@Override
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
int size = size();
if (a.length < size)
return Arrays.copyOf(this.a, size,
(Class<? extends T[]>) a.getClass());
System.arraycopy(this.a, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
@Override
public E get(int index) {
return a[index];
}
@Override
public E set(int index, E element) {
E oldValue = a[index];
a[index] = element;
return oldValue;
}
@Override
public int indexOf(Object o) {
E[] a = this.a;
if (o == null) {
for (int i = 0; i < a.length; i++)
if (a[i] == null)
return i;
} else {
for (int i = 0; i < a.length; i++)
if (o.equals(a[i]))
return i;
}
return -1;
}
@Override
public boolean contains(Object o) {
return indexOf(o) != -1;
}
@Override
public Spliterator<E> spliterator() {
return Spliterators.spliterator(a, Spliterator.ORDERED);
}
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
for (E e : a) {
action.accept(e);
}
}
@Override
public void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
E[] a = this.a;
for (int i = 0; i < a.length; i++) {
a[i] = operator.apply(a[i]);
}
}
@Override
public void sort(Comparator<? super E> c) {
Arrays.sort(a, c);
}
}
而这个内置类中并没有实现add方法,其add方法在父类AbstractList中
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
因此会抛出异常
4. 频繁插入和删除使用LinkedList
- ArrayList是动态扩展的数组,插入和删除元素时需要移动后面元素
- LinkedList是双向链表的数据结构,插入和删除元素只是前后元素引用指针的变化
- 修改元素ArrayList比LinkedList快,因为LinkedList修改用了entry方法定位元素,而arraylist的修改则是数组元素的直接替换
- 增加元素两者效率基本相同
5. 列表相等只需关心元素数据
ArrayList<String> str = new ArrayList();
str.add("aa");
Vector<String> str2 = new Vector();
str2.add("aa");
System.out.println(str.equals(str2));//true
两者都实现了List接口,也都继承了AbastractList抽象类,其equals方法在AbastractList中定义,equals方法不关心List的具体实现类,只要所有元素相等,并且长度也相等就表明两个List相等。其他的集合类型,如Set,Map等与此相同。
6. 子列表只是原列表的一个视图
public static void main(String[] arg) {
List<String> c = new ArrayList<>();
c.add("A");
c.add("B");
c.add("C");
ArrayList<String> c1 = new ArrayList<>(c);
List<String> c2 = c.subList(0, c.size());
c2.add("D");
System.out.println("c==c1 ? "+ c.equals(c1));//false
System.out.println("c==c2 ? "+ c.equals(c2));//true
}
- c1是通过ArrayList的构造函数创建的,它是通过数组的copyOf动作生成的,所生成的c1与原列表c之间没有任何关系(虽然是浅拷贝,但是元素类型是String,也就是说元素是深拷贝)
- subList操作是在原始列表上的操作,它自身并没有生成数组或是链表,也就是子列表只是原列表的一个视图(View),所以修改动作都反映在原列表上