Java常用类:ArrayList类详解

作为一个稀有的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。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值