1:ArrayList
底层数据结构:ArrayList的底层为数组
使用注意点: ArrayList插入数据要确保前一个位置有数据。
ArrayList的遍历方式有三种:for循环,forEach,迭代器
ArrayList<Object> arrayList=new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
arrayList.add(4);
//for循环
for (int i = 0; i < arrayList.size(); i++) {
System.out.println(arrayList.get(i));
}
//foreach
for (Object o : arrayList) {
System.out.println(o);
}
//迭代器
Iterator<Object> iterator = arrayList.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
ArrayList的扩容机制(源码分析):
- 无参构造后:默认数组长为0
- 第一次使用add,数组长为10
- 此后每次扩容1.5倍,若1.5倍不够则直接扩容到想要的大小
- 带参构造给多少初始化大小为多少
ArrayList<Object> arrayList=new ArrayList<>();
arrayList.add(1);
无参构造:
//ArrayList源码 public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;} //可以看到无参构造器创建了一个长度为0的Object类型数组 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //第一次插入数据时 public boolean add(E e) { ensureCapacityInternal(size + 1); // 此时size为0 elementData[size++] = e; return true;} //调用ensureCapacityInternal(1)方法 private void ensureCapacityInternal(int minCapacity) {//minCapacity=1 ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));} //又调用了calculateCapacity(Object数组,1)方法 private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity;} //可以看到DEFAULT_CAPACITY为10,所以这里返回10 private static final int DEFAULT_CAPACITY = 10; //调用ensureExplicitCapacity(10)方法 private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code //这里判断传递的参数minCapacity和底层数组长度相减是否大于0,如果大于0则调用grop方法且将minCapacity作为参数 if (minCapacity - elementData.length > 0) grow(minCapacity);} //看看grop方法 private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length;//这里定义oldCapacity为底层数组长度 int newCapacity = oldCapacity + (oldCapacity >> 1);//可看到newCapacity为1.5倍旧的长度 if (newCapacity - minCapacity < 0) newCapacity = minCapacity;//如果扩容后能不够满足minCapacity的大小,则新的容量为minCapacity if (newCapacity - MAX_ARRAY_SIZE > 0)//如果newCapacity大于MAX_ARRAY_SIZE newCapacity = hugeCapacity(minCapacity);//调用hugeCapacity参数为minCapacity // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); } //MAX_ARRAY_SIZE大小为Integer.MAX_VALUE - 8 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //hugeCapacity方法 private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;}//minCapacity的值大于MAX_ARRAY_SIZE则赋值为Integer.MAX_VALUE,不大于MAX_ARRAY_SIZE赋值为MAX_ARRAY_SIZE
有参构造:
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); } }
2:LinkedList
底层数据结构:LinkedList底层为双向链表,双向链表结点为Date(数据),prev(前驱),next(后继)
为什么引入LinkedList?
ArrayList每次指定位置添加或删除会进行移动元素操作,如果存在大量的添加删除很不方便。
链表: 逻辑上连续,物理地址上非连续
LinkedList的遍历:
List<Integer> list=new LinkedList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
for(int i=0;i<list.size();i++){
System.out.print(list.get(i));
}
for(Integer i:list){
System.out.print(i);
}
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
System.out.print(iterator.next());
}
//逆序输出
ListIterator<Integer> integerListIterator = list.listIterator(list.size());
while (integerListIterator.hasPrevious()){
System.out.print(integerListIterator.previous());
}
3:ArrayLsit和LinkedList的区别
顺序表:适合频繁访问元素
链表:适合频繁插入删除元素
不同点 | ArrayLsit | LinkedList |
---|---|---|
存储空间 | 物理上连续 | 逻辑上连续,物理上不一定连续 |
随机访问 | 支持随机访问,时间复杂度为O(1) | 不支持随机访问,需要遍历,时间复杂度为O(N) |
头插法 | 需要移动元素,时间复杂度O(N) | 修改引用指向,时间复杂度O(1) |
插入 | 空间不够需要扩容 | 无容量概念 |
应用场景 | 元素高效存储且频繁访问 | 任意位置的插入和删除频繁 |
4:栈
栈的底层数据结构:数组
栈的特性:先进后出
push:入栈
pop:弹出栈顶元素并从栈中删除
peek:获取栈顶元素但不删除
empty:是否为空
search:距离栈顶的长度,栈顶元素距离为1
栈,虚拟机栈,栈帧区别:
- 栈:数据结构
- 虚拟机栈:JVM内存
- 栈帧:当运行函数时,会在Java虚拟机栈上开辟栈帧给当前函数
5:队列
public interface Queue<E> extends Collection<E>
队列特性:先进先出
add:插入元素,但是如果队列为空则抛出异常
offer:插入元素,队列为空不会抛出异常
remove:移除队列头部元素,但是如果队列为空则抛出异常
poll:移除队列头部元素,队列为空不会抛出异常
element:将队列头部元素的值返回,但是如果队列为空则抛出异常
peek:将队列头部元素的值返回,队列为空不会抛出异常
实例化:以LinkedList为例,只能使用Queue特有方法或者LinkedList重写的方法。
Queue<Integer> queue=new LinkedList<>();
PriorityQueue:优先级队列,基于小根堆,请关注后续堆的内容