java基础 集合(1)集合概念、 Collection接口 、 List
目录
一、集合的概念
-
数组
-
长度开始的时候长度必须指定,而且一旦指定,不能更改
-
保存的必须是同一类型的元素
-
使用数组增加和删除元素比较麻烦
-
-
集合
-
可以动态保存任意多个对象,使用比较方便
-
提供一系列方便操作对象的方法 : add remove 等
-
使用集合添加删除新元素 简洁了
-
1、集合的框架体系
-
集合主要是两组 (单列集合、双列集合)
-
Collection 接口有两个很重要的子接口 List 和 Set ,他们的实现子类都是单列集合
-
Map接口的实现自了一 是双列集合 存放的键值对

二、Collection接口
1.Collection接口和常用方法
public interface Collection<E> extends Iterable<E>
-
Collection实现子类可以存放多个元素,每个元素都可以是Object
-
有些Collection的实现类,可以存放重复的元素,有些不可以
-
有些Collection的实现类,是有序的,有些不是有序的
-
Collection接口没有直接的实现子类,是通过它的子接口List和Set来实现的
1.1 常用方法
通过 ArrayList来演示Collection的常用方法
-
add 添加单个元素
-
remove 删除指定元素
-
contains 查找元素是否存在
-
size 获取元素个数
-
isEmpty 判断是否为空
-
clear 清空
-
addAll 添加多个元素
-
containsAll 查找多个元素是否都存在
-
removeAll 删除多个元素
public static void main(String[] args) {
Collection list = new ArrayList();
// 1. add 添加单个元素
list.add("jack");
list.add(0);
list.add(true);
System.out.println("list:" + list); //list:[jack, 0, true]
// 2. remove 删除指定元素 可以删除 指定元素
// list.remove(true); //删除指定元素
// System.out.println("list:" + list);
// 3. contains 查找元素是否存在 返回结果boolean
System.out.println(list.contains("jack")); //true
// 4. size 获取元素个数
System.out.println(list.size()); //3
// 5. isEmpty 判断是否为空
System.out.println(list.isEmpty()); //false
// 6. clear 清空
list.clear();
System.out.println("清空集合后:"+list); //清空集合后:[]
// 7. addAll 添加多个元素
List list1 = new ArrayList();
list1.add("洪龙梅");
list1.add("李科技");
list.addAll(list1);
System.out.println(list); //[洪龙梅, 李科技]
// 8. containsAll 查找多个元素是否都存在
System.out.println(list.containsAll(list1)); //true
// 9. removeAll 删除多个元素
list.add("雾都");
System.out.println(list.removeAll(list1)); //true
System.out.println(list); //[雾都]
}
}
2、Collection接口遍历方式
2.1 使用Iterator(迭代器)

-
Iterator称为迭代器,主要用于遍历Collection集合中的元素
-
所有实现了Collection接口的集合类都有一个iterator()方法,用于获取实现Iterator接口的对象,即可以返回一个迭代器
-
Iterator 结构

4.Iterator 仅用于遍历集合,Iterator不存放元素
注意: 在调用iterator.next() 方法之前,一定要先调用iterator.hasNext()进行检测。
如果不调用,且下一条记录无效,直接调用iterator.next()方法 会抛出 NoSuchElementException异常
package com.sofwin.controller;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* @packageName: com.sofwin.controller
* @author: wentao
* @date: 2022/10/29 20:02
* @version: 1.0
* @email 1660420659@qq.com
* @description: 迭代器
*/
public class CollectionIterator {
public static void main(String[]args){
Collection col =new ArrayList();
col.add(new Book("三国演义","罗贯中",12.3));
col.add(new Book("文字","罗贯中",144.3));
col.add(new Book("红龙","柠檬",122.3));
System.out.println("Collection:"+col);
//先得到集合对应的迭代器
Iterator iterator = col.iterator();
//使用while循环遍历
while (iterator.hasNext()) {
System.out.println("树:"+iterator.next());
}
//当退出while循环后 这是 Iterator的迭代器,指向了最后的元素
//会抛出异常 Exception in thread "main" java.util.NoSuchElementException
//如果不想报错 需要我们重置 迭代器
//iterator = col.iterator();
iterator.next();
}
}
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 +
'}';
}
}
2.2 foreach (增强for循环)
-
增强for循环 不仅可以使用在集合中,也可以运用在数组上
-
底层仍然是迭代器 (可以认为是简化版的迭代器)
public class CollectionFor {
public static void main(String[]args){
Collection col =new ArrayList();
col.add(new Book("三国演义","罗贯中",12.3));
col.add(new Book("文字","罗贯中",144.3));
col.add(new Book("红龙","柠檬",122.3));
for (Object o : col) {
System.out.println(o);
}
}
}
三、List接口
1、List接口的常用方法
-
List集合类 中的元素是有序的(添加顺序和取出顺序是一致的)、且可以重复
public static void main(String[]args){ List list = new ArrayList(); list.add("jack"); list.add("tom"); list.add("mary"); list.add("jack"); System.out.println(list); //[jack, tom, mary, jack] 有序可重复 } -
List集合中的每一个元素都有其对应的顺序索引,即支持索引(从0开始)
public static void main(String[]args){ List list = new ArrayList(); list.add("jack"); list.add("tom"); list.add("mary"); list.add("jack"); //索引是从0开始的 System.out.println(list.get(3)); //jack }
常用方法
-
add 在索引位置插入元素
-
addAll 从索引位置开始将元素加入进去
-
get 通过索引获取值
-
indexOf 返回首次元素出现的位置
-
lastIndexOf 返回元素最后一次出现的位置
-
set 指定索引位置 进行替换
-
subList 返回starat 到 end索引位置的子集合
public static void main(String[]args){
List list = new ArrayList();
list.add("张三丰");
list.add("刘备");
// 1. add 在索引位置插入元素
//在索引1的位置插入一个字符串 如果不加默认是加入尾部的
list.add(1,"吕布");
System.out.println(list); //[张三丰, 吕布, 刘备]
// 2. addAll 从索引位置开始将元素加入进去
List list1 = new ArrayList();
list1.add("jkac");
list1.add("tom");
//在张三丰的后加入这个两个元素
list.addAll(1,list1);
System.out.println(list); //[张三丰, jkac, tom, 吕布, 刘备]
// 3. get 通过索引获取值
//获取第一个元素
Object o = list.get(0);
System.out.println(o); //张三丰
// 4. indexOf 返回首次元素出现的位置
int i = list.indexOf("吕布");
System.out.println("吕布对应集合中第一次出现的索引:"+i); //3
// 5. lastIndexOf 返回元素最后一次出现的位置
list.add("吕布");
int j = list.lastIndexOf("吕布");
System.out.println("吕布对应集合中最后一次出现的索引:"+j); //5
// 6. set 指定索引位置 进行替换
list.set(0,"张三丰二");
System.out.println(list); //[张三丰二, jkac, tom, 吕布, 刘备, 吕布]
// 7. subList 返回starat 到 end索引位置的子集合 [start,end) 左闭右开
List list2 = list.subList(0, 3);
System.out.println(list2); //[张三丰二, jkac, tom]
}
注意:这里面的索引都要合法才行,否则会出现报错
2、ArrayList实现类
-
ArrayList可以加入多个null。
-
ArrayList是底层是由数组实现存储数据的
-
ArrayList基本等同于Vector,除了ArrayList是线程不安全的,在多线程的情况下不推荐使用ArrayList
2.1、ArrayList的底层结构和源码分析
先说结论,在看源码
public class ArrayListSource {
public static void main(String[]args){
//使用无参构造器创建ArrayList对象
// ArrayList list = new ArrayList();
ArrayList list = new ArrayList(8);
//使用for循环给List集合加载1到10 10个数据
for (int i = 1; i <= 10; i++) {
list.add(i);
}
//又加到15个数据
for(int i = 11; i <= 15; i++) {
list.add(i);
}
list.add(100);
list.add(200);
list.add(null);
}
}
1、ArrayList中维护了一个Object类型的数组elementData
transient Object[] elementData // transient 表示瞬间、短暂的、表示该属性不会被序列化
2、当创建ArrayList对象时,如果使用的无参构造器,则初始elementData容量为0.第一次添加的时候,则扩容为10,如果需要再次扩容,则扩容elementData的1.5倍
-
代表使用无参构造器,会初始elementData容量为0
-
执行list.add
-
先确定是否要扩容
-
-
calculateCapacity 该方法确定第一次扩容为10

-
modCount++ 记录集合被修改的次数(防止多线程 --出现抛出异常)
-
if (minCapacity - elementData.length > 0) grow(minCapacity); 如果elementData的长度不够了,就调用grow去扩容

-
grow
-
真的扩容
-
第一次newCapacity = 10
-
第二次及以后就是按照1.5倍扩容
-
扩容使用的是 Arrays.copyOf() --->会保留原先的数据
-

- 将传入值放入数组中
当容量不够的情况
-
添加第11个元素的时候
-
扩容1.5倍 将之前的10个元素加入到新扩容为15的数组中,然后赋值给elementData
debug显示更加全面的话(出现上面那个样子 可以这样设置)
3.如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData的1.5倍

3、Vector 实现类
-
Vector底层也是一个对象数组
protected Object[] elementData;
-
Vector是线程同步的,即线程安全,Vector类的操作方法带有synchronized
public synchronized boolean add(E e) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = e; return true; } -
在开发中,需要线程安全时,考虑使用Vector
-
如果无参 默认是10 每次扩容2倍

- 每次扩容2倍

5.如果是有参 按照指定大小 每次扩容2倍

4、LinkedList类
-
LinkedList底层实现了双向链表和双端队列的特点
-
可以添加任意元素(元素可以重复),包括null
-
线程不安全,没有实现同步
4.1 LinkedList的底层结构
-
LinkedList底层维护了一个双向链表
-
LinkedList中维护了两个属性first和last 分别指向首节点和尾节点
-
每个节点(Node对象 ),里面又维护了prev、next、item三个属性,其中prev指向前一个节点,next指向后一个节点。最终实现双向链表
-
所以LinkedList的元素 添加和删除,不是通过数组完成的,相对来说效率比较高
4.2 源码分析
package com.sofwin.controller;
import java.util.LinkedList;
/**
* @packageName: com.sofwin.controller
* @author: wentao
* @date: 2022/10/31 16:18
* @version: 1.0
* @description: LinkedList的源码
*/
@SuppressWarnings("all")
public class LinkedListSource {
public static void main(String[]args){
/**
* 源码解读
* 1、LinkedList linkedList = new LinkedList();
* public LinkedList() {
* }
* 这是普通的类的初始化工作
* 2、这时 linkedList的属性 frist = null last = null size = 0
*
* 3.add方法
* public boolean add(E e) {
* linkLast(e);
* return true;
* }
* 4. 将新的结点加入到双向链表的最后(尾插法)
* private void linkFirst(E e) {
* final Node<E> f = first;
* final Node<E> newNode = new Node<>(null, e, f);
* first = newNode;
* if (f == null)
* last = newNode;
* else
* f.prev = newNode;
* size++;
* modCount++;
* }
*
*/
LinkedList linkedList = new LinkedList();
for (int i = 1; i<=2; i++) {
linkedList.add(i);
}
/**
* 1、删除 第一个元素
* public E remove() {
* return removeFirst();
* }
* 2、 关键代码是 unlinkFirst(f);
* public E removeFirst() {
* final Node<E> f = first;
* if (f == null)
* throw new NoSuchElementException();
* return unlinkFirst(f);
* }
* 3、删除第一个元素 然后返回删除元素的item值
*
* 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;
* }
*/
linkedList.remove();
}
}
-
无参构造方法 LinkedList linkedList = new LinkedList();
实际就是初始化工作
- add 方法
第一次添加的first last 都指向这个新结点

第二次添加的时候 first指向第一个结点 last就指向最后一个结点了
- 删除 remove()

这里只分析remove()其他都大体相同
显示删除的第一个

返回删除的元素

List的选择的原则:
-
如果改查的操作多,选择ArrayList
-
如果增删的操作多,选择LinkedList
-
一般来说,在程序中,80%-90%都是查询,因此大部分情况下会选择ArrayList
-
在一个项目中,要根据业务灵活选择。
更多推荐
所有评论(0)