文章目录
框架体系
1、集合主要分了两组(单列集合,双列集合)
2、Collection接口有两个重要的子接口List与Set,他们的实现子类都是单列集合
3、Map接口的实现子类都是双列集合,存放K-V
Collection
1、Collection接口说明
public interface Collection<E> extends Iterable<E>
(1) collection实现子类可以存放多个元素,每个元素可以使Object(即只要是Object及其子类都可以往里面放)
(2)有些collection的实现类,可以存放重复元素
(3)有些collection的实现类是有序的(List),有些不是有序的(Set)
(4)Collection接口没有直接实现子类,他是通过它的子接口Set和List来实现的
2、Collection常用方法
由于接口无法实例化,所以使用其实现子类ArrayList来演示
public class CollectionMethod {
public static void main(String[] args) {
List list = new ArrayList();
//add:添加单个元素
list.add("jack");
list.add(10);//这里有一个自动装箱的过程,list.add(new Integer(10))
list.add(true);//这里有一个自动装箱的过程
System.out.println("list="+list);//结果为:list=[jack, 10]
//remove:删除指定元素
//list.remove(0);删除第一个元素
list.remove(true);//删除指定元素
System.out.println("list="+list);//结果为:list=[jack, 10]
//contains:查找元素是否存在
System.out.println(list.contains("jack"));
//size:获取元素个数
System.out.println(list.size());
//isEmpty:判断是否为空
System.out.println(list.isEmpty());
//clear:清空
list.clear();
//addAll:添加多个元素
ArrayList list2=new ArrayList();
list2.add("红楼梦");
list2.add("三国演义");
list.addAll(list2);
System.out.println("list="+list);
//containsAll:查找多个元素是否都存在
System.out.println(list.containsAll(list2));
//removeAll:删除多个元素
list.removeAll(list2);
}
}
3、Collection接口遍历元素的方式一:使用Iterator
(1)Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
(2)实现了Collection接口的集合类都有一个iterator()方法,用于返回一个实现了iterator接口的对象,即可以返回一个迭代器
//以下为源码
/**
* Return an iterator over elements of type {@code T}
* @return an Iterator.
*/
Iterator<T> iterator();
(3)iterator仅用于遍历集合,Iterator本身并不存放对象。
(4)执行原理
每次调用next方法,指针就会向下移动一次并将数据取出来。
注意:在调用iterator.next()方法之前,必须调用iterator.hasNext()进行检测,若不调用且下条记录无效时会抛出NoSuchElementException异常。
(5)例子
public class CollectionInterator {
public static void main(String[] args) {
Collection col = new ArrayList();
col.add(new Book("三国演义","罗贯中",10.1));
col.add(new Book("红楼梦","曹雪芹",10.1));
col.add(new Book("水浒传","施耐庵",10.1));
//遍历集合
//1.先得到col对应的迭代器
Iterator iterator =col.iterator();
//2、使用while循环遍历,可以使用itit快速生成
while (iterator.hasNext()) {
//返回下一个元素,类型是Object
Object obj = iterator.next();
System.out.println("obj="+ obj);
}
//3、当退出循环后,这时iterator迭代器指向最后的元素
//iterator .next();//NoSuchElementException
//4、如果希望再次遍历,需要重置我们的迭代器
iterator =col.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println("===第二次遍历===");
System.out.println("obj="+ obj);
}
}
}
class Book{
private String name;
private String author;
private 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 String getAuthor() {
return author;
}
public double getPrice() {
return price;
}
public void setName(String name) {
this.name = name;
}
public void setAuthor(String author) {
this.author = author;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price +
'}';
}
}
4、Collection接口遍历元素的方式二:使用for循环
public class CollectionFor {
public static void main(String[] args) {
Collection col = new ArrayList();
col.add(new Book("三国演义","罗贯中",10.1));
col.add(new Book("红楼梦","曹雪芹",10.1));
col.add(new Book("水浒传","施耐庵",10.1));
//增强for循环,快捷键:I
//增强for在地层仍然是迭代器
for(Object book:col){
System.out.println("book="+book);
}
//注意,增强for循环也能在数组中使用
int[] nums={1,3,56,7};
for(int i:nums){
System.out.println("i="+i);
}
}
}
List
List接口是Collection接口的子接口
1、基本介绍
(1)List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复
(2)List集合中每个元素都有其对应的顺序索引,即List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
public class ListTest {
public static void main(String[] args) {
//List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复
List list = new ArrayList();
list.add("Tom");
list.add("Jack");
list.add("Mary");
list.add("Tom");
System.out.println("list="+list);
//2.List集合中每个元素都有其对应的顺序索引,即支持索引
//索引从0开始
System.out.println(list.get(3));
}
}
(3)JDK API中List接口的实现类有
2、List接口的常用方法
(1)void add(String item, int index) :向滚动列表中索引指示的位置添加指定的项。
(2)boolean addAll(int index,Collection eles):从index位置开始将eles中的所有元素添加进来
(3)Object get(int index):获取指定index位置的元素
(4)int indexOf(Object obj):返回obj在集合中首次出现的位置
(5)int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
(6)Object remove(int index):移除指定index位置的元素,并返回此元素
(7)Object set(int index, Object ele):设置指定index位置的元素为ele , 相当于是替换
(8) List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合,注意返回的子集合 fromIndex <= subList < toIndex
,即前闭后开[ )
public class ListMethod {
@SuppressWarnings({"all"})
public static void main(String[] args) {
List list = new ArrayList();
list.add("张三丰");
list.add("贾宝玉");
// void add(int index, Object ele):在index位置插入ele元素
//在index = 1的位置插入一个对象
list.add(1, "韩顺平");
System.out.println("list=" + 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=" + list);
// Object get(int index):获取指定index位置的元素
//说过
// int indexOf(Object obj):返回obj在集合中首次出现的位置
System.out.println(list.indexOf("tom"));//2
// int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
list.add("韩顺平");
System.out.println("list=" + list);
System.out.println(list.lastIndexOf("韩顺平"));
// Object remove(int index):移除指定index位置的元素,并返回此元素
list.remove(0);
System.out.println("list=" + list);
// Object set(int index, Object ele):设置指定index位置的元素为ele , 相当于是替换.
list.set(1, "玛丽");
System.out.println("list=" + list);
// List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
// 注意返回的子集合 fromIndex <= subList < toIndex
List returnlist = list.subList(0, 2);
System.out.println("returnlist=" + returnlist);
}
}
3、List接口遍历元素的三种方式
三种方式为使用iterator,增强for,for循环
public class ListFor {
@SuppressWarnings({"all"})
public static void main(String[] args) {
//List 接口的实现子类 Vector LinkedList
//List list = new ArrayList();
//List list = new Vector();
List list = new LinkedList();
list.add("jack");
list.add("tom");
list.add("鱼香肉丝");
list.add("北京烤鸭子");
//遍历
//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));
}
}
}
练习:使用冒泡排序按照书的价格排序
public class ListExercise02 {
public static void main(String[] args) {
//List list = new ArrayList();
List list = new LinkedList();
//List list = new Vector();
list.add(new Book("红楼梦", "曹雪芹", 100));
list.add(new Book("西游记", "吴承恩", 10));
list.add(new Book("水浒传", "施耐庵", 19));
list.add(new Book("三国", "罗贯中", 80));
//list.add(new Book("西游记", "吴承恩", 10));
//遍历
for (Object o : list) {
System.out.println(o);
}
//冒泡排序
sort(list);
System.out.println("==排序后==");
for (Object o : list) {
System.out.println(o);
}
}
//静态方法
//价格要求是从小到大
public static void sort(List list) {
int listSize = list.size();
for (int i = 0; i < listSize - 1; i++) {
for (int j = 0; j < listSize - 1 - i; j++) {
//取出对象Book
Book book1 = (Book) list.get(j);
Book book2 = (Book) list.get(j + 1);
if (book1.getPrice() > book2.getPrice()) {//交换取出来的Book
list.set(j, book2);//book2放到j
list.set(j + 1, book1);//book1放到j+1
}
}
}
}
}
ArrayList
1、ArrayList注意事项
(1)ArrayList可以加入null,并且可以加入多个
(2)ArrayList是由数组来实现数据存储的
(3)ArrayList基本等同于Vector,但是ArrayList是线程不安全的(执行效率高),在多线程情况下不建议使用ArrayList。
public class ArrayListDetail {
public static void main(String[] args) {
//ArrayList 是线程不安全的, 可以看源码 没有 synchronized
/*
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
*/
ArrayList arrayList = new ArrayList();
arrayList.add(null);
arrayList.add("jack");
arrayList.add(null);
arrayList.add("hsp");
System.out.println(arrayList);
}
}
2、ArrayList扩容机制
(1)ArrayList中维护了一个Object类型的数组elementData
.
transient Object[] elementData;//transient 表示瞬间,短暂的;该属性不会被序列化
(2)当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第1次添加,则扩容elementData为10,如需要再次扩容,则容量elementData为1.5倍。
(3)如果使用的是指定大小的构造器,则初试elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍。
3、ArrayList底层源码
(1)成员变量
// 默认的容量
private static final int DEFAULT_CAPACITY = 10;
// 默认容量为空的数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 长度为0的数组
private static final Object[] EMPTY_ELEMENTDATA = {};
// 集合真正存储元素的数组
transient Object[] elementData;
// 集合最大容量2的31次方-1-8
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
// 集合容量
private int size;
// 更改次数
protected transient int modCount = 0;
(2)构造方法
// 1.构造一个初始容量的空数组。
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 2.构造具有指定初始容量的数组。
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];//根据参数创建数组
} else if (initialCapacity == 0) {//若参数为0则不创建数组,而将已经初始化的成员变量给它
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
//3.构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序
public ArrayList(Collection<? extends E> c) {
// 将集合构造中的集合对象转成数组
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == ArrayList.class) {
// 类型为ArrayList则直接赋值
elementData = a;
} else {
//如果不一样,使用Arrays的copyOf方法进行元素的拷贝
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
// replace with empty array.
elementData = EMPTY_ELEMENTDATA;
}
}
(3)成员方法
①添加方法
[1]指定的元素添加
public boolean add(E e) {
// 对内部容量进行判断,详情见下方的被调方法
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
[2]指定位置、元素添加
public void add(int index, E element) {
// 检查索引是否在范围内
rangeCheckForAdd(index);
// 对内部容量进行判断,详情见下方的被调方法
ensureCapacityInternal(size + 1);
// 实现从源数组的起始位置开始复制全部元素到目标数组的指定位置中
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
[3]指定集合中的所有元素添加
public boolean addAll(Collection<? extends E> c) {
//源数组
Object[] a = c.toArray();
//源数组长度
int numNew = a.length;
// 对内部容量进行判断,详情见下方的被调方法
ensureCapacityInternal(size + numNew); // Increments modCount
// 实现从源数组的起始位置开始复制全部元素到目标数组的指定位置中
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}```
[4]指定位置和集合中的所有元素添加
```java
public boolean addAll(int index, Collection<? extends E> c) {
// 检查索引是否在范围内
rangeCheckForAdd(index);
//源数组
Object[] a = c.toArray();
//源数组长度
int numNew = a.length;
// 对内部容量进行判断,详情见下方的被调方法
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
// 实现从源数组的起始位置开始复制全部元素到目标数组的指定位置中
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
// 实现从源数组的起始位置开始复制全部元素到目标数组的指定位置中
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
[5]添加方法中的调用方法
// 1.主体函数
private void ensureCapacityInternal(int minCapacity) {
// 对容量进行判断
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 2.通过最小容量和默认容量求出较大值 (用于第一次扩容)
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
// 3.判断是否需要进行扩容
private void ensureExplicitCapacity(int minCapacity) {
// 实际修改集合次数+1 (在扩容的过程中没用,主要是用于迭代器中)
modCount++;
// 判断当前最小容量是否大于数组长度
if (minCapacity - elementData.length > 0)
// 将计算出来的容量传递给核心扩容方法
grow(minCapacity);
}
// 4.核心扩容方法
private void grow(int minCapacity) {
// 记录数组的实际长度
int oldCapacity = elementData.length;
// 核心扩容算法,原容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 判断新容量是否小于当前最小容量(第一次调用add方法必然小于)
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 判断新容量是否大于最大数组长度,如果
if (newCapacity - MAX_ARRAY_SIZE > 0)
// 条件满足就计算出一个超大容量
newCapacity = hugeCapacity(minCapacity);
// 调用数组工具类方法,创建一个新数组,将新数组的地址赋值给elementData
elementData = Arrays.copyOf(elementData, newCapacity);
}
[6]添加方法备注
ensureCapacity方法
:向ArrayList添加大量元素之前通过该方法预设数组大小,以减少增量重新分配的次数。
// 确保集合至少可以容纳参数指定的元素数。
public void ensureCapacity(int minCapacity) {
// 最小扩容
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
? 0
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
②删除方法
[1]根据索引删除
public E remove(int index) {
// 检查索引是否在范围内
rangeCheck(index);
// 实际修改集合次数+1 (在扩容的过程中没用,主要是用于迭代器中)
modCount++;
// 获取当前索引原来的数据
E oldValue = elementData(index);
// 计算集合需要移动元素的个数
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);、
// 将源集合最后一个元素置为null,尽早让垃圾回收机制对其进行回收
elementData[--size] = null;
//返回当前索引原来的数据
return oldValue;
}
[2]根据对象删除
// 1.根据对象删除
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
// 遍历并判断集合的元素是否为null
if (elementData[index] == null) {
// null则用fastRemove方法快速删除
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
// 用o对象的equals方法和集合每一个元素进行比较
if (o.equals(elementData[index])) {
// 如果相等,调用fastRemove方法快速删除
fastRemove(index);
return true;
}
}
// 如果集合没有o该元素,那么就会返回false
return false;
}
// 2.
private void fastRemove(int index) {
// 实际修改集合次数+1 (在扩容的过程中没用,主要是用于迭代器中)
modCount++;
// 计算集合需要移动元素的个数
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 将源集合最后一个元素置为null,尽早让垃圾回收机制对其进行回收
elementData[--size] = null; // clear to let GC do its work
}
③获取方法
public E set(int index, E element) {
// 检查索引是否在范围内
rangeCheck(index);
// 获取当前索引原来的数据
E oldValue = elementData(index);
// 替换后返回旧数据
elementData[index] = element;
return oldValue;
}