12 集合Collection(12-17)
12.1 集合体系
12.2 Collection集合概述和使用
-
Collection集合概述
- 是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素
- JDK 不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现
-
创建Collection集合的对象
- 多态的方式
- 具体的实现类ArrayList
12.3 Collection集合常用方法
方法名 | 说明 |
---|---|
boolean add(E e) | 添加元素 |
boolean remove(Object o) | 从集合中移除指定的元素 |
boolean removeif(Object o) | 根据条件进行删除 |
void clear() | 清空集合 |
boolean contains(Object o) | 判断集合中是否存在指定的元素 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中元素的个数 |
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
c.add("aaa"); // 添加元素
System.out.println(c.remove("aaa")); // 删除元素
c.removeIf( // 删除符合条件的元素
(String s)->{
return s.length() == 3;
}
);
System.out.println(c);
c.clear();
System.out.println(c);
System.out.println(c.contains("aaa"));
System.out.println(c.size());
}
12.4 Collection集合迭代器
-
Iterator
:迭代器,集合的专用遍历方式Iterator\<E> iterator()
:返回集合中的迭代器对象,该迭代器对象默认指向当前集合的0索引。
-
Iterator
中的常用方法boolean hasNext()
:判断当前位置是否有元素可以被取出\E next()
:获取当前位置的元素,并将迭代器对象移向下一个索引位置void remove()
:删除迭代器的当前元素
public static void main(String[] args) {
Collection<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
// 1、通过list.iterator(),获得迭代器的对象
// 迭代器对象一旦被创建出来,默认指向集合的0索引处
Iterator<String> it = list.iterator();
// 2、利用迭代器里面的方法进行遍历
// System.out.println(it.hasNext()); // 当前位置是否有元素可以被取出
// System.out.println(it.next()); //取出当前位置的元素 + 将迭代器往后移动一个索引的位置
// System.out.println(it.next());
// System.out.println(it.next());
// System.out.println(it.next());
// System.out.println(it.next());
// 获取每个元素
while(it.hasNext()){
System.out.println(it.next());
}
// 删除集合中某个元素
while(it.hasNext()){
String s = it.next();
if("b".equals(s)){
//指向谁,那么此时就删除谁.
it.remove();
}
}
System.out.println(list);
}
12.5 Collection集合增强for循环
- 增强for:
简化数组和Collection集合的遍历
- 它是JDK5之后出现的,其
内部原理是一个Iterator迭代器
实现Iterable接口的类
才可以使用迭代器和增强for。- 所以:
单列可以
(Collection继承了Iterable ),双列不可以
(Map没继承Iterable )
- 它是JDK5之后出现的,其
格式:
for(元素数据类型 变量名 : 数组或者Collection集合) {
//在此处使用变量即可,该变量就是元素
}
示例
ArrayList<String> list = new ArrayList<>();
//1,数据类型一定是集合或者数组中元素的类型
//2,str仅仅是一个变量名而已,在循环的过程中,依次表示集合或者数组中的每一个元素
//3,list就是要遍历的集合或者数组.
for(String s : list) {
// 在此处使用变量即可,该变量就是元素
System.out.println(i);
}
注意:关于增强for,修改第三方变量的值不会影响到集合中的元素
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
for(String str : list){
str = "q";
System.out.println(str); // 这里打印的确实是q
}
System.out.println(list); // 这里打印的是[a,b]
}
12.6 普通for、迭代器、增强for,三种循环的使用场景
- 如果需要
操作索引
,使用普通for
循环 - 如果在遍历的过程中需要
删除元素
,请使用迭代器
- 如果
仅仅想遍历
,那么使用增强for
13 集合Collection下的List
13.1 概述
- List集合特点
- 有序:存储和取出的元素顺序一致
- 有索引:可以通过索引操作元素
- 可重复:存储的元素可以重复
13.2 List特有方法
方法名 | 说明 |
---|---|
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
13.3 数据结构
- 栈:先进后出
- 队列:先进先出
- 数组:查询快,增删慢的模型
- 链表:增删快,查询慢的模型(对比数组)
- 链表结构示意图
13.4 List集合实现类:ArrayList和LinkedList
- List集合常用子类:ArrayList,LinkedList
- ArrayList:底层数据结构是数组,查询快,增删慢
- LinkedList:底层数据结构是链表,查询慢,增删快
ArrayList方法
跟LIst特有方法一样
ArrayList 方法名 |
说明 |
---|---|
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
LinkedList特有方法
LinkedList 方法名 |
说明 |
---|---|
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
14 集合Collection下的Set
14.1 Set集合特点
- 可以
去除重复
存取顺序不一致
没有带索引的方法
,所以不能使用普通for
循环遍历,也不能通过索引来获取,删除Set集合里面的元素
14.2 Set集合实现类:TreeSet
1 TreeSet集合特点
- 不包含重复元素的集合
- 没有带索引的方法
- 可以将元素
按照规则进行排序
。有自然排序
和比较器排序
1 自然排序Comparable的使用
- 使用
空参构造
创建TreeSet集合 - 自定义的Student类实现
Comparable
接口 - 重写里面的
compareTo
方法
2 自然排序简单原理图
- 如果返回值为
负数
,表示当前存入的元素是较小值,存左边
- 如果返回值为
0
,表示当前存入的元素跟集合中元素重复了,不存
- 如果返回值为
正数
,表示当前存入的元素是较大值,存右边
3 代码案例
// 主方法
public static void main(String[] args) {
// 使用`空参构造`创建TreeSet集合
TreeSet<Student> ts = new TreeSet<>();
Student s1 = new Student("zhangsan",28);
Student s2 = new Student("lisi",27);
Student s3 = new Student("wangwu",29);
Student s4 = new Student("zhaoliu",28);
Student s5 = new Student("qianqi",30);
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
System.out.println(ts);
}
// Student类
// 实现Comparable接口
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
// 重写里面的compareTo`方法
public int compareTo(Student o) {
//按照对象的年龄进行排序
//主要判断条件
// this.age代表当前存入的数据,o.age代表TreeSet已有数据
int result = this.age - o.age;
//次要判断条件
result = result == 0 ? this.name.compareTo(o.getName()) : result;
return result;
}
}
4 比较器排序Comparator的使用
- TreeSet的
带参构造
方法使用的是比较器排序
对元素进行排序的 - 比较器排序,就是
让集合构造方法接收Comparator的实现类对象
,重写compare(T o1,T o2)方法 重写方法
时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
5 代码案例
// 主方法
public static void main(String[] args) {
// 使用`带参构造`创建TreeSet集合
// `让集合构造方法接收Comparator的实现类对象`
// 可以写成匿名内部类的形式,也可以用Lambda表达式写
TreeSet<Teacher> ts = new TreeSet<>(new Comparator<Teacher>() {
@Override
public int compare(Teacher o1, Teacher o2) {
//o1表示现在要存入的那个元素
//o2表示已经存入到集合中的元素
//主要条件
int result = o1.getAge() - o2.getAge();
//次要条件
result = result == 0 ? o1.getName().compareTo(o2.getName()) : result;
return result;
}
});
Teacher t1 = new Teacher("zhangsan",23);
Teacher t2 = new Teacher("lisi",22);
Teacher t3 = new Teacher("wangwu",24);
Teacher t4 = new Teacher("zhaoliu",24);
ts.add(t1);
ts.add(t2);
ts.add(t3);
ts.add(t4);
System.out.println(ts);
}
// Teacher类
public class Teacher {
private String name;
private int age;
public Teacher() {
}
public Teacher(String name, int age) {
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;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
6 两种比较方式小结
- 自然排序:自定义类实现Comparable接口,重写compareTo方法,根据返回值进行排序。
- 比较器排序:创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序。
- 在使用的时候,
默认使用自然排序
,当自然排序不满足现在的需求
时,使用比较器排序
。
两种方式中,关于返回值的规则一致:
+ 如果返回值为负数,表示当前存入的元素是较小值,存左边
+ 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
+ 如果返回值为正数,表示当前存入的元素是较大值,存右边
第三条的释义:
当String
,Integer
等基础类默认实现的Comparable接口
不满足需求时,没法直接更改源码,此时就需要使用比较器排序
。
案例说明
需求:请自行选择比较器排序和自然排序两种方式;
要求:存入四个字符串, “c”, “ab”, “df”, “qwer”
按照长度排序,如果一样长则按照首字母排序
public static void main(String[] args) {
// TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
// @Override
// public int compare(String o1, String o2) {
// int result = o1.length() - o2.length();
// result = result == 0 ? o1.compareTo(o2) : result;
// return result;
// }
// });
// Lambda表达式写法
// 当String,Integer基础类默认实现了`Comparable`接口不满足需求时,没法直接更改源码,此时就需要使用`比较器排序`。
TreeSet<String> ts = new TreeSet<>(
(String o1, String o2) -> {
int result = o1.length() - o2.length();
result = result == 0 ? o1.compareTo(o2) : result;
return result;
}
);
ts.add("c");
ts.add("ab");
ts.add("df");
ts.add("qwer");
System.out.println(ts);
}
2 TreeSet底层数据结构
TreeSet底层数据结构是红黑树
1 二叉树的基本概念
什么叫二叉树?
二叉树中,任意一个节点的度要小于等于2。
什么叫度?
度是指每一个节点的子节点数量
2 二叉查找树
二叉查找树,又称二叉排序树或者二叉搜索树。
3 平衡二叉树
特点
- 二叉树左右两个子树的
高度差不超过1
任意节点
的左右两个子树都是一颗平衡二叉树
平衡二叉树-旋转
- 利用左旋和右旋机制保证树的平衡
- 左旋
- 右旋
旋转触发时机
- 当添加一个节点之后,该树不再是一颗平衡二叉树
左旋
- 就是将根节点的右侧往左拉,原先的右子节点变成新的父节点,并把多余的左子节点出让,给已经降级的根节点当右子节点
- 逆时针旋转
- 右子节点变成父节点(根节点)
- 原先的根节点降级变成左子节点
- 将多余的左子节点出让,给降级的节点作为右子节点
- 将根节点的左侧往右拉,左子节点变成了新的父节点,并把多余的右子节点出让,给已经降级根节点当左子节点
- 顺时针旋转
- 左子节点变成父节点(根节点)
- 原先的根节点降级变成右子节点
- 将多余的右子节点出让,给降级的节点作为左子节点
4 红黑树
概念
红黑树是一种自平衡的二叉查找树
,是计算机科学中用到的一种数据结构。
特点
红黑树不是高度平衡
的,它的平衡是通过"红黑规则"
进行实现的,规则如下:
1.每一个节点或是红色的,或者是黑色的。
2.根节点必须是黑色
3.如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的;
4.不能出现两个红色节点相连的情况
5.对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点;
14.3 Set集合实现类:HashSet
1 HashSet集合特点
- 底层数据结构是
哈希表
- 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
- 没有带索引的方法,所以不能使用普通for循环遍历
- 由于是Set集合,所以元素唯一
哈希值
哈希值:是JDK根据对象的地址
或者属性
算出来的int类型
的数值
Object类中有一个方法可以获取对象的哈希值
public int hashCode()
:返回对象的哈希码值
对象的哈希值特点
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的
- 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
2 HashSet底层数据结构
HashSet底层数据结构是哈希表
哈希表
JDK8之前,底层采用数组+链表实现,可以说是一个元素为链表的数组
JDK8以后,在长度比较长的时候,底层实现了优化
注意:
利用HashSet存储自定义
元素,必须重写hashCode和equals方法
15 集合Map
15.1 概述
- Map集合特点
- Interface Map<K,V> K:键的数据类型;V:值的数据类型
- 键不能重复,值可以重复
- 键和值是一一对应的,每一个键只能找到自己对应的值
- (键 + 值) 这个整体 我们称之为“键值对”或者“键值对对象”,在Java中叫做“Entry对象”。
Map集合有点像对象嵌套对象。例如A对象嵌套B对象,C对象,此时的BC对象也叫Entry对象
15.2 Map特有方法
方法名 | 说明 |
---|---|
V put(K key,V value) | 添加元素 |
V remove(Object key) | 根据键删除键值对元素 |
void clear() | 移除所有的键值对元素 |
boolean containsKey(Object key) | 判断集合是否包含指定的键 |
boolean containsValue(Object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中键值对的个数 |
V get(Object key) | 根据键获取值 |
Set keySet() | 获取所有键的集合 |
Set<Map.Entry<K,V>> entrySet() | 获取所有键值对对象的集合 |
K getKey() | 获得键 |
V getValue() | 获得值 |
16 可变参数和不可变参数
16.1 可变参数
可变参数:就是形参的个数是可以变化的
格式:修饰符 返回值类型 方法名(数据类型… 变量名) { }
范例:public static int sum(int… a) { }
可变参数注意事项
这里的变量其实是一个数组
如果一个方法有多个参数,包含可变参数,可变参数要放在最后
16.2 不可变参数
方法名 | 说明 |
---|---|
static List of(E…elements) | 创建一个具有指定元素的List集合对象 |
static Set of(E…elements) | 创建一个具有指定元素的Set集合对象 |
static <K , V> Map<K,V> of(E…elements) | 创建一个具有指定元素的Map集合对象 |
在List、Set、Map接口中,
都存在of方法
,可以创建一个不可变的集合。
这个集合不能添加,不能删除,不能修改。
但是可以结合集合的带参构造,实现集合的批量添加
。
例如:ArrayList<String> list1 = new ArrayList<>(List.of("张三丰","张无忌","张翠山","王二麻子","张良","谢广坤"));
在
Map接口
中,还有一个ofEntries
方法可以提高代码的阅读性。
首先会把键值对封装成一个Entry对象,再把这个Entry对象添加到集合当中。
17 Stream流
有点像JS中的链式表达式,Promise.then().then()
注意
在Stream流中无法直接修改集合,数组等数据源中的数据。
17.1 获取Stream流方法
- 单列集合 :
集合对象.stream();
- 双列集合 : 不能直接获取,需要间接获取(keySet/entrySet)
集合对象.keySet().stream();
集合对象.entrySet().stream();
- 数组 :
Arrays.stream(数组名);
- 同种数据类型的多个数据:
Stream.of(数据1,数据2,数据3......);
public class MyStream2 {
public static void main(String[] args) {
//单列集合
//method1();
//双列集合
//method2();
//数组
//method3();
//同种数据类型的多个数据
//method4();
}
private static void method4() {
Stream.of(1,2,3,4,5,6,7,8).forEach(s-> System.out.println(s));
}
private static void method3() {
int [] arr = {1,2,3,4,5};
Arrays.stream(arr).forEach(s-> System.out.println(s));
}
private static void method2() {
HashMap<String,Integer> hm = new HashMap<>();
hm.put("zhangsan",23);
hm.put("lisi",24);
hm.put("wangwu",25);
//keySet
//hm.keySet().stream().forEach(s-> System.out.println(s));
//entrySet
hm.entrySet().stream().forEach(s-> System.out.println(s));
}
private static void method1() {
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
// Stream<String> stream = list.stream();
// stream.forEach(s-> System.out.println(s));
list.stream().forEach(s-> System.out.println(s));
}
}
17.2 Stream流中间方法
方法名 | 说明 |
---|---|
Stream filter(Predicate predicate) | 用于对流中的数据进行过滤 |
Stream limit(long maxSize): | 截取指定参数个数的数据 |
Stream skip(long n): | 跳过指定参数个数的数据 |
static Stream concat(Stream a, Stream b): | 合并a和b两个流为一个流 |
Stream distinct(): | 去除流中重复的元素。依赖(hashCode和equals方法) |
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("张三丰");
list.add("张无忌");
list.add("张翠山");
list.add("王二麻子");
list.add("张良");
list.add("谢广坤");
list.add("谢广坤");
list.stream().limit(2).forEach(s-> System.out.println(s));
list.stream().skip(2).forEach(s-> System.out.println(s));
ArrayList<String> list2 = new ArrayList<>();
list2.add("张三丰");
list2.add("张无忌");
list2.add("张翠山");
list2.add("王二麻子");
list2.add("张良");
list2.add("谢广坤");
Stream<String> stream1 = list.stream();
Stream<String> stream2 = list2.stream();
Stream.concat(list.stream(),list2.stream()).forEach(s-> System.out.println(s));
list.stream().distinct().forEach(s-> System.out.println(s));
}
17.3 Stream流终结方法
方法名 | 说明 |
---|---|
void forEach(Consumer action) | 对此流的每个元素执行操作 |
long count() | 返回此流中的元素数 |
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("张三丰");
list.add("张无忌");
list.add("张翠山");
list.add("王二麻子");
list.add("张良");
list.add("谢广坤");
list.stream().forEach(s->System.out.println(s));
long count = list.stream().count();
System.out.println(count);
}
17.4 Stream流收集方法
Stream流的收集方法
R collect(Collector collector)
工具类Collectors提供了具体的收集方式
public static <T> Collector toList()
:把元素收集到List集合中
public static <T> Collector toSet():
把元素收集到Set集合中
public static Collector toMap(Function keyMapper,Function valueMapper):
把元素收集到Map集合中
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
list1.add(i);
}
list1.add(10);
list1.add(10);
list1.add(10);
list1.add(10);
list1.add(10);
//filter负责过滤数据的.
//collect负责收集数据.
//获取流中剩余的数据,但是他不负责创建容器,也不负责把数据添加到容器中.
//Collectors.toList() : 在底层会创建一个List集合.并把所有的数据添加到List集合中.
List<Integer> list = list1.stream().filter(number -> number % 2 == 0)
.collect(Collectors.toList());
System.out.println(list);
Set<Integer> set = list1.stream().filter(number -> number % 2 == 0)
.collect(Collectors.toSet());
System.out.println(set);
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/84937.html