集合中只能存放引用类型,数组中可以存放基本数据类型和引用类型
一、List(有序 不唯一)
1.ArrayList
1.1 ArrayList特点
- 底层是一个数组
- ArrayList根据索引取元素的效率比较高
- 有序,存储的元素都是有顺序的
ArrayList的基本使用:
public static void main(String[] args) {
List<Integer> list=new ArrayList<>();//多态
ArrayList<Integer> list1=new ArrayList<>();
list.add(123);//自动装箱
list.add(234);
list.add(345);
//get()集合中取元素,括号中为下标
Integer a=list.get(1);
System.out.println(a);
//size();为集合的长度
Integer b=list.size();
System.out.println(b);
//集合的遍历
for (int i = 0; i <list.size() ; i++) {
System.out.println(list.get(i));
}
//集合foreach遍历
for (Integer l:list) {
System.out.println(l);
}
}
1.2 ArrayList的其他使用方法
import java.util.ArrayList;
import java.util.List;
//ArrayList的其他使用
public class ArrayListB {
public static void main(String[] args) {
List<Integer> list=new ArrayList<>();//多态
ArrayList<Integer> list1=new ArrayList<>();
list.add(123);
list.add(0,12);//指定的位置插入
list1.add(1);
list.addAll(list1);//把list1追加到list中
list.set(1,555);//修改list中索引为1的值为555
Integer a=list.remove(0);//移除list中索引为0的元素,并将原值赋值给a
Boolean b=list.remove(new Integer(555));//移除list中的第一个555,返回一个Boolean的值
list.clear();//清空集合中所有元素
}
}
1.3 ArrayList中add底层源码理解
ArrayList成员变量和构造方法的源码分析
public class ArrayList<E> extends AbstractList<E> implements List<E> {ArrayList实现了List的接口
private static final long serialVersionUID = 8683452581122892189L;
private static final int DEFAULT_CAPACITY = 10;定义默认的长度为10并赋值给DEFAULT_CAPACITY
private static final Object[] EMPTY_ELEMENTDATA = new Object[0];声明空数组,并赋值地址
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];声明空数组,并赋值地址
transient Object[] elementData;数组elementData是一个空对象,其地址为null,用途为数据传递
private int size;定义私有化的size变量
private static final int MAX_ARRAY_SIZE = 2147483639;数组最大长度
----------------------------------- ArrayList的有参构造方法---------------------------------------------
public ArrayList(int initialCapacity) { ArrayList的有参构造方法
if (initialCapacity > 0) { 如果初始容量是大于0的
this.elementData = new Object[initialCapacity]; 则elementData【10】,数组容量变为10
} else {
if (initialCapacity != 0) {
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);若是初始容量小于0则抛出异常
}
this.elementData = EMPTY_ELEMENTDATA; 如果初始容量等于0,则将空数组的地址赋值给
}
}
----------------------------------- ArrayList的无参构造方法---------------------------------------------
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; 将空数组地址赋值给当前的elementData数组
}
ArrayList中add方法的源码分析
ArrayList<Integer> list1=new ArrayList<>(); 创建list1对象
list.add(123);
list.add(234);
---------------------list.add(123);分析过程A---------------------------
public static Integer valueOf(int i) {
return i >= -128 && i <= Integer.IntegerCache.high ? Integer.IntegerCache.cache[i + 128] : new Integer(i);
} 实现自动装箱的过程,将int类型的数值转换为Integer
---------------------list.add(123);分析过程B---------------------------
public boolean add(E e) { 返回Boolean类型的值
++this.modCount;
this.add(e, this.elementData, this.size); 调用add的方法,传入当前的值123,空数组,0
return true;返回true
}
---------------------list.add(123);分析过程C---------------------------
private void add(E e, Object[] elementData, int s) { 123 空数组 0
if (s == elementData.length) { 如果数组elementData的长度等于0
elementData = this.grow(); grow中继续调用grow的有参方法,此时此刻数组elementData的大小为1
}
elementData[s] = e;将123赋值给elementData[0]
this.size = s + 1;size加1 size当前的值为1
}
private int newCapacity(int minCapacity) { 通过grow进行的调用,数组扩容的方法
int oldCapacity = this.elementData.length; 旧容量等于当前数组的长度
int newCapacity = oldCapacity + (oldCapacity >> 1); 新容量等于旧的容量加上旧的容量的一半
if (newCapacity - minCapacity <= 0) { 如果新容量和传过来的容量(1)的差小于等于0,也就是容量不够
if (this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(10, minCapacity); 如果数组为空,则返回容量为10
} else if (minCapacity < 0) {
throw new OutOfMemoryError(); 如果传入的值小于0则抛出异常
} else {
return minCapacity; 如果数组的容量大于传过来的值,则返回传来的值
}
} else {
return newCapacity - 2147483639 <= 0 ? newCapacity : hugeCapacity(minCapacity);
如果传来的minCapacity容量小于新容量,则进行扩容,返回新容量,等于原来容量的1.5倍
}
}
list.add(123);
list.add(234);
---------------------list.add(234);分析过程A---------------------------
public static Integer valueOf(int i) { 实现自动装箱的过程
return i >= -128 && i <= Integer.IntegerCache.high ? Integer.IntegerCache.cache[i + 128] : new Integer(i);
}
---------------------list.add(234);分析过程B---------------------------
public boolean add(E e) {
++this.modCount;
this.add(e, this.elementData, this.size); 调用add方法,传入234,elementData数组,1
return true;
}
---------------------list.add(234);分析过程C---------------------------
private void add(E e, Object[] elementData, int s) {传入234,elementData数组,1
if (s == elementData.length) { 如果s和数组长度相等则执行,本次中s==1不成立,继续往下执行,跳出if
elementData = this.grow();
}
elementData[s] = e; elementData【1】=234
this.size = s + 1; size=1+1=2
}
2.LinkedList
2.1 LinkedList特点
- 有序
- 插入、删除的时候比较快
- 底层为双向链表
get方法中元素获取:
链表的下标和数组的下标含义不同
数组通过下标取值是计算出来的,速度比较快
链表的下标指代的是链表的自然顺序,根据下标取值是数出来的效率较低
import java.util.LinkedList;
public class TestLinkedList {
public static void main(String[] args) {
LinkedList<Integer> list=new LinkedList<>();
list.add(123);//追加
list.add(234);
list.add(345);
Integer a=list.get(0);//元素获取
int b=list.size();//长度
for (int i = 0; i <list.size() ; i++) {//遍历
System.out.println(list.get(i));
}
for (Integer m:list) {
System.out.println(m);
}
System.out.println(list);
}
}
2.2 LinkedList相较于ArrayList的特有方法
addFirst(); addLast(); 头部/尾部追加元素
getFirst(); getLast(); 获取头部/尾部的元素
removeFirst(); removeLast(); 移除头部/尾部的元素
import java.util.LinkedList;
public class LinkedListB {
public static void main(String[] args) {
LinkedList<Integer> list=new LinkedList<>();
list.add(123);//末尾追加元素
list.addFirst(12);//在起始位置追加元素
list.addLast(13);//在末尾位置追加元素
System.out.println(list.getFirst());//获取起始位置元素的值
System.out.println(list.getLast());//获取末尾位置元素的值
System.out.println(list);
list.removeFirst();//移除起始位置的元素
list.removeLast();//移除末尾位置的值
System.out.println(list);
}
}
2.3 LinkedList底层源码理解
LinkedList成员变量和构造方法的源码分析
LinkedList底层为双链表
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, Serializable {
transient int size; 链表的容量
transient LinkedList.Node<E> first; 头节点
transient LinkedList.Node<E> last; 尾节点
private static final long serialVersionUID = 876323262645176354L;
public LinkedList() { 无参构造
this.size = 0;
}
public LinkedList(Collection<? extends E> c) { 有参构造
this();
this.addAll(c);
}
-----------------Node源码-----------------
private static class Node<E> { Node类
E item; item为节点值
LinkedList.Node<E> next; 下一个节点
LinkedList.Node<E> prev; 上一个节点
Node(LinkedList.Node<E> prev, E element, LinkedList.Node<E> next) { 有参构造
this.item = element;
this.next = next;
this.prev = prev;
}
}
------------------list.add(123);分析过程A-------------------------------
public static Integer valueOf(int i) { 自动装箱
return i >= -128 && i <= Integer.IntegerCache.high ? Integer.IntegerCache.cache[i + 128] : new Integer(i);
}
------------------list.add(123);分析过程B-------------------------------
public boolean add(E e) { 返回值为布尔类型,e为123
this.linkLast(e);调用linkLast方法,传入参数e
return true; 返回true
}
void linkLast(E e) { e为123
LinkedList.Node<E> l = this.last; 当前last为null,将地址赋值给节点l
创建一个节点对象,将null,123,null赋值给newNode oX222
LinkedList.Node<E> newNode = new LinkedList.Node(l, e, (LinkedList.Node)null);
this.last = newNode; 此时last为新节点的地址 0X222
if (l == null) { 因为l的值为null,所以在当前情况下null==null
this.first = newNode; 将新节点的地址赋值给前驱节点,frist的地址也为 0X222 0X22 123 0X22
} else {
l.next = newNode;
}
++this.size; 当前size从0变为1
++this.modCount;
}
------------------list.add(234);分析过程A-------------------------------
public static Integer valueOf(int i) { 自动装箱
return i >= -128 && i <= Integer.IntegerCache.high ? Integer.IntegerCache.cache[i + 128] : new Integer(i);
}
public boolean add(E e) { 此时此刻 e为234
this.linkLast(e);调用linkLast方法
return true;
}
void linkLast(E e) { 234
LinkedList.Node<E> l = this.last; 将尾节点的地址(0X222)赋值给节点l
LinkedList.Node<E> newNode = new LinkedList.Node(l, e, (LinkedList.Node)null); 0X222 234 null
this.last = newNode; 尾节点被赋值成新节点的地址0X333,为下个节点(0X444)的引用做准备
if (l == null) { 0X222!=null 执行else
this.first = newNode; 将新节点的地址指向上一节点
} else {
l.next = newNode; 将地址为0X222的尾节点变成0X333指向0X333的地址
}
++this.size; size从1变成2
++this.modCount;
}
二、Set(无序 唯一)
1. Set集合的方法
1.1 HashSet
1.2 TreeSet
1.3 LinkedHashSet
public class SetTest {
public static void main(String[] args) {
Set<String> set=new HashSet<>();
Set<String> set1=new TreeSet<>();
Set<String> set2=new LinkedHashSet<>();
//元素添加
set.add("A");
set.add("D");
set.add("C");
System.out.println(set);
//输出集合长度
System.out.println(set.size());
//只可以通过增强for循环进行取值
//注意:不可以通过普通for循环进行取值,set集合没有索引
for (String s:set) {
System.out.println(s);
}
set.clear();//清空集合元素
System.out.println(set);
}
}
2.栈
- 特点:先进后出
栈的基本方法
public class StackTest { public static void main(String[] args) { Stack<String> stack=new Stack<>(); //入栈/压栈 stack.push("A"); stack.push("B"); //输出栈顶元素 System.out.println(stack.peek()); //出栈/弹栈 stack.pop(); System.out.println(stack); } }
3.队列
分类:单端队列(先进先出)和双端队列
单端队列方法
public class QueueTest { public static void main(String[] args) { Queue queue=new LinkedList();//LinkedList为Queue的子类 //进入单端队列 queue.offer("A"); queue.offer("D"); queue.offer("C"); System.out.println(queue); //出队 queue.poll(); System.out.println(queue); } }
双端队列方法
public class DequeTest { public static void main(String[] args) { //Deque<E> extends Queue<E> Deque<String> deque=new LinkedList<>(); System.out.println("-----------------把Deque当作双端队列使用--------------------"); deque.offerFirst("123");//从首端进入 deque.offerLast("345");//从尾端进入 deque.pollFirst();//从首端出去 deque.pollLast();//从尾端出去 System.out.println("-----------------把Deque当作单端队列使用--------------------"); deque.offer("123"); deque.poll(); System.out.println("-----------------把Deque当作栈使用--------------------------"); deque.push(""); deque.pop(); } }
4. 栈溢出、堆溢出
1.堆溢出
创建对象时如果没有可以分配的堆内存,JVM就会抛出OutOfMemoryError:java heap space异常。
堆溢出实例:public class heapOverflow { public static void main(String[] args) { List<byte[]> list = new ArrayList<>(); int i=0; while(true){ list.add(new byte[1024*1024*1024]); System.out.println("分配次数:"+(++i)); } } }
2.栈溢出
栈空间不足时,需要分下面两种情况处理:
- 线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError
- 虚拟机在扩展栈深度时无法申请到足够的内存空间,将抛出OutOfMemberError
附:当前大部分的虚拟机栈都是可动态扩展的。
public class StackOverflow {
public static void main(String[] args) {
dy(20);
}
static int dy(int a){
return a*dy(a-1);
}
}