Java集合(一)——LinkedList和ArrayList源码
一、集合
集合主要分为两组(单列集合,双列集合)
-
单列集合:存放单个元素
-
- Collection:两个重要的接口 List Set
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("jack");
arrayList.add("tom");
-
双列集合:键值对的形式存元素K-V
-
- Map
HashMap<String, Object> hashMap = new HashMap<>(64);
hashMap.put("NO1", "北京");
hashMap.put("NO2", "上海");
1.2、Conllection接口和常用方法
1.2.1、常用方法
- add:添加单个元素
- remove:删除指定元素
- contains:查找元素是否存在
- size:获取元素的个数
- isEmpty:判断集合是否为空
- clear:清空集合
- addAll:添加多个元素
- containsAll:查找多个元素是否都存在
- removeAll:删除多个元素
/**
* @author java小豪
* @date 2022/6/8
*/
public class CollectionMethod {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
// add:添加单个元素
list.add("jack");
// list.add(new Integer(10))
list.add(10);
list.add(true);
System.out.println("List=" + list);
// remove:删除指定元素
// 通过下标删除元素
list.remove(0);
// 指定删除的元素
list.remove(true);
System.out.println("List=" + list);
// contains:查找元素是否存在
System.out.println(list.contains("jack"));
// size:获取元素的个数
System.out.println(list.size());
// isEmpty:判断集合是否为空
System.out.println(list.isEmpty());
// clear:清空集合
//list.clear();
//System.out.println(list);
// addAll:添加多个元素
ArrayList<Object> arrayList = new ArrayList<>();
arrayList.add("红楼梦");
arrayList.add("三国演义");
list.addAll(arrayList);
System.out.println(list);
// containsAll:查找多个元素是否都存在
System.out.println(list.containsAll(arrayList));
// removeAll:删除多个元素
list.add("聊斋");
list.removeAll(arrayList);
System.out.println(list);
}
}
1.2.2、Conllection接口遍历元素方式
- 使用Iterator(迭代器)
/**
* @author java小豪
* @date 2022/6/8
*/
public class CollectionIterator {
public static void main(String[] args) {
Collection<Object> collection = new ArrayList<>();
collection.add(new Book("三国演义", "罗贯中", 50));
collection.add(new Book("小李飞刀", "古龙", 70.0));
collection.add(new Book("红楼梦", "曹雪芹", 60.0));
// System.out.println("collection = " + collection);
// 得到迭代器
Iterator<Object> iterator = collection.iterator();
// 使用while循环遍历集合
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println("obj = " + obj);
}
// 重置迭代器
iterator = collection.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
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 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(元素类型 元素名 : 集合名或数组名) { 访问元素 }
/**
* @author java小豪
* @date 2022/6/8
*/
public class CollectionFor {
public static void main(String[] args) {
Collection<Object> collection = new ArrayList<>();
collection.add(new Book("三国演义", "罗贯中", 50));
collection.add(new Book("小李飞刀", "古龙", 70.0));
collection.add(new Book("红楼梦", "曹雪芹", 60.0));
// 增强for
// 增强for底层仍然是迭代器
for (Object book : collection) {
System.out.println("book = " + book);
}
// 增强for用在数组上
int[] nums = {1, 6, 7, 9, 10};
for (int i : nums) {
System.out.println("i = " + i);
}
}
}
1.3、List接口和常用方法
1.3.1、介绍
-
List集合类中元素有序、且可重复
-
List集合中的每个元素有其对应的顺序索引,即支持索引。
-
List容器中的元素对应一个整数的序号记载在容器中的位置,可以根据序号取出容器中的元素.
/**
* @author java小豪
* @date 2022/6/9
*/
public class List_ {
public static void main(String[] args) {
// List集合类中元素有序、且可重复
List<Object> list = new ArrayList<>();
list.add("jack");
list.add("mary");
list.add("tom");
list.add("chen");
System.out.println("list = " + list);
// List集合中的每个元素有其对应的顺序索引,即支持索引。
System.out.println(list.get(2));
}
}
1.3.2、常用方法
- 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 fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
/**
* @author java小豪
* @date 2022/6/9
*/
public class ListMethod {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
//void add(int index, Object ele):在index位置插入ele元素
list.add("张三丰");
list.add("李小龙");
// 在index = 1 的位置插入一个对象
list.add(1, "宋江");
System.out.println("list = " + list);
//boolean addAll(int index, Collection eles):从index位置开始将eles中的元素插入
List<Object> list2 = new ArrayList<>();
list2.add("jack");
list2.add("tom");
list.addAll(1, list2);
System.out.println("list = " + list);
//Object get(int index):获取指定index位置的元素
System.out.println(list.get(3));
//int indexOf(Object obj):返回obj在当前集合中首次出现的位置
System.out.println(list.indexOf("tom"));
//int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
list.add("tom");
System.out.println("list = " + list);
System.out.println(list.lastIndexOf("tom"));
//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, toIndex)
List returnList = list.subList(0, 2);
System.out.println("returnList = " + returnList);
}
}
1.3.3、练习
- 添加十个以上的 String “hello”
- 在2号位插入一个元素
- 获取第6个元素
- 删除第7个元素
- 修改第8个元素
- 使用迭代器遍历集合
/**
* @author java小豪
* @date 2022/6/10
*/
public class ListExercise {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
// 添加十个以上的 String "hello"
for (int i = 0; i < 12; i++) {
list.add("hello" + i);
}
System.out.println("list = " + list);
// 在2号位插入一个元素"chen"
list.add(1, "chen");
System.out.println("list = " + list);
// 获取第6个元素
System.out.println("第6个元素 = " + list.get(5));
// 删除第7个元素
list.remove(6);
System.out.println("list = " + list);
// 修改第8个元素
list.set(7, "三国演义");
System.out.println("list = " + list);
// 使用迭代器遍历集合
Iterator<Object> iterator = list.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println("obj = " + obj);
}
}
}
1.3.4、List集合的三种遍历方式
- 迭代器Iterator
- 增强for
- 普通for
/**
* @author java小豪
* @date 2022/6/10
*/
public class ListFor {
public static void main(String[] args) {
// List 接口的实现子类 Vector LinkedList
// List<Object> list = new ArrayList<>();
List<Object> list = new LinkedList<>();
// List<Object> list = new Vector<>();
list.add("jack");
list.add("tom");
list.add("天龙八部");
list.add("北京烤鸭");
// 遍历
// 1.迭代器
Iterator<Object> iterator = list.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println("obj = " + obj);
}
System.out.println("=================");
//2.增强for循环
for (Object x : list) {
System.out.println(x);
}
System.out.println("================");
//3.普通for循环
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
1.3.5、List排序练习
Book类
/**
* @author java小豪
* @date 2022/6/10
*/
public class Book {
private String name;
private String author;
private int price;
public Book(String name, String author, int 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 int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "名称:"+ name +"\t\t价格:" + price+ "\t\t作者:" + author;
}
}
main:
/**
* @author java小豪
* @date 2022/6/10
*/
public class ListExercise2 {
public static void main(String[] args) {
// List<Object> list = new Vector<>();
List<Object> list = new ArrayList<>();
// List<Object> list = new LinkedList<>();
list.add(new Book("红楼梦", "曹雪芹", 70));
list.add(new Book("西游记", "吴承恩", 10));
list.add(new Book("水浒传", "施耐庵", 90));
list.add(new Book("三国志", "罗贯中", 80));
// 遍历
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<Object> list) {
for (int i = 0; i < list.size() - 1; i++) {
for (int j = 0; j < list.size() - 1 - i; j++) {
// 取出对象Book
Book book1 = (Book) list.get(j);
Book book2 = (Book) list.get(j + 1);
if (book1.getPrice() > book2.getPrice()) {
list.set(j, book2);
list.set(j + 1, book1);
}
}
}
}
}
1.4、ArrayList底层结构和源码分析
1.4.1、注意事项
- ArrayList可以存放空值null
- ArrayList是由数组来实现数据存储的
- ArrayList是线程不安全的(执行效率高),多线程情况下,不建议使用ArrayList
/**
* @author java小豪
* @date 2022/6/10
*/
public class ArrayListDetail {
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add(null);
arrayList.add("jack");
arrayList.add(null);
System.out.println("arrayList = " + arrayList);
}
}
1.4.2、ArrayList扩容机制
- ArrayList中维护一个Object类型的数组elementData transient Object[] elementData;
- transient :表示瞬间,短暂的
- 当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加,则扩容elementData为10,如需再次扩容,则扩容elementData为1.5倍。
- 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如需扩容,则直接扩容elementData为1.5倍。
1.4.3、ArrayList源码
一、当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加,则扩容elementData为10,如需再次扩容,则扩容elementData为1.5倍。
演示代码
/**
* @author java小豪
* @date 2022/6/10
*/
public class ArrayListSource {
public static void main(String[] args) {
// 使用午餐构造器创建ArrayList对象
ArrayList<Integer> list = new ArrayList<>();
// ArrayList<Integer> list = new ArrayList<>(8);
// 使用for给list集合添加 1-10数据
for (int i = 1; i <= 10; i++) {
list.add(i);
}
// 使用for给list集合添加11-15数据
for (int i = 11; i <= 15; i++) {
list.add(i);
}
list.add(200);
list.add(100);
}
}
1.5、Vector底层结构和源码分析
1.5.1、基本介绍
- Vector类的定义说明
- Vector底层也是一个对象数组,protected Object[] elementData;
- Vector是线程同步的,即线程安全,Vector类的操作方法带有synchronized
1.5.2、Vector底层和ArrayList的比较
底层结构 | 版本 | 线程安全(同步)效率 | 扩容倍数 | |
---|---|---|---|---|
ArrayList | 可变数组 | JDK1.2 | 不安全,效率高 | 如果有参构造1.5倍如果是无参1.第一次102.从第二次开始按照1.5倍 |
Vector | 可变数组 | JDK1.0 | 安全效率高 | 如果是无参,默认10,满后直接按2倍扩容如果指定大小,则每次直接按2倍扩容 |
1.5.3、源码解读
一、创建Vector对象时使用无参构造器源码
/**
* @author java小豪
* @date 2022/6/11
*/
public class Vector_ {
public static void main(String[] args) {
// 无参构造器
Vector<Integer> vector = new Vector<>();
for (int i = 0; i < 10; i++) {
vector.add(i);
}
vector.add(100);
System.out.println("vector = " + vector);
// 源码解读
// 1. new Vector() 底层
/*
public Vector() {
this(10);
}
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);
}
*/
}
}
二、分析图
1.6、LinkedList底层结构
1.6.1、说明
- LinkedList实现了双向链表和双端队列特点
- 可以添加任意元素(元素可以重复),包括null
- 线程不安全,没有实现同步
1.6.2、LinkedList的底层操作机制
- LinkedList底层维护了一个双向链表
- LinkedList中维护了两个属性first和last分别指向首节点和尾节点
- 每个节点(Node对象),里面有维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点。最终实现双向链表
- LinkedList的元素添加和删除,不是通过数组完成的,相对来说效率较高
- 模拟简单的双向链表
/**
* @author java小豪
* @date 2022/6/11
*/
public class LinkedList01 {
public static void main(String[] args) {
// 模拟简单的双向链表
Node jack = new Node("jack");
Node tom = new Node("tom");
Node chen = new Node("chen");
// 连接三个节点,形成双向链表
// jack -> tom -> chen
jack.next = tom;
tom.next = chen;
// chen -> tom -> jack
chen.prev = tom;
tom.prev = jack;
// 让first引用指向jack,就是双向链表的头节点
Node first = jack;
// 让last引用指向chen,就是双向链表的尾节点
Node last = chen;
// 遍历 从头到尾遍历
while (true) {
if (first == null) {
break;
}
// 输出信息
System.out.println(first);
first = first.next;
}
// 遍历 从尾到头遍历
System.out.println("===从尾到头遍历===");
while (true) {
if (last == null) {
break;
}
// 输出信息
System.out.println(last);
last = last.prev;
}
// 添加元素
// 1.创建一个Node结点, name 是张飞
Node zf = new Node("张飞");
zf.next = chen;
zf.prev = tom;
chen.prev = zf;
tom.next = zf;
// 让first 再次指向jack
first = jack;
System.out.println("===从头到尾遍历===");
while (true) {
if (first == null) {
break;
}
// 输出信息
System.out.println(first);
first = first.next;
}
}
}
/**
* 定义一个Node类
*/
class Node {
/**存放数据*/
public Object item;
/**指向下一个节点*/
public Node next;
/**指向前一个节点*/
public Node prev;
public Node(Object name) {
this.item = name;
}
public String toString() {
return "Node name = " + item;
}
}
1.6.3、LinkedList底层源码
1.6.3.1、添加元素
- linkedList.add(1);
// 1、LinkedList<Integer> linkedList = new LinkedList<>();
public LinkedList() {}
// 2、这是LinkedList 的属性first = null last = null
// 3、执行add方法
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++;
}
- 流程图
- 执行 add 方法
- 执行 linkList
1.6.3.2、删除元素
- linkedList.remove();
// 1、linkedList.remove();// 默认删除第一个元素
// 2、执行 removeFirst() 方法
public E remove() {
return removeFirst();
}
// 3、执行
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
// 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;
}
- 流程图
1.6.3.3、LinkedList的其他方法
- get()
- remove(int index)
- set(int index)
等等的源码都可以依照以上方法进行追溯。