文章目录

1.Collection集合

1.1数组和集合的区别【理解】

  • 相同点
    都是容器,可以存储多个数据
  • 不同点
  • 数组的长度是不可变的,集合的长度是可变的
  • 数组可以存基本数据类型和引用数据类型
    集合只能存引用数据类型,如果要存基本数据类型,需要存对应的包装类
public static void main(String[] args) {
int[] arr1={1,2,3};
String[] arr2={"a","b","c"};
System.out.println(Arrays.toString(arr1));
System.out.println(Arrays.toString(arr2));

System.out.println("----------------------");

ArrayList<String> list1 = new ArrayList<>();
list1.add("a");list1.add("b");list1.add("c");
System.out.println(list1);

//ArrayList<int> list2 = new ArrayList<>();//报错 只能存储引用数据类型 不能存储基本数据类型
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(1);list2.add(2);list2.add(3);
System.out.println(list2);
}

08-集合1-集合类体系结构、Collection、List、泛型_数据结构

1.2集合类体系结构【理解】

08-集合1-集合类体系结构、Collection、List、泛型_数组_02


蓝色外框都是接口、红色外框都是具体实现类

1.3Collection 集合概述和使用【应用】

  • Collection集合概述
  • 是单列集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素
  • JDK 不提供此接口的任何直接实现.它提供更具体的子接口(如Set和List)实现
  • 创建Collection集合的对象
  • 多态的方式
  • 具体的实现类ArrayList

Collection集合常用方法(所有单列集合:list和set都 继承了的(共有)的方法)

方法名

说明

boolean add(E e)

添加元素

boolean remove(Object o)

从集合中移除指定的元素(第一次出现的那一个 且 成功返回true 失败返回false)

boolean removeIf(Object o)

根据条件进行移除

void clear()

清空集合中的元素

boolean contains(Object o)

判断集合中是否存在指定的元素

boolean isEmpty()

判断集合是否为空

int size()

集合的长度,也就是集合中元素的个数

代码1:add&remove

public static void main(String[] args) {
Collection<String> c =new ArrayList<>();//只能new实现类 不能new接口
// boolean add(E e) | 添加元素
c.add("aaa");c.add("bbb");c.add("ccc");
System.out.println(c);//[aaa, bbb, ccc]
//boolean remove(Object o) | 从集合中移除指定的元素(成功返回true 失败返回false)
print("remove");
System.out.println(c.remove("aaa"));//true
System.out.println(c.remove("ddd"));//false
System.out.println(c);//[bbb, ccc]
}

public static void print(String s){
System.out.println("-----------"+s+"-----------");
}

代码2:removeIf

public static void main(String[] args) {
Collection<String> c =new ArrayList<>();//只能new实现类 不能new接口
c.add("aaa");c.add("bbb");c.add("ccc");c.add("dddd");

//boolean removeIf(Object o) | 根据条件进行删除
/*removeIf底层会遍历集合,得到集合中的每一个元素
s依次表示集合中的每一个元素
就会把这每一个元素都到lambda表达式中去判断一下
如果返回的是true,则删除
如果返回的是false,则保留不删除.*/
c.removeIf( (String s)->{return s.length()==3;}/*删除长度为3的*/ );//传入lambda表达式 条件删除
System.out.println(c);//[dddd]
}

代码3:清空集合中的元素

public static void main(String[] args) {
Collection<String> c =new ArrayList<>();//只能new实现类 不能new接口
c.add("aaa");c.add("bbb");c.add("ccc");c.add("dddd");
// void clear() | 清空集合中的元素
System.out.println(c);//[aaa, bbb, ccc, dddd]
c.clear();
System.out.println(c);//[]
}

代码4:判断集合中是否存在指定元素

public static void main(String[] args) {
Collection<String> c =new ArrayList<>();//只能new实现类 不能new接口
c.add("aaa");c.add("bbb");c.add("ccc");c.add("dddd");

//boolean contains(Object o) | 判断集合中是否存在指定的元素
System.out.println(c.contains("aaa"));//true
System.out.println(c.contains("cccc"));//false
}

代码5:集合判空、集合长度

public static void main(String[] args) {
Collection<String> c =new ArrayList<>();//只能new实现类 不能new接口
c.add("aaa");c.add("bbb");c.add("ccc");c.add("dddd");
// boolean isEmpty() | 判断集合是否为空
System.out.println(c.isEmpty());//false
// int size() | 集合的长度,也就是集合中元素的个数
System.out.println(c.size());//4
}

1.4Collection集合的遍历【应用】

  • 迭代器介绍
  • 迭代器,集合的专用遍历方式
  • Iterator iterator(): 返回此集合中元素的迭代器,通过集合对象的iterator()方法得到
  • Iterator中的常用方法
    ​ boolean hasNext(): 判断当前位置是否有元素可以被取出
    ​ E next(): 获取当前位置的元素,将迭代器对象移向下一个索引位置
  • Collection集合的遍历
public static void main(String[] args) {
Collection<String> list=new ArrayList<>();
list.add("a");list.add("b");list.add("c");list.add("d");

//1.获取迭代器对象
//迭代器对象一旦被创建出来,默认指向集合的0索引处
Iterator<String> it = list.iterator();
while (it.hasNext()){//当前位置是否有元素可以被取出
System.out.println(it.next());//取出当前位置的元素 + 将迭代器往后移动一个索引的位置
}
}

08-集合1-集合类体系结构、Collection、List、泛型_collection_03


08-集合1-集合类体系结构、Collection、List、泛型_数组_04

  • 迭代器中删除的方法
public static void main(String[] args) {
ArrayList<String> list=new ArrayList<>();
list.add("a");list.add("b");list.add("b");list.add("c");list.add("d");

Iterator<String> it = list.iterator();
while (it.hasNext()){
String s = it.next();
if("b".equals(s)){
it.remove();
//迭代器不会因为删除而导致指针混乱(应该自动帮你实现了it--啥的 或者it一起左移了)
}
}
System.out.println(list);//[a, c, d]
}

普通根据下标删除的弊端

public static void main(String[] args) {
ArrayList<String> list=new ArrayList<>();
list.add("a");list.add("b");list.add("b");list.add("c");list.add("d");

for (int i = 0; i < list.size(); i++) {
if("b".equals(list.get(i))){//ArrayList 才有get方法 Collection没有
list.remove(i);
i--;//必须i-- 否则bug list.remove(i);会自动将后面元素整体后移动(标准顺序表)然后表长-1
//System.out.println(list.size());
}
}
System.out.println(list);//[a, c, d]
//remove后不加 i--; 输出[a, b, c, d] 原因,每次remove后集合元素会整体前移,然后i++,正好错过了相邻的b

list.add("e");list.add("e");
System.out.println(list);
while (list.contains("e")) //结合api删除 多快 不过可能时间效率太慢了 不提倡
list.remove("e");//一次remove只能删除一个
System.out.println(list);
}

1.5增强for循环【应用】

  • 介绍
  • 它是JDK5之后出现的,其内部原理是一个Iterator迭代器
  • 实现Iterable接口的类才可以使用迭代器和增强for
  • 简化数组和Collection集合的遍历 (Map接口没有继承Iterable接口 不能直接用
  • 格式
    ​ for(集合/数组中元素的数据类型 变量名 : 集合/数组名) {
    ​ // 已经将当前遍历到的元素封装到变量中了,直接使用变量即可
    }
  • 代码
public static void main(String[] args) {
ArrayList<String> list=new ArrayList<>();
list.add("a");list.add("b");list.add("b");list.add("c");list.add("d");

//只能是数组或者Collection(单列集合) Map不能用(需要自己实现Iterable接口后才可以用)
for (String s : list) {
System.out.print(s+" ");//a b b c d
}
}

注意:增强for无法修改集合

public static void main(String[] args) {
ArrayList<String> list=new ArrayList<>();
list.add("a");list.add("b");list.add("b");list.add("c");list.add("d");

//只能是数组或者Collection(单列集合) Map不能用(需要自己实现Iterable接口后才可以用)
for (String s : list) {
s="q";//s只是list元素(新地址下)的一个副本 不要妄想通过增强for修改集合
}
System.out.println(list);//[a, b, b, c, d]
}

三种循环使用场景

08-集合1-集合类体系结构、Collection、List、泛型_数组_05

2.List集合

2.1List集合的概述和特点【记忆】

  • List集合的概述
  • 有序集合,这里的有序指的是存取顺序
  • 用户可以精确控制列表中每个元素的插入位置,用户可以通过整数索引访问元素,并搜索列表中的元素
  • 与Set集合不同,列表通常允许重复的元素
  • List集合的特点
  • 存取有序
  • 可以重复
  • 有索引

2.2List集合的特有方法【应用】

List接口就有的

方法名

描述

void add(int index,E element)

在此集合中的指定位置插入指定的元素

E remove(int index)

删除指定索引处的元素,返回被删除的元素

E set(int index,E element)

修改指定索引处的元素,返回被修改的元素

E get(int index)

返回指定索引处的元素

  • 代码1:list基本使用
public static void main(String[] args) {
List<String> list = new ArrayList<>();//类型采用List接口类型
list.add("aaa");list.add("bbb");list.add("bbb");

Iterator<String> it = list.iterator();
while (it.hasNext()){
String s = it.next();
System.out.println(s);
}
System.out.println("---------------");
for (String s : list) {
System.out.println(s);
}
}

08-集合1-集合类体系结构、Collection、List、泛型_迭代器_06

  • 代码2:List.add(index,element) 指定位置插入
public static void main(String[] args) {
List<String> list = new ArrayList<>();//类型采用List接口类型
list.add("aaa");list.add("bbb");list.add("ccc");

// void add(int index,E element) 在此集合中的指定位置插入指定的元素
// 原来位置上的元素往后挪一个索引.
list.add(0,"qqq");
System.out.println(list);//[qqq, aaa, bbb, ccc]
}
  • 代码3:List.remove(0) 删除指定位置处的元素

08-集合1-集合类体系结构、Collection、List、泛型_数据结构_07

public static void main(String[] args) {
List<String> list = new ArrayList<>();//类型采用List接口类型
list.add("aaa");list.add("bbb");list.add("ccc");

// E remove(int index) 删除指定索引处的元素,返回被删除的元素
//在List集合中有两个删除的方法
//第一个 删除指定的元素,返回值表示当前元素是否删除成功
//第二个 删除指定索引的元素,返回值表示实际删除的元素
String s = list.remove(0);
System.out.println(s);//aaa
System.out.println(list);//[bbb, ccc]
/* boolean remove(Object o) //Collection就有,继承下来的
从此列表中移除第一次出现的指定元素(如果存在)(可选操作)。 */
}
  • 代码4.1:List.set(index,元素) 修改指定索引处的元素 (被替换的那个元素,在集合中就不存在了)
  • 代码4.2:List.get(int index) 返回指定索引处的元素
public static void main(String[] args) {
List<String> list = new ArrayList<>();//类型采用List接口类型
list.add("aaa");list.add("bbb");list.add("ccc");

// E set(int index,E element) 修改指定索引处的元素,返回被修改的元素
//被替换的那个元素,在集合中就不存在了.
String s = list.set(0, "qqq");
System.out.println(s);//aaa (返回被修改消失的元素)
System.out.println(list);//[qqq, bbb, ccc]

// E get(int index) 返回指定索引处的元素
String s1 = list.get(0);
System.out.println(s1);//qqq
}

3.数据结构

3.1数据结构之栈和队列【记忆】

  • 栈结构
    先进后出
  • 队列结构
    先进先出

3.2数据结构之数组和链表【记忆】

  • 数组结构
    查询快、增删慢
  • 队列结构
    查询慢、增删快

4.List集合的实现类

4.1List集合子类的特点【记忆】

  • ArrayList集合
    底层是数组结构实现,查询快、增删慢 (add、remove、get…算法完全就是顺序表的那些操作,需要知道
    [ArrayList.get(i) 数组当然可以随机访问(查看源码也确实有个数组)]

ArrayList.java源码里面一句话: private static final int DEFAULT_CAPACITY = 10;
则:初试数组容量0,第一次申请数组容量10 (以后再满了应该是1.5倍地增长 还带有原来数组元素的复制)

08-集合1-集合类体系结构、Collection、List、泛型_集合_08

  • LinkedList集合
    底层是链表结构实现,查询慢、增删快(add、remove、get…算法完全就是链表的那些操作,需要知道

LinkedList也有get(i)方法,只不过效率很低罢了 [List接口继承下来的嘛]

public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("aaa");list.add("bbb");list.add("ccc");

for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));//链表的get(i)方法,效率很低
}

System.out.println("----------------------------");
Iterator<String> it = list.iterator();
while (it.hasNext()){
String s = it.next();
System.out.println(s);
}

System.out.println("----------------------------");
for (String s : list) {
System.out.println(s);
}
}

08-集合1-集合类体系结构、Collection、List、泛型_collection_09

4.2LinkedList集合的特有功能【应用】

  • 特有方法

方法名

说明

public void addFirst(E e)

在该列表开头插入指定的元素

public void addLast(E e)

将指定的元素追加到此列表的末尾

public E getFirst()

返回此列表中的第一个元素

public E getLast()

返回此列表中的最后一个元素

public E removeFirst()

从此列表中删除并返回第一个元素

public E removeLast()

从此列表中删除并返回最后一个元素

public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("aaa");list.add("bbb");list.add("ccc");

// public void addFirst(E e) | 在该列表开头插入指定的元素 |
list.addFirst("qqq");
System.out.println(list);//[qqq, aaa, bbb, ccc]
// public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
list.addLast("www");
System.out.println(list);//[qqq, aaa, bbb, ccc, www]

// public E removeFirst() | 从此列表中删除并返回第一个元素 |
String s = list.removeFirst();
System.out.println(s);//qqq (返回被删元素(也就是第一个元素))
System.out.println(list);//[aaa, bbb, ccc, www]
// public E removeLast() | 从此列表中删除并返回最后一个元素 |
String s1 = list.removeLast();
System.out.println(s1);//www
System.out.println(list);//[aaa, bbb, ccc]

// public E getFirst() | 返回此列表中的第一个元素 |
String first = list.getFirst();
System.out.println(first);//aaa
// public E getLast() | 返回此列表中的最后一个元素 |
String last = list.getLast();
System.out.println(last);//ccc
}
  • 源码分析

查LinkedList.java源码 发现里面有个内部类 (是双向链表的结构啊)

private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;

Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}

还维护了首尾指针

transient Node<E> first;

/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;

get(i)方法查找,i<size/2从头查 i>=size/2 从后查 时间复杂度O(size/2)
因为是双向链表,且维护了首尾指针,因此可以做这种优化

public E get(int index) {
checkElementIndex(index);
return node(index).item;
}

Node<E> node(int index) {
// assert isElementIndex(index);

if (index < (size >> 1)) {//i<size/2
Node<E> x = first;
for (int i = 0; i < index; i++)//从前往后查找
x = x.next;
return x;//此时下标i==index
} else { //i>=size/2
Node<E> x = last;
for (int i = size - 1; i > index; i--)//从后往前查找
x = x.prev;
return x;//此时下标i==index
}
}

add方法

08-集合1-集合类体系结构、Collection、List、泛型_collection_10

5.泛型

5.1泛型概述【理解】

  • 泛型的介绍
    泛型是JDK5中引入的特性,它提供了编译时类型安全检测机制
  • 泛型的好处
  1. 把运行时期的问题提前到了编译期间
  2. 避免了强制类型转换
public static void main(String[] args) {
ArrayList list= new ArrayList();
list.add("aaa");list.add("bbb");list.add("ccc");
list.add(123);//123也是Object 此时下面就没办法强转类型了 所以泛型的作用不可缺少

Iterator it = list.iterator();
while (it.hasNext()){
Object next = it.next();//默认类型Object 则该类型的特有方法失去了
//next.length();该类型的特有方法失去了
String s=(String)next;//加上泛型就不存在需要强转的问题了
System.out.println(s.length());//只能强转后才能用
//System.out.print(next+" ");//aaa bbb ccc 只是遍历不影响
}
}

5.2泛型类【应用】

如果一个类的后面有 < E >,表示这个类是一个泛型类 【E可以是任意字母,eg:Q,T,M…】
创建泛型类的对象时,必须要给这个泛型确定具体的数据类型

  • 泛型的定义格式
  • <类型>: 指定一种类型的格式.尖括号里面可以任意书写,一般只写一个字母.例如: < E > < T>
  • <类型1,类型2…>: 指定多种类型的格式,多种类型之间用逗号隔开.例如: <E,T> <K,V>
  • 定义格式
修饰符 class 类名<类型> {  }

08-集合1-集合类体系结构、Collection、List、泛型_collection_11

  • 示例代码
  • 泛型类 Box.java
public class Box<E> {
private E element;

public E getElement() {
return element;
}

public void setElement(E element) {
this.element = element;
}
}
  • 测试类
public static void main(String[] args) {
Box<String> box=new Box<>();
box.setElement("abc");
String s = box.getElement();
System.out.println(s);//abc
//box.setElement(123);//直接编译报错 多好

Box<Integer> box1 = new Box<>();
box1.setElement(123);
Integer i = box1.getElement();
System.out.println(i);//123
}

5.3泛型方法【应用】

5.3.1 泛型方法使用

08-集合1-集合类体系结构、Collection、List、泛型_数组_12

调用格式

方法说明

备注

Object[] toArray()

按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组

直接返回Object[] 使用不方便

T[] toArray(T[] a)

按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组;返回传入的参数指定的数组类型。

通过泛型,可以指定返回类型。 将list内容传到T[] a数组中,并返回该数组

public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aaa");list.add("bbb");list.add("ccc");

Object[] objects = list.toArray();//list转数组
System.out.println(Arrays.toString(objects));//[aaa, bbb, ccc]
//list.toArray()方法的弊端,返回值Object[] 需要强转,非常不便,存在问题

//参数给他一个数组,会将list内容放到参数数组中,同时返回该数组
String[] strings = list.toArray(new String[list.size()]);//类型正确了 ★
System.out.println(Arrays.toString(strings));//[aaa, bbb, ccc]
}

5.3.2 自定义泛型方法

  • 定义格式
修饰符 <类型> 返回值类型 方法名(类型 变量名) {  }

< T>表示这是在定义一个泛型T,告诉编译器本没有T这个类型的
泛型只有在被调用时才有具体类型

08-集合1-集合类体系结构、Collection、List、泛型_集合_13

  • 示例代码
public static void main(String[] args) {
//好方便实用的一个方法啊 (不用一个一个慢慢add了 还通用)
ArrayList<String> list = addElement(new ArrayList<String>(), "a", "b", "c", "d");
System.out.println(list);//[a, b, c, d]

ArrayList<Integer> list1 = addElement(new ArrayList<Integer>(), 1, 2, 3, 4);
System.out.println(list1);//[1, 2, 3, 4]
}

//注意定义泛型 <T> 和方法返回值是两个不同的东西
public static <T> ArrayList<T> addElement(ArrayList<T> list,T t1,T t2,T t3,T t4){//后期有技术来实现不定长参数
list.add(t1);list.add(t2);list.add(t3);list.add(t4);
return list;
}

5.4泛型接口【应用】 (和泛型类差不多 接口看成类即可)

  • 定义格式
修饰符 interface 接口名<类型> {  }

注意自定义泛型接口 好多细节 哪里需要泛型定义< E> 哪里又不需要

08-集合1-集合类体系结构、Collection、List、泛型_数组_14

  • 示例代码
  • 泛型接口
public class GenericityInterface {
public static void main(String[] args) {
GenericityImpl<String> genericity = new GenericityImpl<>();
genericity.method("abc");//"abc"

//此时这个实现类只是一个普通类
GenericityImpl1 genericity1 = new GenericityImpl1();
genericity1.method(123);//"123"
}
}

interface Genericity<E>{
public abstract void method(E e);//接口定义处已经定义了泛型 此处的泛型方法定义时就不需要再定义泛型<E>了
}

//不指定接口泛型的具体类型 实现类还要有泛型定义<E>
class GenericityImpl<E> implements Genericity<E>{
@Override
public void method(E e) {
System.out.println(e);
}
}

//给出了具体类型后 实现类就不用有泛型定义<E>了
class GenericityImpl1 implements Genericity<Integer>{
@Override
public void method(Integer integer) {
System.out.println(integer);
}
}

5.5类型通配符

  • 类型通配符: <?>
  • ArrayList<?>: 表示元素类型未知的ArrayList,它的元素可以匹配任何的类型
  • 但是并不能把元素添加到ArrayList中了,获取出来的也是父类类型
  • 类型通配符上限: <? extends 类型>
  • ArrayListList <? extends Number>: 它表示的类型是Number或者其子类型
  • 类型通配符下限: <? super 类型>
  • ArrayListList <? super Number>: 它表示的类型是Number或者其父类型

08-集合1-集合类体系结构、Collection、List、泛型_数据结构_15

- 泛型通配符的使用

import java.util.ArrayList;

public class GenericityGlobbing1 {
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<>();
ArrayList<Number> list2 = new ArrayList<>();
ArrayList<Object> list3 = new ArrayList<>();

printList(list1);
printList(list2);

method1(list1);
method1(list2);
//method1(list3);//报错了 Number的父类类型不行了(extends修饰的)

//method2(list1);//报错了 Number的子类类型不行了(super修饰的)
method2(list2);
method2(list3);
}

// 泛型通配符: 此时的泛型?,可以是任意类型
private static void printList(ArrayList<?> list){ }//比<E>简单 因为返回值前还要写<E>

//extends 表示传递进来集合的类型,可以是Number类型,也可以是Number所有的子类类型
private static void method1(ArrayList<? extends Number> list){ }

//super 表示传递进来集合的类型,可以是Number类型,也可以是Number所有的父类类型
private static void method2(ArrayList<? super Number> list){ }
}