作为一个稀有的Java妹子,所写的所有博客都只是当作自己的笔记,留下证据自己之前是有用心学习的~哈哈哈哈(如果有不对的地方,也请大家指出,不要悄悄咪咪的不告诉我)
ArrayList
ArrayList其实就是维护了一个数组,使用扩容机制使其长度可变,可以把它想象成是一个数组的工具类,能够快速的改变数组的元素和长度。
1.成员变量
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//类中有一个成员变量是数组,维护的就是这个数组
transient Object[] elementData;
//长度,记录的也是数组的长度
private int size;
//默认容量,这个不是集合的初始容量,这个是扩容时用来比较是否需要扩容的
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
}
2.构造方法
三种:
1.public ArrayList(int initialCapacity);给定初始化容量创建对象
2.public ArrayList();直接创建对象
3.public ArrayList(Collection<? extends E> c) 给定Collection创建对象
第三种构造方法之前一直不知道有什么用,后来才知道,这个可以让其他集合类转为Arraylist
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(Collection<? extends E> c) {
...
}
直接贴一小段我刚写的业务代码,实现将set转为Arraylist
public String getParam(Map<String,Object> map){
String param = "";
if(map!=null && map.size()>0){
Set<String> keySet = map.keySet();
//构造函数直接转
List<String> list = new ArrayList<>(keySet);
}
return null;
}
3.常用方法
1.添加元素和扩容方法
public boolean add(E e) {
//判断是否需要扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
//把元素放到数组最后,把size+1
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//计算目前的最大容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
//扩大长度为原来的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//最终是使用的Arrays.copyOf来实现数组的扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
指定初始容量的好处在于不需要频繁的扩容,在第一次调用add方法时,如果没有初始化容量时,数组的长度会变为默认长度10,当添加元素个数超过10就会扩容,而如果指定了大小则会超过指定大小才会进行扩容。
2.get、remove、clear方法
public E get(int index) {
//检查下标是否越界
rangeCheck(index);
return elementData(index);
}
E elementData(int index) {
//返回数组元素
return (E) elementData[index];
}
public E remove(int index) {
//检查下标是否越界
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//将对应下标的元素设为null,长度-1
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
//清除集合里的所有元素
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
特别注意下remove方法,移除元素后会把集合的长度减1,所以在使用for循环遍历删除集合的元素时,会报下标越界的错误,因为当某个元素匹配后,删除完了,这时候集合的size-1,但是循环里的size是移除元素前的长度。
所以list在循环删除元素时,不能使用list的remove方法,需要使用迭代器的remove方法
public void startLiveRoom(Map<String, List<Integer>> userIdMap){
List<Integer> followList = userIdMap.get("followList");
List<Integer> appointmentList = userIdMap.get("appointmentList");
if (followList != null && followList.size() > 0 && appointmentList != null && appointmentList.size() > 0) {
Iterator<Integer> iterator = followList.iterator();
while (iterator.hasNext()) {
Integer userId = iterator.next();
if (appointmentList.contains(userId)) {
iterator.remove();
}
}
}
}
3.contains和indexOf查询集合中是否存在某元素
public boolean contains(Object o) {
//不存在返回false
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
//不存在返回-1
return -1;
}
4.toArray集合转数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
5.subList截取集合
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
6.合并集合
boolean addAll(Collection<? extends E> c);
举例:
public static void main(String[] args) {
//不指定泛型时,表示ArrayList里的元素是Object
List list = new ArrayList();
list.add("aaa");
list.add(1);
list.add(2);
//ArrayList是可以放null元素的
list.add(null);
//ArrayList里的元素不去重,按照先后依次放入
list.add(null);
list.add("aaa");
System.out.println(list);//[aaa, 1, 2, null, null, aaa]
System.out.println(list.contains(1));//true
System.out.println(list.indexOf(78));//-1
System.out.println(list.get(1));//1
//注意remove方法,只会删除匹配到的第一个元素,不是删除集合里的所有“aaa"
System.out.println(list.remove("aaa"));
System.out.println(list);[1, 2, null, null, aaa]
//这里只是返回sublist的结果,list还是不变
System.out.println(list.subList(0,2));[1, 2]
List<Integer> list2 = new ArrayList<>();
list2.add(42);
list2.add(543);
list2.add(1);
list2.add(21);
list.addAll(list2);
//合并集合
System.out.println(list);//[1, 2, null, null, aaa, 42, 543, 1, 21]
}
因为ArrayList底层是数组实现的,添加元素默认是添加到数组的最后,如果要插入元素,底层是复制数组实现的,移除元素也是采用复制数组的方式,所以ArrayList不适合插入删除多的场景,适合查询多的场景。
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
4.集合类型是对象,需要按照对象的某个属性排序
1.如果集合类型是整数,浮点类型时,可以直接使用Collections.sort(List list)来排序,默认是正序排列
List<Integer> list2 = new ArrayList<>();
list2.add(42);
list2.add(543);
list2.add(1);
list2.add(21);
Collections.sort(list2);
System.out.println(list2);//[1, 21, 42, 543]
如果需要倒叙排列的话,可以先正序排列,然后反转集合
Collections.reverse(list2);
System.out.println(list2);//[543, 42, 21, 1]
2.当集合的类型是自定义的对象时,需要根据对象的某个属性进行排序
@Data
public class User implements Serializable {
private static final long serialVersionUID = 9093453312L;
private String name;
private Integer age;
public User(String name,Integer age){
this.name = name;
this.age = age;
}
public User(){
}
public static void main(String[] args) {
List<User> listUser = new ArrayList<>();
User user1 = new User("李四",29);
User user2 = new User("张三",18);
User user3 = new User("王五",30);
User user4 = new User("李天一",40);
listUser.add(user1);
listUser.add(user2);
listUser.add(user3);
listUser.add(user4);
Collections.sort(listUser, new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
if(o1.getAge()>o2.getAge()){
return 1;
}
if(o1.getAge()<o2.getAge()){
return -1;
}
return 0;
}
});
System.out.println(listUser);
//[User(name=张三, age=18), User(name=李四, age=29), User(name=王五, age=30), User(name=李天一, age=40)]
}
}
5.与Vector的区别
两者底层数据结构是一样的,用法也是一致的,只是ArrayList是线程不安全的,Vector是线程安全的。
因为Vector在常用方法中使用了synchronized关键字,使得在非多线程环境中,Vector对于元素的查询、添加、删除和更新操作效果不是很好。
虽然ArrayList是非线程安全的,要想实现线程安全的ArrayList,可在ArrayList的基础上通过同步块来实现,或者使用同步包装器(Collections.synchronizedList),还可以使用J.U.C中的CopyOnWriteArrayList。