# 1、Collection接口和常用方法
## 1、Colleations接口的方法
```java
//add添加单个元素G
List list = new ArrayList();
list.add("1");
list.add(10);//list.add(new Integer(10))
list.add(true);
System.out.println("list="+list);
//remove 删除指定元素
// list.remove(1);//删除第2个元素
list.remove("1");//删除指定对象
System.out.println("list="+list);
//contains :查找元素是否存在
System.out.println(list.contains(10));//true
//size :获取元素个数
System.out.println(list.size());//2
//isEmpty:判断是否为空
list.clear();
//System.out.println("list="+list);
//addAll:添加多个元素
ArrayList<Object> list1 = new ArrayList<>();
list1.add("java");
list1.add("python");
list.addAll(list1);
System.out.println("list="+list);
//containsAll:查找多个元素是否都存在
System.out.println(list.containsAll(list1));//true
//removeAll:删除多个元素
list.add("c++");
list.removeAll(list1);
System.out.println("list="+list);
```
## 2、Collection接口遍历元素的方式
### 1、使用Iterator迭代器
1、Iterator对象称为迭代器,主要用于遍历Collection集合中的元素。
2、所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了iterator接口的对象,即可以返回一个迭代器。
3、iterator的结构;
![image-20230204102423910](D:\study\Java_study\image\iterator.png)
4、iterator近用于遍历集合,iterator本身并不存放对象
**注意:在调用iterator.next()方法之前必须要调用iterator.hasNext()进行检测,否则会抛出异常**
```java
package com.demo.collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionIterator {
public static void main(String[] args) {
Collection arrayList = new ArrayList();
arrayList.add(new Book("java","程序员1",1));
arrayList.add(new Book("python","程序员2",2));
arrayList.add(new Book("c++","程序员3",3));
System.out.println("arrayList"+arrayList);
//1.先得到arraylist对应的迭代器
Iterator iterator=arrayList.iterator();
System.out.println(iterator);
while (iterator.hasNext()){//判断是否还有数据
//返回下一个元素,类型是Object
Object obj=iterator.next();
System.out.println("obj"+obj);
}
//3.当迭代器退出while循环后,这时iterator指向了最后的元素,所以需要重制迭代器
//也可以说获取一个新的迭代器
iterator=arrayList.iterator();
System.out.println(iterator);
//while循环快捷键 快速生成 while ===>itit
//显示所有快捷键 ctrl+j
}
}
class Book{
private String bookName;
public Book(String bookName, String name, int age) {
this.bookName = bookName;
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
private String name;
private int age;
@Override
public String toString() {
return "Book{" +
"bookName='" + bookName + '\'' +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
```
### 2、for循环增强
增强for循环不仅可以在集合中使用,也可以在数组中使用。
增强for的底层也是迭代器;
增强for可以理解成简化版的 迭代器遍历;
# 2、list接口和常用方法
## 1、list接口的基本介绍
list接口时Collection接口的子接口;
```java
//list集合类中元素有序(即添加顺序和取出顺序一致,且可重复
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
System.out.println("list"+list);
//2.list集合中的每个元素都有其对应的顺序索引,即支持索引
// 索引是从0开始的
System.out.println(list.get(0));
```
常用的有ArrayList、LinkedList和Vector
## 2、List接口的常用方法
```java
List list = new ArrayList();
list.add("张三丰");
list.add("贾宝玉");
//void add(int index,Object ele):在index位置插入ele元素
//在index = 1 的位置上插入一个对象
list.add(1,"刘备");
//boolean addAll(int index,Collection eles):从index位置开始将eles中的所有元素添加进来
List list1=new ArrayList<>();
list1.add("java");
list.addAll(1,list1);
list.add("张三丰");
System.out.println("list"+list);
//Object get(int index):获取指定index位置的元素
//int indexOf(Object obj):返回obj在集合中首次出现的位置
System.out.println(list.indexOf("java"));
// int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
System.out.println(list.lastIndexOf("张三丰"));
//Object remove(int index):移除指定index位置的元素,并返回此元素
System.out.println(list.remove(4));
//Object set(int index,Object ele):设置指定index位置的元素ele,相当于是替换。
list.set(2,"python");//如果设置索引不存在则会抛出异常
System.out.println("list"+list);
//List subList(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集合
// 注意返回的子集合 fromIndex <= subList <
//返回区间是前闭后开的
List list2=list.subList(0,2);
System.out.println("List2"+list2);
```
### 3、list的三种遍历方式
```java
System.out.println("===========迭代器=========");
Iterator iterator=list.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
System.out.println("========普通的for循环==========");
for (int j = 0; j < list.size(); j++) {
System.out.println(list.get(j));
}
System.out.println("========增强for循环==========");
for (Object obj :list) {
System.out.println(obj);
}
```
# 3、ArrayLisr底层结构和源码分析
arryalist可以加入null,并且可以放多个
```java
//ArrayList是线程不安全的,查看源码,没有 synchronized 关键字修饰
/*
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
*/
List list = new ArrayList();
list.add(null);
list.add("java");
list.add(null);
System.out.println(list);
```
## 1、ArrayList的底层操作机制源码分析(重难点)
### 1、ArrayList中维护了一个Object类型的数组elementData.
```java
transient Object[] elementData; //transient 表示瞬间,短暂的,表示该属性不会被序列化
```
### 2、当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData的1.5倍。
创建了一个空的elementData空数组
```java
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
```
执行list.add
1.先确定是否要扩容
2.然后再执行赋值操作
```java
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
```
```java
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;
}
```
1.modcount++记录当前集合被修改的次数
2.如果若elementData的大小不够就调用group去扩容
```java
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
```
1.扩容方法
2.使用扩容机制来确定要扩容到多大
3.第一次newCapacity =10
4.第二次及其以后按照1.5倍扩容
5.扩容使用的是Arrays.copyOf()
```java
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);
}
```
### 3、如果使用的是指定大小的构造器,则初始elementData 容量为指定大小,如需要再次扩容,则直接扩容elementData的1.5倍。
分析有参构造器创建和使用ArrayList的源码
创建了一个指定大小的elementData数组
this.elementData = new Object[initialCapacity];
```java
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);
}
}
```
有参构造器的扩容机制第一次扩容就按照elementData的1.5倍扩容
其他原理与不指定大小的构造器相同
## 2、Vector底层结构和源码剖析
### 1、Vector累的定义说明
```java
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
```
### 2、Vector底层也是一个对象数组,protected Object[] elementData;
### 3、Vector 是线程同步的,即线程安全,Vector累的操作方法带有synchronized
```java
public synchronized int capacity() {
return elementData.length;
}
```
### 4、在开发中,需要线程同步安全时,考虑用Vector
### 5、源码分析
```java
package com.demo.list;
import java.util.Vector;
public class Vector_ {
public static void main(String[] args) {
//无惨构造器
//1、构造器
//1.1new Vector()无参构造底层
/*
public Vector() {
this(10);
}
*/
//1.2Vector 有参构造器
/*
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
*/
Vector vector = new Vector();
for (int i = 0; i < 10; i++) {
vector.add(i);
}
vector.add(100);
//2.Vector.add(i)
//2.1下面这个方法就是添加数据到Vector集合
/*
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
*/
//2.2 确定是否需要扩容 条件 :minCapacity - elementData.length >0
/*
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
*/
//2.3 如果需要的数组大小 不够用 ,就扩容,扩容算法int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
// capacityIncrement : oldCapacity);
/*
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);
}
*/
}
}
```
## 3、Vector底层结构和ArrayList的比较
![Vector底层和ArrayList的比较](D:\study\Java_study\image\Vector底层和ArrayList的比较.png)
## 4、LinkedList底层结构
### 1、LinkedList 底层实现了双向链表和双端队列特点
### 2、可以添加任意元素(元素可重复),包括null
### 3、线程不安全,没有实现同步
### 4、LinkedList的底层操作机制
1.LinkedList底层中维护了一个双向链表
2.LinkedList中维护了两个属性first和last分别指向 首节点和尾结点
3.每个节点(Node)对象,里面又维护了Prev、next、item、 三个属性,其中通过prev指向前一个,通过next指向后一个节点,最终实现双向链表。
4.所以LinkedList 的元素的添加和删除,不是通过数组完成的,相对来说效率较高。
5.模拟一个简单的双向链表 LinkedList01.java
```java
package com.demo.list;
public class LinkedList01 {
public static void main(String[] args) {
//模拟简单的双向链表
Node jack = new Node("jack");
Node tom = new Node("tom");
Node java = new Node("java");
//连接三个节点,形成双向链表
//jack -> tom -> java
jack.next=tom;
tom.next=java;
//java -> tom -> jack
java.pre=tom;
tom.pre=jack;
Node first = jack; //让first引用指向jack ,就是双向列表的头结点
Node last = java; //让last引用指向java,就是双向列表的尾结点
//演示:遍历
System.out.println("first遍历");
while (true){
if (first==null){
break;
}else {
System.out.println(first);
first=first.next;
}
}
System.out.println("last遍历");
while (true){
if (last==null){
break;
}else {
System.out.println(last);
last=last.pre;
}
}
//演示:链表添加对象/数据
//要求:在Tom和java之间添加一个对象 Python
//1.先创建一个Node节点 ,name就是Python
Node python = new Node("Python");
python.next=java;
python.pre=tom;
tom.next=python;
java.pre=python;
//让first 再次指向jack
first=jack;
//让last重新指向java
last=java;
//演示:遍历
System.out.println("first遍历");
while (true){
if (first==null){
break;
}else {
System.out.println(first);
first=first.next;
}
}
System.out.println("last遍历");
while (true){
if (last==null){
break;
}else {
System.out.println(last);
last=last.pre;
}
}
}
}
//定义一个node类,node对象 表示双向链表的一个节点
class Node{
public Object item;//真正存放数据的地方
public Node next;
public Node pre;
public Node(Object name){
this.item=name;
}
@Override
public String toString() {
return "Node item=" + item ;
}
}
```
### 5、LinkedList底层结构
```java
package com.demo.list;
import java.util.LinkedList;
public class LinkedListCRUD {
public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
linkedList.add("1");
linkedList.add("2");
System.out.println(linkedList.remove());//这里默认删除的是第一个元素
System.out.println("linkedList="+linkedList);
}
/* 1.构造器
public LinkedList() {
}
2.这时 linkedList 的属性 first = null last= null
3.执行添加
public boolean add(E e) {
linkLast(e);
return true;
}
4.将新的节点,加入到双向链表的最后
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++;
}
5.删除过程分析
5.1.执行linkedList.remove();//这里默认删除的是第一个元素
5.2 执行remove()
public E remove() {
return removeFirst();
}
5.3 执行 removeFirst()
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
5.4 执行unlinkFirst() 将f指向的双向链表的第一个节点拿掉
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;
}
*/
}
```
### 6、ArrayList和LinkedList比较
![ArrayList和LinkedList的比较](D:\study\Java_study\image\ArrayList和LinkedList的比较.png)
# 5、Set接口
## 1、Set接口基本介绍
·无序(添加和取出的顺序不一致),没有索引
·不允许重复元素,最多包含一个null
·和List接口一样,他都是继承自Collection接口,因此常用方法与list一样
·set接口的遍历方式只有迭代器和增强for循环
```java
public static void main(String[] args) {
//1.以Set接口的实现类 HashSet来讲解Set接口的方法
//2. set 接口的实现类的对象(Set接口对象),不能存放重复的元素,可以添加一个null
//3. Set 接口存放数据是无序的(添加顺序和取出顺序不一致)
//4. 注意:取出的顺序虽然不是添加的顺序,但是它是固定的
HashSet set = new HashSet();
set.add("1");
set.add("2");
set.add(2);
set.add(null);
set.add(null);
System.out.println(set);
set.add("测试");
System.out.println(set);
//遍历
System.out.println("=====使用迭代器=====");
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println("obj="+obj);
}
System.out.println("======增强for循环=====");
for (Object obj:set) {
System.out.println("obj="+obj);
}
//set接口对象不能通过索引获取对象
}
```
## 2、HashSet
HashSet的底层就是HashMap
```java
//1. 构造器源码
/*
public HashSet() {
map = new HashMap<>();
}
*/
//2.可以存放null,但只有一个null
//3.HashSet不保证元素是有序的,取决于hash后,在确定索引的结果。(即,不保证存放元素与取出元素的顺序一致)
```
HashMap底层是(数组+链表+红黑树)
添加一个元素是,先得到hash值-会转成-》索引值
找到存储数据表table,看这个索引位置是否已经存放的有元素
如果没有,直接加入
如果有,调用equals比较,如果相同,就放弃添加,如果不相同,则添加到最后
**在java8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD(默认是8),并且table的大小>=MIN_TREELFY_CAPACITY(64)。就会树化(红黑树)**