31. [Java]集合
31.1. 内部类
概念:在一个类中定义另一个类
两种内部类
成员内部类 书写的位置和成员变量、成员方法在同一个位置(方法外)
public class Person{ //成员内部类 public class InnerClass{ } }
局部内部类
局部内部类
书写在成员方法内
不能使用访问修饰符
31.1.1. 匿名内部类
匿名内部类:匿名对象+子类
匿名对象
Student stu = new Student();创建一个Student对象
访问方法:stu.method()
匿名对象格式:
书写格式:new Student()
访问内部成员方法 new Student().method()
作用:通常用于作实参传递
子类
//父类 abstract class Person{ abstract void eat(); } class Student exdents Person{ public void eat(){ } }
匿名内部类+子类 一个继承了类或者接口的子类对象
new Student
{
//重写方法
public void eat(){
}
}
使用
1.当抽象类或接口中没有过多方法使用也比较少,可以考虑使用匿名内部类的方式
2.匿名内部类可以作为方法的实参进行传递
31.2. 集合
概念:
集合是用来存储多个同类型的数据的容器,它的长度是可以变化的
集合的体系结构
Collection接口: |--- List接口: |---ArrayListL类: |---LinkedList类: |--- Set接口: |---Hash类: |---TreeSet类:
归属于Java.util
Collection是一个接口,属于java集合体系中顶层的父接口,该接口下有两大体系:List、Set
List特点:有序、可重复
Set特点:无序、唯一
使用方式
属于接口,无法实例化,需要借助子类,通过多态的方式创建子类对象
collection coll = new ArrayList();
常用方法
添加:public boolean add(Object obj);
删除 public boolean remove(Object obj);
修改 在遍历的过程中针对某个元素进行修改
查询 需要借助迭代器进行遍历查询
获取元素个数 public int size();
是否包含某个元素 public boolean contains(Object obj);
清空所有元素 public void clear();
判断是否为空 public boolean isEmpty();
集合内装的元素都经过了类型提升为Object(比如整形经过了自动装箱)
31.2.1. 泛型:约束的类型
常用于创建集合时使用,用来约束集合中可以存储的元素的类型
31.2.2. 集合的遍历
集合没有类似数组一样的索引,无法利用索引进行遍历,需要借助迭代器
Iterator it = Collection.iterator();
常用方法:
public boolean hasNext() //判断迭代器中是否还有下一个元素.
public E next() //获取迭代器中的下一个元素.
集合遍历的大致过程
1.创建集合对象.
2.创建元素对象.
3.把元素添加到集合中.
4.遍历集合
1.根据集合对象获取其对应的迭代器对象. 通过Collection#iterator()方法实现.
2.判断迭代器中是否有下一个元素. 通过Iterator#hasNext()方法实现.
3.如果有, 就获取该元素. 通过Iterator#next()方法实现.
案例:
//案例: 演示Collection集合存储自定义对象, 并遍历 public class Demo { public static void main(String[] args) { //1. 创建集合对象. Collection<Student> coll = new ArrayList<>(); //2. 创建元素对象. Student s1 = new Student("刘亦菲", 33); Student s2 = new Student("赵丽颖", 31); Student s3 = new Student("高圆圆", 35); //Student s4 = new Student("丹丹", 18); //3. 把元素对象添加到集合对象中. coll.add(s1); coll.add(s2); coll.add(s3); //coll.add(s4); //4. 遍历集合. //4.1 根据集合对象获取其对应的迭代器对象. Collection#iterator(); Iterator<Student> it = coll.iterator(); //4.2 判断迭代器中是否有下一个元素. Iterator#hasNext(); while (it.hasNext()) { //4.3 有就获取. Iterator#next(); Student stu = it.next(); System.out.println(stu); //不能写成如下的形式, 因为next()方法调用一次, 就会获取一个值. //下边这个代码就属于: 判断一次, 获取两个值. //System.out.println(it.next().getName() + "..." + it.next().getAge()); } }
31.2.3. List集合
特点:
1.存取元素有序。存储元素的顺序和取出元素的顺序一致
2.list集合允许存储重复元素
3.list集合中有索引,可以利用索引精确访问集合中的每一个元素
创建:
没有构造方法
List是一个接口,无法实例化,需要借助子类
List list = new ArrayList();
List继承于Collection,父接口的相关功能,list集合也可以使用
常用方法(特有方法):
添加 void add(int index, Object obj)
修改 Object set(int index, Object obj) //指定索引位置上的元素修改为Obj,并返回修改之前的元素
删除
boolean remove(Object obj) 删除指定对象
Object remove(int index) 删除指定位置元素并返回旧元素
查询
Object get(int index) 查询到元素并返回
int indexOf(Object obj)
遍历
iterator迭代器(见ListDemo4.java)
ListIterator迭代器(专有)
四种方式:
1.Iterator迭代器
不适合遍历的同时增删
2.ListIterator迭代器
3.for循环
迭代器
普通迭代器的弊端
在迭代遍历时,如果向list集合中添加新元素或删除元素,迭代器会引发异常:ConcurrentModificationException
解决办法:
使用专用迭代器:ListIterator (ListIterator listIt = list.ListIterator())
增强for循环的底层是迭代器
31.2.3.1. AarrayList类
底层使用可变数组
特点
存取有序,有索引
可以存储重复元素
查询和修改效率比较高
添加和删除元素效率比较低
31.2.3.2. LinkedList类
底层使用链表
在创建LinkedList时所指定的索引,可以指定位置,但是在查询的时候用不上
特点
存取有序
可以存储重复元素
可以存储null
有角标,但是底层是链表结构用不上
添加和删除元素效率高
查询较慢,修改元素值较慢
链表特性:有头有尾
特有方法(针对链表的头和尾设计的):
添加:
addFirst(Object obj)
addLast(Object obj)
删除
removeFirst()
removeLast()
获取
getFirst()
getLast()
31.3. 增强For循环
增强for循环是用来遍历的,针数组和集合的遍历
弊端
只能用来遍历
增强for的底层其实是通过迭代器(Iterator)实现的
格式:
int[] arr = {11,22,33,44,55};
for(元素类型 变量名 : array){
//直接使用变量名就可以获取到array中的每一个元素
}
//集合
List list = new ArrayList();
for(元素类型 元素名: list){
//打印
}
在循环过程中不能向集合中添加元素或者删除元素
31.4. 集合的遍历
1.普通迭代器
iterator it = 集合对象.iterator()
2.专用迭代器
只适用于List
ListIterator li = 集合对象.listIterator()
3.普通for循环
4.增强for循环
如果要对集合元素进行删除修改应该使用for循环和专用迭代器
31.5. 数据结构
32. 简单(常用)数据结构
栈
队列
数组(可变数组)
链表
32.1. 栈
特点:FILO,先进后出
32.2. 队列
特点:FIFO,先进先出
32.3. 可变数组
创建新数组,将原数组中元素选择性拷贝到新数组中
特点
有索引
查询效率高
32.4. 链表
内存中的存储节点,每个节点通过地址值链接在一起
特点
查找元素时,从链表的头部开始遍历查找
无索引
查询效率低
添加删除元素的效率高
单链表
双链表
32.5. Set集合
特点
存取无序,唯一
不能存储重复元素
是一个接口,无法实例化,需要借助子类
无索引,不能通过for循环遍历
子类:HashSet和TreeSet
HashSet底层使用哈希表结构
哈希表结构在存储元素时的过程
1.拿出存储的元素,结合哈希算法,计算出元素的存储位置
借助了HashCode()方法
2.把存储的元素存放到计算出来的位置上
判断在该位置上是否已经存在元素
没有:直接在该位置存储
有:比较两个元素是否相同(要存储的元素、已存在的元素)
相同:意味着元素一样,不用存储
不同:拉链法(拿计算出来的位置,再次结合哈希算法重新计算存储的位置)
TreeSet底层使用树结构
在使用HashSet集合存储自定义对象时,如果希望所存储的自定义对象属性的值不能重复时,需要对HashCode()和equals()方法进行重写
遍历
迭代器
增强for
toArray()
32.6. 可变参数
定义不同的参数的方法,通过重载来实现,但是当参数的个数越来越多时,重载就不好使了,就需要使用可变参数
格式:
修饰符 返回值类型 方法名(数值类型… 变量名)
要求
可变参数必须书写在方法参数声明的最后一个参数位置上
在方法参数声明时,只能存在一个可变参数(不考虑参数类型)
当方法中需要传递其他参数时,需要将这些参数书写在可变参数之前
32.7. Map集合
是一个接口,不能实例化,需要借助子类
特点
底层使用了两个单列集合
存储两个元素:一次存储一对元素(键值对)
存储的Key元素不能重复(底层使用使用Set集合来存储键)
|--HashMap类
|--TreeMap类
常用方法:
添加:
public V put(Object key,Object value)
一次性向集合中添加键值对这一对元素,并返回value
底层实现:现在Map集合中通过key来判断是否存在的key元素
有:就直接针对当前的key,来修改value(新的覆盖旧的),返回旧的value
没有,直接添加
如果Map是空集合,第一次使用put方法,返回的是null,因此可以通过判断返回的是不是null来判断是不是第一次天剑
修改:
public V put(Object key,Object value)
删除:
public V remove(Object key) //根据指定的键删除集合中相应的一对元素,返回被删除的value
查询:public V get(Object key) //根据指定的key元素,获取集合中匹配的value元素
判断:
boolean containsKey(Object key)
boolean containsValue(Object value)
boolean isEmpty() 判断是否为空
遍历
Map集合本身无法遍历,Map集合中没有迭代器
Map集合的遍历:
1.使用存储Key元素的Set集合,实现遍历
1.先获取到存储所有Key元素的Set集合
Map集合对象中的KeySet()方法
2.遍历Set集合(迭代器,增强for)
3.遍历过程中获取每一个key元素
4.利用Map集合中的get(Object key),实现通过key获取value
2.向Map集合中存储的一对键值对对象类型是:Map.Entry类型
1.利用Map集合中的方法,获取集合中所有的Map.Entry
2.遍历所有的Map.Entry
3.利用Map.Entry对象中的方法,分别获取:Key,Value
EntrySet()
获取所有的键值对对象集合(Set集合<Map.Entry>)
32.8. 小结
|---Collection集合(接口):单列集合的顶层父接口
|---List集合(接口):存取有序、有索引、元素可重复
|---ArrayList集合(类):底层使用数组,查询修改比较高快
|--- 常用方法:add remove set get
|---LinkList集合(类):底层使用链表,删除添加比较快
|---常用方法:addFirst addLast removeFirst removeLast getFirst getLast
|---Set集合(接口):存取无序,没有索引,元素唯一
|---常用方法:全部来自于Collection
|---HashSet集合(类):底层使用哈希表
|---TreeSet集合(类):底层使用树(二叉树)结构
|---LinkedHashSet(类):底层使用哈希表+链表结构。特点,有序存取
|---Map集合(接口):双列集合的顶层父接口
|---常用方法:put remove get
Set KeySet() 获取双列集合中用来存储所有的Key元素的单列集合
Set<Map.Entry> entrySet
|---HashMap集合(类):底层使用哈希表
|---TreeMap集合(类):底层使用树结构
当拿捏不定时,选择List集合