数组:
- 长度开始时必须指定,而且一旦指定,不能修改
- 保存的必须为同一类型的元素
- 使用数组进行增加/删除元素比较麻烦
集合:
- 可以动态保存任意多个对象,使用比较方便
- 提供了一系列方便操作对象的方法: add、remove、set、get
- 使用集合添加,删除新元素的代码简洁明了
集合框架体系
Collection体系图
Map集合体系图
Collection 接口和常用方法
Collection接口实现类的特点
public interface Collection<E> extends Iterable<E>
- collection实现子类可以存放多个元素,每个元素可以是Object
- 有些Collection的实现类,可以存放重复的元素,有些不可以
- 有些Collection的实现类,有些是有序的(List),有些不是有序的(Set)
- Collection接口没有直接的实现子类,是通过它的子接口Set和List来实现的
Collection接口常用方法
操作元素方法
- add:添加单个元素
- remove:删除指定元素
- contains:查找元素是否存在
- size:获取元素个数
- isEmpty:判断是否为空
- clear:清空
- addAll:添加多个元素
- containsAll:查找多个元素是否都存在
- removeAll:删除多个元素
/**
* Collection 常用方法举例
* 以实现子类 ArrayList举例
*/
public class CollectionMethod {
public static void main(String[] args) {
List list = new ArrayList();
// 1. add:添加单个元素
list.add("卡卡西");
list.add(20); // list.add(new Integer(10))
list.add(true);
System.out.println("list="+list);
// 2. remove:删除指定元素
list.remove(0); // 按索引删除第一个元素
list.remove(new Integer(20)); //指定删除某个元素
System.out.println("list="+list);
// 3. contains:查找元素是否存在
System.out.println(list.contains("卡卡西"));
// 4. size:获取元素个数
System.out.println(list.size());
// 5. isEmpty:判断是否为空
System.out.println(list.isEmpty());
// 6. clear:清空
list.clear();
System.out.println("list="+list);
// 7. addAll:添加多个元素
ArrayList list2 = new ArrayList();
list2.add("鸣人");
list2.add("佐助");
list2.add("博人");
list.addAll(list2);
System.out.println("list="+list);
// 8. containsAll:查找多个元素是否都存在
System.out.println(list.containsAll(list2));
// 9. removeAll:删除多个元素
list.add("大蛇丸");
list.removeAll(list2);
System.out.println("list="+list);
}
}
Iterator(迭代器)遍历元素
Iterator基本介绍
- Iterator对象称为迭代器,主要用于遍历Collection集合中的元素。
- 所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器。
- Iterator 的结构
- Iterator 仅用于遍历集合,Iterator 本身并不存放对象。
Iterator迭代器执行原理
//得到一个集合的迭代器
Iterator iterator = coll.iterator();
//hasNext():判断是否还有下一个元素
while(iterator.hasNext()){
//next(): 返回迭代中的下一个元素
System.out.printIn(iterator.next());
}
Iterator接口方法
返回类型 | 方法 | 描述 |
---|---|---|
boolean | hasNext() | 如果迭代具有更多元素,则返回 true 。 |
E | next() | 返回迭代中的下一个元素。 |
default void | remove() | 从底层集合中删除此迭代器返回的最后一个元素(可选操作)。 |
default void | forEachRemaining(Consumer action) | 对每个剩余元素执行给定的操作,直到所有元素都被处理或动作引发异常。 |
注意:在调用it.next()方法之前必须调用it.hasNext()进行检测。若不调用,且下一条记录无效,直接调用it.next会抛出 NoSuchElementException异常。
public class CollectionIterator {
public static void main(String[] args) {
Collection col = new ArrayList();
col.add(new book("三国演义","罗贯中",20.2));
col.add(new book("红楼梦","曹雪芹",150.3));
col.add(new book("小李飞刀","古龙",25.3));
System.out.println(col);
// 遍历集合
// 1. 得到集合对应的迭代器
Iterator iterator = col.iterator();
// 2. 使用while循环遍历
while (iterator.hasNext()){ //判断是否还有数据
// 返回下一个元素,类型是Object
Object object = iterator.next();
System.out.println("onject="+object);
}
// 提示: 快速生成while循环快捷键 => itit
// 显示快捷提示键 => ctrl+J
}
}
class book{
public String name;
public String author;
public double price;
public book(String name, String author, double price) {
this.name = name;
this.author = author;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price +
'}';
}
}
增强 for 循环遍历集合元素
增强for循环,可以替代iterator迭代器,特点:增强for就是简化版的iterator,本质一样。只能用于遍历集合或数组。
基本语法
for(元素类型 元素名 : 集合名或数组名){
访问元素
}
public class CollectionFor {
public static void main(String[] args) {
Collection col = new ArrayList();
col.add(new book("三国演义","罗贯中",20.2));
col.add(new book("红楼梦","曹雪芹",150.3));
col.add(new book("小李飞刀","古龙",25.3));
System.out.println(col);
// 使用增强for循环遍历
// 增强for循环底层依然是迭代器
// 提示:快捷键 => col.for
for(Object book : col){
System.out.println("book="+book);
}
// 增强for也可以用在数组上
int[] nums = {1,25,13,8,5};
for(int i : nums){
System.out.println("i="+i);
}
}
}
List 接口和常用方法
List 接口基本结束
List接口是 Collection 接口的子接口
- List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复。
- List集合中的每个元素都有其对应的顺序索引,即支持索引。
- List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
- List接口常用的实现类有:ArrayList、LinkedList 和 Vector。
List 接口常用方法
方法 | 描述 |
---|---|
void add ( int index , Object ele) | 在index位置插入ele元素 |
boolean addAll ( int index , Collection eles) | 从index位置开始将eles中的所有元素添加进来 |
Object get ( int index ) | 获取指定index位置的元素 |
int indexOf ( Object obj) | 返回obj在集合中首次出现的位置 |
int lastIndexOf ( Object obj) | 返回obj在当前集合中末次出现的位置 |
Object remove ( int index) | 移除指定index位置的元素,并返回此元素 |
Object set ( int index , Object ele) | 设置指定index位置的元素为ele,相当于是替换 |
List subList ( int fromeIndex , int toIndex) | 返回从fromIndex 到 toIndex 位置的子集合 |
public class ListMethod {
public static void main(String[] args) {
List list = new ArrayList();
list.add("卡卡西");
list.add("鸣人");
// void add (int index,Object ele)在index位置插入ele元素
list.add(1,"佐助");
System.out.println(list);
// boolean addAll(int index,Collection eles)从index位置开始将eles中的所有元素添加进来
List list2 = new ArrayList();
list2.add("jack");
list2.add("tom");
list.addAll(1,list2);
System.out.println(list);
// Object get(int index)获取指定index位置的元素
Object o = list.get(1);
System.out.println(o);
// int indexOf(Object obj)返回obj在集合中首次出现的位置
System.out.println(list.indexOf("tom"));
// int lastIndexOf(Object obj)返回obj在当前集合中末次出现的位置
list.add("jack");
System.out.println(list.lastIndexOf("jack"));
// Object remove(int index)移除指定index位置的元素,并返回此元素
Object remove = list.remove(5);
System.out.println(remove);
// Object set(int index,Object ele)设置指定index位置的元素为ele,相当于是替换
list.set(1,"mary");
System.out.println(list);
// List subList(int fromeIndex,int toIndex)返回从fromIndex 到 toIndex 位置的子集合
// 子集合范围为前闭后开 fromeIndex <= subList < toIndex
List subList = list.subList(2, 4);
System.out.println(subList);
}
}
List的三种遍历方式
- 使用iterator
- 使用增强for
- 使用普通for
public class ListFor {
public static void main(String[] args) {
List list = new ArrayList();
list.add("jack");
list.add("tom");
list.add("卡卡西");
list.add("博人");
// 遍历
System.out.println("=======迭代器遍历");
// 1. 迭代器
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println(obj);
}
System.out.println("=======增强for循环");
// 2. 增强for循环
for (Object o : list) {
System.out.println("o="+o);
}
System.out.println("=======普通for循环");
// 3. 普通for循环
for (int i = 0; i < list.size(); i++) {
System.out.println("对象="+list.get(i));
}
}
}
ArrayList 底层结构和源码分析
ArrayList 注意事项
- ArrayList 可以加入null,并且多个
- ArrayList是由数组来实现的
- ArrayList基本等同于Vector,除了 ArrayList是线程不安全(执行效率高),在多线程情况下,不建议使用ArrrayList。
// ArrayList 是线程不安全,源码 没有 synchronized public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
ArrayList 的底层操作机制源码分析(重点,难点)
- ArrayList 中维护了一个 Object类型的数组 elementData。
transient Object[] elementData; // transient 标识瞬间,短暂的,表示该属性不会被序列化 - 当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData 容量为0,第一次添加,则扩容elementData为10,如需再次扩容,则扩容elementData为1.5倍。
- 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍。
源程序:
// 关闭警告
@SuppressWarnings({"all"})
public class ArrayListSource {
public static void main(String[] args) {
// 源码分析
// 使用无参构造器创建ArrayList对象
ArrayList list = new ArrayList();
// 使用for循环给list集合添加 1-10 数据
for (int i = 0; i <= 10; i++) {
list.add(i);
}
// 使用for循环给list集合添加 11-15 数据
for (int i = 11; i<=15;i++){
list.add(i);
}
list.add(100);
list.add(200);
list.add(null);
for (Object o : list) {
System.out.println(o);
}
}
}
-
在
ArrayList list = new ArrayList();
处添加断点 -
debug -- step Into 到 ArrayList.java的ArrarList()构造方法
/** * Constructs an empty list with an initial capacity of ten. */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
查询DEFAULTCAPACITY_EMPTY_ELEMENTDATA可发现 默认为空数组
/** * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first element is added. */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
-
第一次for循环,程序先进入
Integer valueOf()
方法对数据进行装箱public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
然后执行 list 的
boolean add(E e)
方法/** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return <tt>true</tt> (as specified by {@link Collection#add}) */ public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
在
boolean add(E e)
方法中,先执行ensureCapacityInternal(size + 1)
方法确定是否要扩容,然后再执行赋值。private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); }
在
calculateCapacity()
方法中 先确定elementData是否为空数组,如果为空数组,返回DEFAULT_CAPACITY(默认为10) 和 minCapacity(第一次为1) 中的最大值,private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; }
在
ensureExplicitCapacity(int minCapacity)
方法中确定是否真的扩容modCount++ :记录集合修改次数
minCapacity - elementData.length > 0 :如果数组所需最小容量 - 数组当前实际大小 大于 0 则执行扩容
private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
grow()
方法执行扩容- 将elementData.length 记录到 oldCapacity中,第一次值为0
- newCapacity = oldCapacity + (oldCapacity >> 1); 执行扩容,扩容大小为 数组当前容量+数组当前大小右移1位(除以2),即扩容1.5倍
- 因为第一次扩容oldCapacity 为0 所有newCapacity 也为0,执行
if (newCapacity - minCapacity < 0) newCapacity = minCapacity;
此时newCapacity 为 10,所以第一次扩容大小为 10 elementData = Arrays.copyOf(elementData, newCapacity);
Arrays.copyOf()方法可保留原先数据扩容
执行Arrays.copyOf()方法进行扩容,第一次执行完elementData 中有10个空数据
/** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity */ private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
扩容完成后,继续执行add()方法,将数据添加到elementData数组中
Vector 底层结构和源码分析
Vector 基本介绍
-
Vector类的定义
-
Vector 底层也是一个对象数组,
protected Object[] elementData;
-
Vector 是线程同步的,即线程安全,Vector 类的操作方法带有 ==synchronized==
public synchronized boolean add(E e) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = e; return true; }
-
在开发中,需要线程同步安全时,考虑使用Vector.
Vector 和 ArrayList的比较
底层结构 | 版本 | 线程安全(同步)效率 | 扩容倍数 | |
---|---|---|---|---|
ArrayList | 可变数组 | jdk1.2 | 不安全,效率高 | 如果有参构造1.5倍,如果是无参 1. 第一次10 2. 从第二次开始按1.5倍扩容 |
Vector | 可变数组 | jdk1.0 | 安全,效率不高 | 如果是无参,默认10,满后,就按2倍扩容。如果指定大小,则每次直接按2倍扩容 |
Vector 源码分析
创建Vector对象,并循环添加
@SuppressWarnings({"all"})
public class Vector_ {
public static void main(String[] args) {
// 无参构造 创建对象
Vector vector = new Vector();
for (int i = 0; i < 10; i++) {
vector.add(i);
}
}
}
-
创建Vector 对象,首先执行无参构造器
public Vector() { this(10); }
public Vector(int initialCapacity) { this(initialCapacity, 0); }
所以 new Vector() 会默认创建 容量为10的 对象
-
add 方法添加
public synchronized boolean add(E e) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = e; return true; }
首先执行ensureCapacityHelper() 方法判断是否要扩容。
如果数组所需最小容量大于当前数组容量,执行grow()方法扩容。
private void ensureCapacityHelper(int minCapacity) { // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
-
grow()方法源码:
默认扩容两倍大小。
private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); }
也可指定扩容大小。在vector带参构造器中指定扩容大小
public Vector(int initialCapacity, int capacityIncrement) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new Object[initialCapacity]; this.capacityIncrement = capacityIncrement; }
LinkedList 底层结构
LinkedList 基本介绍
- LinkedList底层实现了双向链表和双端队列特点
- 可以添加任意元素(元素可以重复),包括null
- 线程不安全,没有实现同步
LinkedList 的底层操作机制
-
LinkedList 底层维护了一个双向链表
-
LinkedList中维护了两个属性 first 和 last 分别指向 首节点和尾节点
-
每个节点(Node对象),里面又维护了prev、next、item、三个属性,其中通过prev指向前一个,通过next指向后一个节点。最终实现双向链表。
-
LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高。
LinkedList 源码分析
-
LinkedList的增删改查案例
@SuppressWarnings({"all"}) public class LinkedListCRUD { public static void main(String[] args) { LinkedList linkedList = new LinkedList(); linkedList.add(1); linkedList.add(2); linkedList.add(3); System.out.println("linkedList="+linkedList); // 删除节点,默认删除首节点 linkedList.remove(); System.out.println("linkedList="+linkedList); // 修改某个节点对象 linkedList.set(0,666); System.out.println("linklist="+linkedList); // 得到某个节点对象 // get(1)为第二个对象 Object o = linkedList.get(1); System.out.println(o); // LinkedList 实现 List 接口 遍历可是迭代器 System.out.println("====linkedList迭代器遍历"); Iterator iterator = linkedList.iterator(); while (iterator.hasNext()) { Object next = iterator.next(); System.out.println(next); } System.out.println("====增强for循环遍历"); for (Object o1 : linkedList) { System.out.println(o1); } System.out.println("====传统for循环"); for (int i = 0; i < linkedList.size(); i++) { System.out.println(linkedList.get(i)); } } }
LinkedList 添加元素
-
创建集合对象
LinkedList linkedList = new LinkedList();
/** * Constructs an empty list. */ public LinkedList() { }
初始化双向链表
-
执行add()方法
public boolean add(E e) { linkLast(e); return true; }
将新的节点,加入到双向链表的最后
void linkLast(E e) { final Node<E> l = last; final Node<E> newNode = new Node<>(l, e, null); last = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++; }
LinkedList 删除元素
// 删除节点,默认删除首节点
linkedList.remove();
System.out.println("linkedList="+linkedList);
public E remove() {
return removeFirst();
}
-
首先让 f 指向 首节点,判断首节点是否为空;
如果为空,抛出异常;
如果不为空,执行删除操作;
public E removeFirst() { final Node<E> f = first; if (f == null) throw new NoSuchElementException(); return unlinkFirst(f); }
-
执行删除操作
将 首节点置空,first指向下一个节点,下一个节点的prev指向空,即将下一个节点调整为首节点,原首节点有GC算法回收。
private E unlinkFirst(Node<E> f) { // assert f == first && f != null; final E element = f.item; final Node<E> next = f.next; f.item = null; f.next = null; // help GC first = next; if (next == null) last = null; else next.prev = null; size--; modCount++; return element; }
ArrayList 和 LinkedList 比较
底层结构 | 增删效率 | 改查效率 | |
---|---|---|---|
ArrayList | 可变数组 | 较低,数组扩容 | 较高 |
LinkedList | 双向链表 | 较高,通过链表追加 | 较低 |
如何选择ArrayList和LinkedList:
- 如果改查的操作较多,选择ArrayList
- 如果增删的操作较多,选择LinkedList
- 一般来说,程序中,80%-90%都是查询,因此大部分情况下选择ArrayList
- 在一个项目中,根据业务灵活选择,也可能这样,一个模块使用的是ArrayList,另外一个模块是LinkedList。根据业务合理选择。