Collection集合,List集合,set集合,Map集合
文章目录
- 集合框架
- 认识集合
- 集合体系结构
- Collection的功能
- 常用功能
- 三种遍历方式
- 三种遍历方式的区别
- List集合
- List的特点、特有功能
- ArrayList底层原理
- LinkedList底层原理
- LinkedList的应用场
- list:电影信息管理模块案例
- Set集合
- set集合使用
- 哈希值
- 红黑树
- HashSet底层原理
- HashSet集合元素的对象去重操作
- LinkedHashSet集合的底层原理
- TreeSet集合底层原理
- TreeSet集合排序规则
- TreeSet集合自定义排序规则
- Collection小节
- Map集合
- Map集合的常用方法
- 三种遍历方式
- 键找值
- 键值对
- Lambda
- 案例练习 统计投票信息
- HashMap集合的底层原理
- LinkedHashMap集合的原理
- TreeMap集合的原理
集合框架
认识集合
集合是一种容器,用来装数据的,类似于数组,但集合的大小可变,开发中也非常常用。
集合体系结构
常用集合有哪些?各自有啥特点?
- Collection(接口):单列集合:每个元素(数据)只包含一个值。
- List(接口):添加的元素是有序、可重复、有索引。
- ArrayList:有序、可重复、有索引。
- LinkedList:有序、可重复、有索引。
- Set(接口):无序、不重复、无索引
- HashSet:无序、不重复、无索引
- LinkedHashSet:有序、不重复、无索引
- TreeSet:按照大小默认升序排序、不重复、无索引
- HashSet:无序、不重复、无索引
- List(接口):添加的元素是有序、可重复、有索引。
- Map(接口):双列集合:每个元素包含两个值(键值对)。
- HashMap:
- LinkedHashMap
- TreeMap
- HashMap:
Collection的功能
常用功能
为啥要先学Collection的常用方法?
Collection是单列集合的祖宗,它规定的方法(功能)是全部单列集合都会继承的。
方法名 | 说明 |
---|---|
public boolean add(E e) | 把给定的对象添加到当前集合中 |
public void clear() | 清空集合中所有的元素 |
public boolean remove(E e) | 把给定的对象在当前集合中删除 |
public boolean contains(Object obj) | 判断当前集合中是否包含给定的对象 |
public boolean isEmpty() | 判断当前集合是否为空 |
public int size() | 返回集合中元素的个数。 |
public Object[] toArray() | 把集合中的元素,存储到数组中 |
三种遍历方式
方式1
迭代器是用来遍历集合的专用方式(数组没有迭代器),在Java中迭代器的代表是Iterator。
Collection集合获取迭代器的方法
通过迭代器获取集合的元素,如果取元素越界会出现什么异常?
会出现NoSuchElementException异常。
方法名称 | 说明 |
---|---|
Iterator<E> iterator() | 返回集合中的迭代器对象,该迭代器对象默认指向当前集合的第一个元素 |
Iterator迭代器中的常用方法
方法名称 | 说明 |
---|---|
boolean hasNext() | 询问当前位置是否有元素存在,存在返回true ,不存在返回false |
E next() | 获取当前位置的元素,并同时将迭代器对象指向下一个元素处。 |
Iterator<String> it = names.iterator();while (it.hasNext()) {String name = it.next();System.out.println(name);}
方式2增强for循环
// for (元素的数据类型 变量名 : 数组或者集合) { }
Collection<String> c = new ArrayList<>();
for(String s : c) { System.out.println(s);}
增强for可以用来遍历集合或者数组。
增强for遍历集合,本质就是迭代器遍历集合的简化写法。
方式3Lambda表达式
得益于JDK 8开始的新技术Lambda表达式,提供了一种更简单、更直接的方式来遍历集合。
需要使用Collection的如下方法来完成
方法名称 | 说明 |
---|---|
default void forEach(Consumer<? super T> action) | 结合lambda遍历集合 |
Collection<String> lists = new ArrayList<>();
lists.forEach(new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);}
});lists.forEach(s -> { System.out.println(s); });
// lists.forEach(s -> System.out.println(s));
names.forEach(System.out::println);
三种遍历方式的区别
认识并发修改异常问题
遍历集合的同时又存在增删集合元素的行为时可能出现业务异常,这种现象被称之为并发修改异常问题。
需求:
- 现在假如购物车中存储了如下这些商品:Java入门,宁夏枸杞,黑枸杞,人字拖,特级枸杞,枸杞子。现在用户不想买枸杞了,选择了批量删除。
分析:
- 后台使用ArrayList集合表示购物车,存储这些商品名。
- 遍历集合中的每个数据,只要这个数据包含了“枸杞”则删除它。
- 输出集合看是否已经成功删除了全部枸杞数据了。
代码实现以及bug分析
ArrayList<String> list = new ArrayList<>();list.add("Java入门");list.add("宁夏枸杞");list.add("黑枸杞");list.add("人字拖");list.add("特级枸杞");list.add("枸杞子");list.add("西洋参");System.out.println(list);// 需求1:删除全部枸杞for (int i = 0; i < list.size(); i++) {String name = list.get(i);if(name.contains("枸杞")){list.remove(name);}}System.out.println(list); //出现并发修改异常问题。// [Java入门, 宁夏枸杞, 黑枸杞, 人字拖, 特级枸杞, 枸杞子, 西洋参]// [Java入门, 黑枸杞, 人字拖, 枸杞子, 西洋参]// i// [Java入门, 黑枸杞, 人字拖, 枸杞子, 西洋参]// 每次删完后紧接着的第二次元素会占用被删元素的的位置。会被跳过
使用迭代器
// 需求1:删除全部枸杞
// 方案一:迭代器遍历并删除默认也存在并发修改异常问题。
// 可以解决,解决方案3:使用迭代器自己的方法来删除
Iterator<String> it = list4.iterator();
while(it.hasNext()){String name = it.next();if(name.contains("枸杞")){it.remove(); // 可以解决 解决方案3:使用迭代器自己的方法来删除当前数据}
}
System.out.println(list4);
使用增强for
ArrayList<String> list2 = new ArrayList<>();
list2.add("Java入门");
list2.add("宁夏枸杞");
list2.add("黑枸杞");
list2.add("人字拖");
list2.add("特级枸杞");
list2.add("枸杞子");
list2.add("西洋参");
System.out.println(list2);// 需求1:删除全部枸杞
for (int i = 0; i < list2.size(); i++) {String name = list2.get(i);if(name.contains("枸杞")){list2.remove(name);i--; // 解决方案1:删除数据后做一步i--操作 (前提是支持索引)}
}
// [Java入门, 宁夏枸杞, 黑枸杞, 人字拖, 特级枸杞, 枸杞子, 西洋参]
// [Java入门, 人字拖, 西洋参]
// i
System.out.println(list2);System.out.println("=====================================================");ArrayList<String> list3 = new ArrayList<>();
list3.add("Java入门");
list3.add("宁夏枸杞");
list3.add("黑枸杞");
list3.add("人字拖");
list3.add("特级枸杞");
list3.add("枸杞子");
list3.add("西洋参");
System.out.println(list3);// 需求1:删除全部枸杞
// 解决方案2:倒着遍历并删除(前提是支持索引)
for (int i = list3.size() - 1; i >= 0; i--) {String name = list3.get(i);if(name.contains("枸杞")){list3.remove(name);}
}
// [Java入门, 人字拖, 西洋参]
// i
System.out.println(list3);
使用Lambda
// 直接报错无法解决
- 如果集合支持索引,可以使用for循环遍历,每删除数据后做i–;或者可以倒着遍历
- 可以使用迭代器遍历,并用迭代器提供的删除方法删除数据。
注意:增强for循环/Lambda遍历均不能解决并发修改异常问题,因此增它们只适合做数据的遍历,不适合同时做增删操作。
List集合
- Collection(接口):单列集合:每个元素(数据)只包含一个值。
- List(接口):添加的元素是有序、可重复、有索引。
- ArrayList:有序、可重复、有索引。基于数组存储数据的,索引查询快,增删慢。
- LinkedList:有序、可重复、有索引。基于双链表存储数据的,索引查询慢,增删快,对首尾增删改查极快。
- Set(接口):无序、不重复、无索引
- HashSet:无序、不重复、无索引
- LinkedHashSet:有序、不重复、无索引
- TreeSet:按照大小默认升序排序、不重复、无索引
- HashSet:无序、不重复、无索引
- List(接口):添加的元素是有序、可重复、有索引。
- Map(接口):双列集合:每个元素包含两个值(键值对)。
- HashMap:
- LinkedHashMap
- TreeMap
- HashMap:
ArrayList和LinkedList底层实现不同,适合的场景不同
List集合因为支持索引,所以多了很多与索引相关的方法,当然,Collection的功能List也都继承了。
方法名称 | 说明 |
---|---|
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
遍历方式
for循环(因为List集合有索引)
迭代器
增强for循环
Lambda表达式
public class ListDemo1 {public static void main(String[] args) {// 目标:掌握List系列集合独有的功能。ArrayList<String> names = new ArrayList<>(); // 一行经典代码// 添加数据names.add("张三");names.add("李四");names.add("王五");names.add("赵六");System.out.println(names); // [张三, 李四, 王五, 赵六]// 给第三个位置插入一个数据:赵敏names.add(2, "赵敏");System.out.println(names);// 删除李四System.out.println(names.remove(1)); // 根据下标删除,返回删除的数据System.out.println(names);// 把王五修改成:金毛System.out.println(names.set(2, "金毛")); // 根据下标修改,返回修改前的数据System.out.println(names);// 获取张三System.out.println(names.get(0));System.out.println("-----------四种遍历演示---------------");// 1、for循环for (int i = 0; i < names.size(); i++) {System.out.println(names.get(i));}// 2、迭代器Iterator<String> it = names.iterator();while (it.hasNext()) {String name = it.next();System.out.println(name);}// 3、增强forfor (String name : names) {System.out.println(name);}// 4、lambda表达式names.forEach(name -> System.out.println(name) );System.out.println(15 >> 1);}
}
LinkedList新增了:很多首尾操作的特有方法。
方法名称 | 说明 |
---|---|
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
List的特点、特有功能
ArrayList底层原理
LinkedList底层原理
LinkedList的应用场
可以用来设计队列: 先进先出,后进后出。
设计栈:后进先出,先进后出。
import java.util.LinkedList;
public class ListDemo2 {public static void main(String[] args) {// 目标:用LinkedList做一个队列对象。LinkedList<String> queue = new LinkedList<>();// 入队queue.addLast("赵敏");queue.addLast("西门吹雪");queue.addLast("陆小凤");queue.addLast("石观音");System.out.println(queue); // [赵敏, 西门吹雪, 陆小凤, 石观音]// 出队System.out.println(queue.removeFirst());// 赵敏System.out.println(queue.removeFirst());// 西门吹雪System.out.println(queue); // [陆小凤, 石观音]System.out.println("-------------------------------------------------");// 做一个栈LinkedList<String> stack = new LinkedList<>();// 压栈stack.push("第1颗子弹");stack.push("第2颗子弹");stack.push("第3颗子弹");stack.push("第4颗子弹");System.out.println(stack); // [第4颗子弹, 第3颗子弹, 第2颗子弹, 第1颗子弹]// 出栈System.out.println(stack.pop());// 第4颗子弹System.out.println(stack.pop());// 第3颗子弹System.out.println(stack);// [第2颗子弹, 第1颗子弹]}
}
list:电影信息管理模块案例
需求
- 开发一个电影信息管理模块,用户可以上架电影,查询电影
- 下架某部电影,以及下架某个主演参演的全部电影。
分析
- 每部电影都是一个电影对象,设计电影类。
- 需要定义一个电影操作类,其对象专门用于处理电影数据的业务。
- 操作类中需要定义一个集合存放全部的电影对象
public class Test {public static void main(String[] args) {// 目标:完成电影案例// 1、创建电影对象:定义电影类。// 2、创建一个电影操作对象:专门负责对象电影数据进行业务处理(上架,下架,查询,封杀某个电影明星的电影)MovieService movieService = new MovieService();movieService.start();}
}
// javabean类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Movie {private String name;private double score;private String actor;private double price;
}
// 业务层
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class MovieService {// 4、准备一个集合容器:存储全部上架的电影数据。private static List<Movie> movies = new ArrayList<>();private static Scanner sc = new Scanner(System.in);public void start() {while (true) {// 3、准备操作界面:GUI界面/cmd命令做。System.out.println("====电影信息操作系统===");System.out.println("1、上架");System.out.println("2、下架某个电影");System.out.println("3、查询某个电影");System.out.println("4、封杀某个明星");System.out.println("5、退出");System.out.println("6、展示全部电影");System.out.println("7、修改某个电影");System.out.println("请您输入操作命令:");String command = sc.next();switch (command) {case "1":// 上架(独立功能独立成方法)addMovie();break;case "2":// 下架某个电影(自己做一下)break;case "3":// 查询某个电影queryMovie();break;case "4":// 封杀某个明星deleteStar();break;case "5":System.out.println("退出成功!");return;case "6":// 展示全部电影queryAllMovies();break;case "7":// 修改某个电影(自己做一下)break;default:System.out.println("命令有毛病!");}}}private void queryAllMovies() {System.out.println("===========展示全部电影============");for (Movie m : movies) {System.out.println(m.getName() + " " + m.getActor() + " " + m.getPrice() + " " + m.getScore());}}/*** 封杀某个明星*/private void deleteStar() {System.out.println("===========封杀明星============");System.out.println("请您输入要封杀的明星:");String star = sc.next();for (int i = 0; i < movies.size(); i++) {Movie movie = movies.get(i);if (movie.getActor().contains(star)) {movies.remove(movie);i--;// 退一步}}System.out.println("封杀成功!");// 展示全部电影。queryAllMovies();}/*** 根据电影名称查询某部电影对象展示出来*/private void queryMovie() {System.out.println("===========查询电影============");System.out.println("请您输入电影名称:");String name = sc.next();// 根据电影名称查询电影对象返回,展示这个对象数据。Movie movie = queryMovieByName(name);if (movie != null) {System.out.println(movie.getName() + " " + movie.getActor() + " " + movie.getPrice() + " " + movie.getScore());} else {System.out.println("没有找到这个电影!");}}// 根据电影名称查询电影对象返回// movies = [m1, m2, m3 , ...]// mpublic Movie queryMovieByName(String name) {for (Movie m : movies) {if (m.getName().equals(name)) {return m; // 找到这个电影对象}}return null; // 没有找到这个电影对象}/*** 上架电影*/private void addMovie() {System.out.println("===========上架电影============");// 分析:每点击一次上架电影,其实就是新增一部电影。每部电影是一个电影对象封装数据的// 1、创建电影对象,封装这部电影信息。Movie movie = new Movie();// 2、给电影对象注入数据。System.out.println("请您输入电影名称:");movie.setName(sc.next());System.out.println("请您输入主演:");movie.setActor(sc.next());System.out.println("请您输入电影价格:");movie.setPrice(sc.nextDouble());System.out.println("请您输入电影评分:");movie.setScore(sc.nextDouble());// 3、把电影对象添加到集合中。movies.add(movie);System.out.println("上架成功!");}
}
Set集合
- Collection(接口):单列集合:每个元素(数据)只包含一个值。
- List(接口):添加的元素是有序、可重复、有索引。
- ArrayList:有序、可重复、有索引。基于数组存储数据的,索引查询快,增删慢。第一次扩容长度是10。
- LinkedList:有序、可重复、有索引。基于双链表存储数据的,索引查询慢,增删快,对首尾增删改查极快。
- Set(接口):不重复、无索引,常用方法是Collection提供的,几乎没有新增的常用功能。
- HashSet:无序、不重复、无索引。第一次扩容长度是16。哈希表(哈希表=数组+链表+红黑树)。
- LinkedHashSet:有序、不重复、无索引。哈希表(数组+链表+红黑树)。但是,它的每个元素都额外的多了一个双链表的机制记录它前后元素的位置。即双链表+哈希表(哈希表=数组+链表+红黑树)。
- TreeSet:按照大小默认升序排序、不重复、无索引。红黑树+哈希表(哈希表=数组+链表+红黑树)。对于自定义类型如Student对象,TreeSet默认是无法直接排序的会报错。想要排序需要重写Comparator对象去指定比较规则。
- HashSet:无序、不重复、无索引。第一次扩容长度是16。哈希表(哈希表=数组+链表+红黑树)。
- List(接口):添加的元素是有序、可重复、有索引。
- Map(接口):双列集合:每个元素包含两个值(键值对)。
- HashMap:
- LinkedHashMap
- TreeMap
- HashMap:
注意:HashSet
Set要用到的常用方法,基本上就是Collection提供的。自己几乎没有额外新增一些常用功能!
set集合使用
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;public class SetDemo1 {public static void main(String[] args) {// 目标:认识Set家族集合的特点。// 1、创建一个Set集合,特点:无序,不重复,无索引。
// Set<String> set = new HashSet<>(); // 一行经典代码 HashSet 无序,不重复,无索引。// 使用上面进行添加,打印结果如下[java, 新媒体, 鸿蒙, 电商设计, 大数据]Set<String> set = new LinkedHashSet<>(); // LinkedHashSet 有序,不重复,无索引。set.add("鸿蒙");set.add("鸿蒙");set.add("java");set.add("java");set.add("电商设计");set.add("电商设计");set.add("新媒体");set.add("大数据");System.out.println(set);// [鸿蒙, java, 电商设计, 新媒体, 大数据]// 2、创建一个TreeSet集合:排序(默认一定要大小升序排序),不重复,无索引。Set<Double> set1 = new TreeSet<>();set1.add(3.14);set1.add(5.6);set1.add(1.0);set1.add(1.0);set1.add(2.0);System.out.println(set1);// [1.0, 2.0, 3.14, 5.6]}
}
哈希值
这是学习底层原理的前置知识。
哈希值:就是一个int类型的随机值,Java中每个对象都有一个哈希值。
Java中的所有对象,都可以调用Obejct类提供的hashCode方法,返回该对象自己的哈希值。
// public int hashCode(); //返回对象的哈希码值
String s1 = "acd";
String s2 = "abc";
System.out.println(s1.hashCode());// 96386
System.out.println(s1.hashCode());// 96386
System.out.println(s2.hashCode());// 96354
System.out.println(s2.hashCode());// 96354
对象哈希值的特点:
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的。
- 不同的对象,它们的哈希值大概率不相等,但也有可能会相等(哈希碰撞)。
为什么大概率不相等?
因为int取值范围 (-21亿多 ~ 21亿多)一共45亿个对象。当我们有48亿个对象,就会有三亿个对象重复。
红黑树
前置知识。
我们知道普通二叉树。
但是存在问题我们的数字都出现在一侧
于是有了平衡二叉树
在满足查找二叉树的大小规则下,让树尽可能矮小,以此提高查数据的性能。
但是还不够好。于是有了红黑树。
红黑树,就是可以自平衡的二叉树:让红黑数量相等。
HashSet底层原理
基于哈希表存储数据的。
哈希表
JDK8之前,哈希表 = 数组+链表
JDK8开始,哈希表 = 数组+链表+红黑树
哈希表是一种增删改查数据,性能都较好的数据结构
Set<String> set = new HashSet<>();
- 创建一个默认长度16的数组,默认加载因子为0.75,数组名table
- 使用元素的哈希值对数组的长度做运算计算出应存入的位置,可以想象这个运算是取余。
- 判断当前位置是否为null,如果是null直接存入
- 如果不为null,表示有元素,则调用equals方法比较相等,则不存入链表;不相等,则存入链表。
- JDK 8之前,新元素存入数组,占老元素位置,老元素挂下面
- JDK 8开始之后,新元素直接使用链表挂在老元素下面。
- 但是如果我们链表过长不就相当于LinkedList了吗。所以当这16个元素被占用了16(当前长度) * 上面第一步的加载因子(0.75) = 12个元素后,会自动扩容。
- 但是会有一种可能,我的链表可以一直无限长。但是我的HashSet长度的元素一直没有达到扩容标准。于是加了一个另一个准则:JDK8开始,当链表长度超过8,且数组长度>=64时,自动将链表转成红黑树
HashSet集合元素的对象去重操作
在类中重写hashCode()和equals()
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Objects;@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {private String name;private int age;private String address;private String phone;// 只要两个对象的内容一样结果一定是true.// s3.equals(s1)@Overridepublic boolean equals(Object o) {// 1、如果是自己和自己比直接返回trueif (this == o) return true;// 2、如果o为空或者o不是Student类型,直接返回falseif (o == null || this.getClass() != o.getClass()) return false;// 3、比较两个对象的内容是否一样Student student = (Student) o;return this.age == student.age && Objects.equals(name, student.name) && Objects.equals(address, student.address) && Objects.equals(phone, student.phone);}@Overridepublic int hashCode() {// 不同学生对象,如果内容一样返回的哈希值一定是一样的,return Objects.hash(name, age, address, phone);}
}
// main
import java.util.HashSet;
import java.util.Set;
public class SetDemo2 {public static void main(String[] args) {// 目标:掌握HashSet集合去重操作。Student s1 = new Student("张三", 18, "北京", "123456");Student s2 = new Student("李四", 19, "上海", "989876");Student s3 = new Student("张三", 18, "北京", "123456");Student s4 = new Student("李四", 19, "上海", "989876");Set<Student> set = new HashSet<>(); // 不重复的!set.add(s1);set.add(s2);set.add(s3);set.add(s4);System.out.println(set);// 如果在Student类中不重写hashCode()和equals()方法。这里hashCode()相当于不同地址。虽然内容一样。// [Student(name=张三, age=18, address=北京, phone=123456),// Student(name=李四, age=19, address=上海, phone=989876)]}
}
LinkedHashSet集合的底层原理
依然是基于哈希表(数组、链表、红黑树)实现的。
但是,它的每个元素都额外的多了一个双链表的机制记录它前后元素的位置。
TreeSet集合底层原理
底层是基于红黑树实现的排序。
TreeSet集合排序规则
- 对于数值类型:Integer , Double,默认按照数值本身的大小进行升序排序。
- 对于字符串类型:默认按照首字符的编号升序排序。
- 对于自定义类型如Student对象,TreeSet默认是无法直接排序的。
TreeSet集合自定义排序规则
TreeSet集合存储自定义类型的对象时,必须指定排序规则,支持如下两种方式来指定比较规则。
-
让自定义的类(如学生类)实现Comparable接口,重写里面的compareTo方法来指定比较规则。
package com.itheima.demo1hashset;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;// 1、对象类实现一个Comparable比较接口,重写compareTo方法,// 指定大小比较规则 @Data @NoArgsConstructor @AllArgsConstructor public class Teacher implements Comparable<Teacher>{private String name;private int age;private double salary;// t2.compareTo(t1)// t2 == this 比较者// t1 == o 被比较者// 规定1:如果你认为左边大于右边 请返回正整数// 规定2:如果你认为左边小于右边 请返回负整数// 规定3:如果你认为左边等于右边 请返回0// 默认就会升序。@Overridepublic int compareTo(Teacher o) {// 按照年龄升序 // if(this.getAge() > o.getAge()) return 1; // if(this.getAge() < o.getAge()) return -1; // return 0;return this.getAge() - o.getAge(); // 升序 // return o.getAge() - this.getAge(); // 降序} }
-
通过调用TreeSet集合有参数构造器,可以设置Comparator对象(比较器对象,用于指定比较规则。
public TreeSet(Comparator<? super E> comparator)
设置Comparator
package com.itheima.demo1hashset;import java.util.Comparator; import java.util.Set; import java.util.TreeSet;public class SetDemo3 {public static void main(String[] args) {// 2、public TreeSet(Comparator c)集合自带比较器Comparator对象,指定比较规则Set<Teacher> teachers = new TreeSet<>(new Comparator<Teacher>() {@Overridepublic int compare(Teacher o1, Teacher o2) {// return o2.getAge() - o1.getAge(); //降序// if(o1.getSalary() > o2.getSalary()){ return 1; }// else if(o1.getSalary() < o2.getSalary()){// return -1; }// return 0;// return Double.compare(o1.getSalary(), o2.getSalary()); // 薪水升序return Double.compare(o2.getSalary(), o1.getSalary()); // 薪水升序}}); // 排序,不重复,无索引// 简化形式//Set<Teacher> teachers = new TreeSet<>((o1, o2) -> Double.compare(o1.getSalary(), o2.getSalary())); // 排序,不重复,无索引teachers.add(new Teacher("老陈", 20, 6232.4));teachers.add(new Teacher("dlei", 18, 3999.5));teachers.add(new Teacher("老王", 22, 9999.9));teachers.add(new Teacher("老李", 20, 1999.9));System.out.println(teachers);// 结论:TreeSet集合默认不能 给自定义对象排序啊,因为不知道大小规则。} }
两种方式中,关于返回值的规则:
如果认为第一个元素 > 第二个元素 返回正整数即可。
如果认为第一个元素 < 第二个元素返回负整数即可。
如果认为第一个元素 = 第二个元素返回0即可,此时Treeset集合只会保留一个元素,认为两者重复。
注意:如果类本身有实现Comparable接口,TreeSet集合同时也自带比较器,默认使用集合自带的比较器排序。
Collection小节
- 如果希望记住元素的添加顺序,需要存储重复的元素,又要频繁的根据索引查询数据?
用ArrayList集合(有序、可重复、有索引),底层基于数组的。(常用) - 如果希望记住元素的添加顺序,且增删首尾数据的情况较多?
用LinkedList集合(有序、可重复、有索引),底层基于双链表实现的。 - 如果不在意元素顺序,也没有重复元素需要存储,只希望增删改查都快?
用HashSet集合(无序,不重复,无索引),底层基于哈希表实现的。 (常用) - 如果希望记住元素的添加顺序,也没有重复元素需要存储,且希望增删改查都快?
用LinkedHashSet集合(有序,不重复,无索引), 底层基于哈希表和双链表。 - 如果要对元素进行排序,也没有重复元素需要存储?且希望增删改查都快?
用TreeSet集合,基于红黑树实现。
Map集合
- Collection(接口):单列集合:每个元素(数据)只包含一个值。
- List(接口):添加的元素是有序、可重复、有索引。
- ArrayList:有序、可重复、有索引。基于数组存储数据的,索引查询快,增删慢。第一次扩容长度是10。
- LinkedList:有序、可重复、有索引。基于双链表存储数据的,索引查询慢,增删快,对首尾增删改查极快。
- Set(接口):不重复、无索引,常用方法是Collection提供的,几乎没有新增的常用功能。
- HashSet:无序、不重复、无索引。第一次扩容长度是16。哈希表(哈希表=数组+链表+红黑树)。
- LinkedHashSet:有序、不重复、无索引。哈希表(数组+链表+红黑树)。但是,它的每个元素都额外的多了一个双链表的机制记录它前后元素的位置。即双链表+哈希表(哈希表=数组+链表+红黑树)。
- TreeSet:按照大小默认升序排序、不重复、无索引。红黑树+哈希表(哈希表=数组+链表+红黑树)。对于自定义类型如Student对象,TreeSet默认是无法直接排序的会报错。想要排序需要重写Comparator对象去指定比较规则,或者实现Comparable接口,重写里面的compareTo方法来指定比较规则。
- HashSet:无序、不重复、无索引。第一次扩容长度是16。哈希表(哈希表=数组+链表+红黑树)。
- List(接口):添加的元素是有序、可重复、有索引。
- Map(接口):双列集合:每个元素包含两个值(键,值),键不重复、无索引
- HashMap:无序、键不重复、无索引。哈希表(哈希表=数组+链表+红黑树)
- LinkedHashMap:有序、键不重复、无索引。双链表+哈希表(哈希表=数组+链表+红黑树)
- TreeMap:按照大小默认升序排序、键不重复、无索引。红黑树+哈希表(哈希表=数组+链表+红黑树),想要排序需要重写Comparator对象去指定比较规则。
- HashMap:无序、键不重复、无索引。哈希表(哈希表=数组+链表+红黑树)
Map集合也被叫做“键值对集合”,格式:{key1=value1 , key2=value2 , key3=value3 , …}
Map集合的所有键是不允许重复的,但值可以重复,键和值是一一对应的,每一个键只能找到自己对应的值
Map集合在什么业务场景下使用。
需要存储一一对应的数据时,就可以考虑使用Map集合来做。
{商品1=2 , 商品2=3 , 商品3 = 2 , 商品4= 3}
注意:Map系列集合的特点都是由键决定的,值只是一个附属品,值是不做要求的
HashMap(由键决定特点): 无序、不重复、无索引; (用的最多)
LinkedHashMap (由键决定特点):由键决定的特点:有序、不重复、无索引。
TreeMap (由键决定特点):按照大小默认升序排序、不重复、无索引。
Map集合的常用方法
为什么要先学习Map的常用方法 ?
Map是双列集合的祖宗,它的功能是全部双列集合都可以继承过来使用的。
方法名称 | 说明 |
---|---|
public V put(K key,V value) | 添加元素 |
public int size() | 获取集合的大小 |
public void clear() | 清空集合 |
public boolean isEmpty() | 判断集合是否为空,为空返回true , 反之 |
public V get(Object key) | 根据键获取对应值 |
public V remove(Object key) | 根据键删除整个元素 |
public boolean containsKey(Object key) | 判断是否包含某个键 |
public boolean containsValue(Object value) | 判断是否包含某个值 |
public Set<K> keySet() | 获取全部键的集合 |
public Collection<V> values() | 获取Map集合的全部值 |
public class MapDemo2 {public static void main(String[] args) {// 目标:掌握Map的常用方法。Map<String,Integer> map = new HashMap<>();map.put("嫦娥", 20);map.put("女儿国王", 31);map.put("嫦娥", 28);map.put("铁扇公主", 38);map.put("紫霞", 31);map.put(null, null);System.out.println(map); // {null=null, 嫦娥=28, 铁扇公主=38, 紫霞=31, 女儿国王=31}// 写代码演示常用方法System.out.println(map.get("嫦娥")); // 根据键取值 28System.out.println(map.get("嫦娥2")); // 根据键取值 nullSystem.out.println(map.containsKey("嫦娥")); // 判断是否包含某个键 trueSystem.out.println(map.containsKey("嫦娥2")); // falseSystem.out.println(map.containsValue(28)); // 判断是否包含某个值 trueSystem.out.println(map.containsValue(28.0)); // falseSystem.out.println(map.remove("嫦娥")); // 根据键删除键值对,返回值System.out.println(map);// {null=null, 铁扇公主=38, 紫霞=31, 女儿国王=31}// map.clear(); // 清空map// System.out.println(map);System.out.println(map.isEmpty()); // 判断是否为空 falseSystem.out.println(map.size()); // 获取键值对的个数 4// 获取所有的键放到一个Set集合返回给我们Set<String> keys = map.keySet();for (String key : keys) {System.out.print(key+" ");}// null 铁扇公主 紫霞 女儿国王// 获取所有的值放到一个Collection集合返回给我们Collection<Integer> values = map.values();for (Integer value : values) {System.out.print(value+" ");}// null 38 31 31}
}
三种遍历方式
- 键找值:先获取Map集合全部的键,再通过遍历键来找值
- 键值对:把“键值对“看成一个整体进行遍历(难度较大)
- Lambda:JDK 1.8开始之后的新技术(非常的简单)
键找值
方法名称 | 说明 |
---|---|
public Set<K> keySet() | 获取所有键的集合 |
public V get(Object key) | 根据键获取其对应的值 |
import java.util.HashMap;
import java.util.Map;
import java.util.Set;public class MapTraverseDemo3 {public static void main(String[] args) {Map<String,Integer> map = new HashMap<>();map.put("嫦娥", 20);map.put("女儿国王", 31);map.put("嫦娥", 28);map.put("铁扇公主", 38);map.put("紫霞", 31);System.out.println(map); // {嫦娥=28, 铁扇公主=38, 紫霞=31, 女儿国王=31}// 1、提起Map集合的全部键到一个Set集合中去Set<String> keys = map.keySet();// 2、遍历Set集合,得到每一个键for (String key : keys) {// 3、根据键去找值Integer value = map.get(key);System.out.println(key + "=" + value);}}
}
键值对
Map提供的方法 | 说明 |
---|---|
Set<Map.Entry<K, V>> entrySet() | 获取所有“键值对”的集合 |
Map.Entry提供的方法 | 说明 |
---|---|
K getKey() | 获取键 |
V getValue() | 获取值 |
import java.util.HashMap;
import java.util.Map;
import java.util.Set;public class MapTraverseDemo4 {public static void main(String[] args) {Map<String,Integer> map = new HashMap<>();map.put("嫦娥", 20);map.put("女儿国王", 31);map.put("嫦娥", 28);map.put("铁扇公主", 38);map.put("紫霞", 31);System.out.println(map); // {嫦娥=28, 铁扇公主=38, 紫霞=31, 女儿国王=31}// 1、把Map集合转换成Set集合,里面的元素类型都是键值对类型(Map.Entry<String, Integer>)/*** map = {嫦娥=28, 铁扇公主=38, 紫霞=31, 女儿国王=31}* ↓* map.entrySet()* ↓* Set<Map.Entry<String, Integer>> entries = [(嫦娥=28), (铁扇公主=38), (紫霞=31), (女儿国王=31)]* entry*/Set<Map.Entry<String, Integer>> entries = map.entrySet();// 2、遍历Set集合,得到每一个键值对类型元素for (Map.Entry<String, Integer> entry : entries) {String key = entry.getKey();Integer value = entry.getValue();System.out.println(key + "=" + value);}}
}
Lambda
方法名称 | 说明 |
---|---|
default void forEach(BiConsumer<? super K, ? super V> action) | 结合lambda遍历Map集合 |
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;public class MapTraverseDemo5 {public static void main(String[] args) {Map<String,Integer> map = new HashMap<>();map.put("嫦娥", 20);map.put("女儿国王", 31);map.put("嫦娥", 28);map.put("铁扇公主", 38);map.put("紫霞", 31);System.out.println(map); // {嫦娥=28, 铁扇公主=38, 紫霞=31, 女儿国王=31}// 1、直接调用Map集合的forEach方法完成遍历
// map.forEach(new BiConsumer<String, Integer>() {
// @Override
// public void accept(String key, Integer value) {
// System.out.println(key + "=" + value);
// }
// });map.forEach((k,v) -> System.out.println(k + "=" + v));}
}
案例练习 统计投票信息
需求:
某个班级80名学生,现在需要组织秋游活动,班长提供了四个景点依次是(A、B、C、D),每个学生只能选择一个景点,请统计出最终哪个景点想去的人数最多。
分析
- 将80个学生选择的数据拿到程序中去,[A, A, B , A, B, C, D, …]
- 准备一个Map集合用于存储统计的结果,Map<String,Integer>,键是景点,值代表投票数量。
- 遍历80个学生选择的景点,每遍历一个景点,就看Map集合中是否存在该景点,不存在存入“景点=1“,存在则其对应值+1
需要存储一一对应的数据时,就可以考虑使用Map集合来做
import java.util.*;public class MapTest6 {public static void main(String[] args) {// 目标:完成Map集合相关的案例:投票统计程序。calc();}public static void calc(){// 1、把80个学生选择的景点数据拿到程序中来,才可以统计。List<String> locations = new ArrayList<>();String[] names = {"玉龙雪山", "长城", "少林寺", "丽江"};Random r = new Random();for (int i = 1; i <= 80; i++) {int index = r.nextInt(names.length); // 0 1 2 3locations.add(names[index]);}System.out.println(locations);// locations = [丽江, 玉龙雪山, 玉龙雪山, 丽江, 少林寺, 玉龙雪山, 丽江, 丽江, 长城, 长城, 长城, 少林寺, ....// 2、统计每个景点被选择的次数// 最终统计的结果是一个键值对的形式,所以可以考虑定义一个Map集合来统计结果。Map<String, Integer> map = new HashMap<>(); // map = { }// 3、遍历80个学生选择的景点,来统计选择的次数。for (String location : locations) {// 4、判断当前遍历的景点是否在Map集合中存在,如果不存在说明是第一次出现,如果存在说明之前统计过。
// if (map.containsKey(location)) {
// // 这个景点之前出现过,其值+1
// map.put(location, map.get(location) + 1);
// } else {
// // 这个景点是第一次统计,存入“景点=1”
// map.put(location, 1);
// }// 简化写法!map.put(location, map.containsKey(location) ? map.get(location) + 1 : 1);}// 5、把统计结果打印出来。map.forEach((k, v) -> {System.out.println(k + "被选择了" + v + "次");});}
}
HashMap集合的底层原理
HashMap跟HashSet的底层原理是一模一样的,都是基于哈希表实现的。
实际上:原来学的Set系列集合的底层就是基于Map实现的,只是Set集合中的元素只要键数据,不要值数据而已。
下面是 HashSet源码。我们发现set就是给Map套了一个壳子。
public HashSet() { map = new HashMap<>();}
LinkedHashMap集合的原理
底层数据结构依然是基于哈希表实现的,只是每个键值对元素又额外的多了一个双链表的机制记录元素顺序(保证有序)。
实际上:原来学习的LinkedHashSet集合的底层原理就是LinkedHashMap。
TreeMap集合的原理
特点:不重复、无索引、可排序(按照键的大小默认升序排序,只能对键排序)
原理:TreeMap跟TreeSet集合的底层原理是一样的,都是基于红黑树实现的排序。
TreeMap集合同样也支持两种方式来指定排序规则
让类实现Comparable接口,重写比较规则。
TreeMap集合有一个有参数构造器,支持创建Comparator比较器对象,以便用来指定比较规则。
Map<Teacher, String> map = new TreeMap<>((o1, o2) -> Double.compare(o2.getSalary(), o1.getSalary())); // 按照键排序:升序
map.put(new Teacher("老陈", 20, 6232.4), "462期");
map.put(new Teacher("dlei", 18, 3999.5), "422期");
map.put(new Teacher("老王", 22, 9999.9), "461期");
map.put(new Teacher("老李", 20, 1999.9), "423期");
System.out.println(map);
相关文章:
Collection集合,List集合,set集合,Map集合
文章目录 集合框架认识集合集合体系结构Collection的功能常用功能三种遍历方式三种遍历方式的区别 List集合List的特点、特有功能ArrayList底层原理LinkedList底层原理LinkedList的应用场list:电影信息管理模块案例 Set集合set集合使用哈希值红黑树HashSet底层原理HashSet集合元…...
Kafka 核心使用机制总结
Kafka 核心使用机制总结 Kafka 核心使用机制总结1. 分区 (Partitions) - 实现伸缩性与并行处理2. 副本 (Replicas) / 复制因子 (Replication Factor) - 实现高可用与容错3. 消费者组 (Consumer Groups) - 控制消息分发与消费进度4. 数据保留策略 (Retention Policies) - 管理存…...
【MCP】第二篇:IDE革命——用MCP构建下一代智能工具链
【MCP】第二篇:IDE革命——用MCP构建下一代智能工具链 一、引言二、IDE集成MCP2.1 VSCode2.1.1 安装VSCode2.1.2 安装Cline2.1.3 配置Cline2.1.4 环境准备2.1.5 安装MCP服务器2.1.5.1 自动安装2.1.5.2 手动安装 2.2 Trae CN2.2.1 安装Trae CN2.2.2 Cline使用2.2.3 内…...
WebSocket是h5定义的,双向通信,节省资源,更好的及时通信
浏览器和服务器之间的通信更便利,比http的轮询等效率提高很多, WebSocket并不是权限的协议,而是利用http协议来建立连接 websocket必须由浏览器发起请求,协议是一个标准的http请求,格式如下 GET ws://example.com:3…...
【PostgreSQL教程】PostgreSQL 特别篇之 语言接口连接Perl
博主介绍:✌全网粉丝22W+,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物联网、机器学习等设计与开发。 感兴趣的可…...
springboot-基于Web企业短信息发送系统(源码+lw+部署文档+讲解),源码可白嫖!
摘要 当今社会已经步入了科学技术进步和经济社会快速发展的新时期,国际信息和学术交流也不断加强,计算机技术对经济社会发展和人民生活改善的影响也日益突出,人类的生存和思考方式也产生了变化。本系统采用B/S架构,数据库是MySQL…...
Centos9安装docker
1. 卸载docker 查看是否安装了docker yum list | grep docker卸载老版本docker,拷贝自官网 sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine卸载新版本…...
MySQL表的操作
MySQL表的操作 1. 创建表 在创建表前,需要先进入到某个数据库: use db_name创建表时,最好提前设计好表应该有的所有内容,否则后续添加或删除表的列可能会引发一连串的问题。 create table tb_name (field1 data_type [comment…...
Jmeter中同步定时器使用注意点
1.设置数量不可大于总线程数量,不然会一直等待 2.设置数量必须与总线程数量成整数倍数,不然还是要一直等。 3.当配置的数量小于线程数时,最好把循环打开,避免最后一次未准备好的线程数量达不到并发数。...
从零开始搭建Django博客③--前端界面实现
本文主要在Ubuntu环境上搭建,为便于研究理解,采用SSH连接在虚拟机里的ubuntu-24.04.2-desktop系统搭建,当涉及一些文件操作部分便于通过桌面化进行理解,通过Nginx代理绑定域名,对外发布。 此为从零开始搭建Django博客…...
使用Handsontable实现动态表格和下载表格
1.效果 2.实现代码 首先要加载Handsontable,在示例中我是cdn的方式引入的,vue的话需要下载插件 let hot null;var exportPlugin null;function showHandsontable(param) {const container document.getElementById("hot-container");// 如果…...
结合地理数据处理
CSV 文件不仅可以存储表格数据,还可以与地理空间数据结合,实现更强大的地理处理功能。例如,你可以将 CSV 文件中的坐标数据转换为点要素类,然后进行空间分析。 示例:将 CSV 文件中的坐标数据转换为点要素类 假设我们有…...
使用Geotools实现将Shp矢量文件加载SLD并合并图例生成-以湖南省周边城市为例
目录 前言 一、技术实现简介 1、生成成果说明 2、生成流程图说明 二、具体生成实践 1、渲染地图 2、生成图例 3、合并图像及输出 三、总结 前言 在当今数字化时代,地理信息系统(GIS)技术已成为城市管理、资源规划、环境监测等众多领域…...
openGauss数据库:起源、特性与对比分析
openGauss数据库:起源、特性与对比分析 一、起源与发展历程 1. 技术背景与开源历程 openGauss是由华为公司主导开发的开源关系型数据库管理系统,其技术根源可追溯至PostgreSQL。2019年,华为在内部整合了多个数据库产品线(如GMDB…...
相机中各个坐标系的转换关系如像素坐标系到世界坐标系以及相机标定的目的
一、背景 无论是机器人领域、SLAM还是自动驾驶领域,都会涉及相机标定,但是看了很多博客,都是各种坐标系的变换,没有从上层说明进行坐标变换的目的是什么,以及相机标定完成后,是已知像素坐标求世界坐标&…...
ubuntu24设置拼音输入法,解决chrome不能输入中文
## 推荐方案:使用 Fcitx5 Fcitx5 是当前在 Wayland 环境下兼容性最好的输入法框架。 ### 1. 安装 Fcitx5 bash sudo apt update sudo apt install fcitx5 fcitx5-chinese-addons fcitx5-frontend-gtk3 fcitx5-frontend-gtk4 fcitx5-frontend-qt5 fcitx5-module-c…...
linux内核进程管理(1)——创建,退出
linux源码阅读——进程管理(1) 1. 进程的基本介绍1.1 linux中进程和线程的区别1.2 task_struct中的基本内容1.3 命名空间ns(namespace)命名空间结构图Linux 中的命名空间类型 1.4 进程标识符 2. 创建一个进程的流程2.1 CLONE宏2.2 创建进程系统调用1. do…...
容器修仙传 我的灵根是Pod 第8章 护山大阵(DaemonSet)
第三卷:上古遗迹元婴篇 第8章 护山大阵(DaemonSet) 九霄之上,雷云如怒海翻腾。 天调度宗的护山大阵「九霄雷光阵」正发出悲鸣,七十二根镇山雷柱已有半数熄灭。每根雷柱底部,本该守护节点的「雷符傀儡」&a…...
使用Python将YOLO的XML标注文件转换为TXT文件格式
使用Python将YOLO的XML标注文件转换为TXT文件格式,并划分数据集 import xml.etree.ElementTree as ET import os from os import listdir, getcwd from os.path import join import random from shutil import copyfile from PIL import Image# 只要改下面的CLASSE…...
在面试中被问到spring是什么?
Spring框架的核心回答 1. 定义与定位 Spring是一个轻量级、开源的企业级应用开发框架,旨在简化Java应用的开发,提供全面的编程和配置模型。它的核心目标是解决企业应用开发的复杂性,通过模块化设计和松耦合架构,帮助开发者更高效…...
MongoDB Ubuntu 安装
MongoDB 安装 https://www.mongodb.com/zh-cn/docs/manual/installation/ https://www.mongodb.com/zh-cn/docs/manual/tutorial/install-mongodb-on-ubuntu/ https://www.mongodb.com/zh-cn/docs/manual/reference/ulimit/ https://www.mongodbmanager.com/download ubun…...
ElasticSearch深入解析(三):Elasticsearch 7的安装与配置、Kibana安装
文章目录 〇、简介1.Elasticsearch简介2.典型业务场景3.数据采集工具4.名词解释 一、安装1.使用docker(1)创建虚拟网络(2)Elasticsearch安装步骤 2.使用压缩包 二、配置1.目录介绍2.配置文件介绍3.elasticsearch.yml节点配置4.jvm.options堆配置问题:为什么说堆内存…...
初始SpringBoot
此文介绍一些有关我对SpringBoot的学习理解, 声明:此处我的IDEA是企业版的,可能和社区版会有一些差异 1. 第⼀个SpringBoot程序 1. SpringBoot介绍 我们看下Spring官方的介绍 可以看到,Spring让Java程序更加快速,简单和安全.Spring对于速…...
【算法笔记】动态规划基础(一):dp思想、基础线性dp
目录 前言动态规划的精髓什么叫“状态”动态规划的概念动态规划的三要素动态规划的框架无后效性dfs -> 记忆化搜索 -> dp暴力写法记忆化搜索写法记忆化搜索优化了什么?怎么转化成dp?dp写法 dp其实也是图论首先先说结论:状态DAG是怎样的…...
C++入门基础(2)
Hello~,欢迎大家来到我的博客进行学习! 目录 1.缺省参数2.函数重载3.引用3.1 引用的概念和定义3.2 引用的特性3.3引用的使用3.4 const引用3.5 指针和引用的关系扩展 4. nullptr 1.缺省参数 缺省参数是声明或定义函数时为函数的参数指定⼀个缺省值。在调用该函数时&…...
OpenCV-Python (官方)中文教程(部分一)_Day15
18.图像梯度 梯度简单来说就是求导。 OpenCV 提供了三种不同的梯度滤波器,或者说高通滤波器:Sobel, Scharr和Laplacian。Sobel,Scharr 其实就是求一阶或二阶导数。Scharr 是对 Sobel(使用小的卷积核求解求解梯度角度时)的优化。Laplacian 是…...
大厂面试:MySQL篇
前言 本章内容来自B站黑马程序员java大厂面试题和小林coding 博主学习笔记,如果有不对的地方,海涵。 如果这篇文章对你有帮助,可以点点关注,点点赞,谢谢你! 1.MySQL优化 1.1 定位慢查询 定位 一个SQL…...
软件工程的13条“定律”:从Hyrum定律到康威定律,再到Zawinski定律
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
Linux删除大文件df空间avail空间不增加
背景 根磁盘被同事写满,使用> 删除一些安装包后,df中的avail空间还是0 排除有进程正在占用文件,已使用lsof命令检测过我所删的文件是没有进程在使用 原因 是文件系统预留空间在作祟 解决 # 文件系统预留块 tune2fs -l /dev/vda2 | gr…...
【C语言-选择排序算法】实现对十个数进行排序
目录 前言 一、选择排序算法原理 二、选择排序算法实现对十个数进行排序 三、代码运行示例 四、选择排序算法的时间复杂度和空间复杂度分析 五、选择排序算法的优缺点 六、总结 前言 在计算机科学领域,排序算法是基石般的存在,它们就像是整理杂乱…...
驱动开发硬核特训 · Day 18:深入理解字符设备驱动与子系统的协作机制(以 i.MX8MP 为例)
日期:2025年04月23日 回顾:2025年04月22日(Day 17:Linux 中的子系统概念与注册机制) 本日主题:字符设备驱动 子系统协作机制剖析 学习目标:理解字符设备的注册原理,掌握其与子系统间…...
SQL Server 2022 常见问题解答:从安装到优化的全场景指南
SQL Server 2022 作为微软最新的数据库管理系统,在性能、安全性和云集成方面带来了多项革新。然而,用户在实际使用中仍可能遇到各类问题。本文将围绕安装配置、性能优化、备份恢复、安全设置、高可用性方案、兼容性问题及错误代码解析等核心场景…...
软件开发版本库命名规范说明
背景:近期一直再更新自己所开发的一个前端大文件上传npm库(enlarge-file-upload),为了让库的发版更加规范,于是参考了各种文档写下了这篇关于软件开发库的版本命名规范,且不仅局限于前端的版本命名规范,适用于整个软件…...
Kafka 详解
1.基本概念:Kafka 是分布式发布 - 订阅消息系统,具有高吞吐量、可扩展性等优势,支持点对点和发布订阅两种消息模式,涉及 Broker、Topic、Partition 等多种角色。 2.安装步骤:需先安装 JDK 和 Zookeeper,下…...
【Qwen2.5-VL 踩坑记录】本地 + 海外账号和国内账号的 API 调用区别(阿里云百炼平台)
API 调用 阿里云百炼平台的海内外 API 的区别: 海外版:需要进行 API 基础 URL 设置国内版:无需设置。 本人的服务器在香港,采用海外版的 API 时,需要进行如下API端点配置 / API基础URL设置 / API客户端配置…...
硬核解析:整车行驶阻力系数插值计算与滑行阻力分解方法论
引言:阻力优化的核心价值 在汽车工程领域,行驶阻力是影响动力性、经济性及排放的核心因素。根据统计,车辆行驶中约60%的燃油消耗用于克服阻力(风阻、滚阻、传动内阻等)。尤其在电动化趋势下,阻力降低1%可提…...
【网络原理】TCP提升效率机制(一):滑动窗口
目录 一. 前言 二. 滑动窗口 三. 丢包现象 1)ACK报文丢失 2)数据丢失 四. 总结 一. 前言 TCP最核心的机制就是可靠传输 ,确认应答,超时重传,连接管理这些都保证了可靠传输,得到了可靠传输,…...
移动端使用keep-alive将页面缓存和滚动缓存具体实现方法 - 详解
1. 配置组件名称 确保列表页组件设置了name选项,(组合式API额外配置): <!-- vue2写法 --> export default {name: UserList // 必须与 <keep-alive> 的 include 匹配 }<!-- vue3写法 --> defineOptions({na…...
工作记录9
1.点击按钮发送AJAX请求 <!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title&…...
Java 异常 SSLException: fatal alert: protocol_version 全解析与解决方案
在 Java 网络通信中,SSLException: fatal alert: protocol_version 是典型的 TLS/SSL 协议版本不兼容异常。本文结合 Java 官方规范、TLS 协议标准及实战经验,提供体系化解决方案,帮助开发者快速定位并解决协议版本冲突问题。 一、异常本质&…...
连锁美业管理系统「数据分析」的重要左右分析︳博弈美业系统疗愈系统分享
美业管理系统中的数据分析功能在提升运营效率、优化客户体验、增强决策科学性等方面具有重要作用。 数据分析功能将美业从“经验驱动”升级为“数据驱动”,帮助商家在客户管理、成本控制、服务创新等环节实现精细化运营,最终提升盈利能力与品牌竞争力…...
Openharmony 和 HarmonyOS 区别?
文章目录 OpenHarmony 与 HarmonyOS 的区别:开源生态与商业发行版的定位差异一、定义与定位二、技术架构对比1. OpenHarmony2. HarmonyOS 三、应用场景差异四、开发主体与生态支持五、关键区别总结六、如何选择?未来展望 OpenHarmony 与 HarmonyOS 的区别…...
26.OpenCV形态学操作
OpenCV形态学操作 形态学操作(Morphological Operations)源自二值图像处理,主要用于分析和处理图像中的结构元素,对图像进行去噪、提取边缘、分割等预处理步骤。OpenCV库中提供了丰富的形态学函数,常见的包括…...
uniapp小程序使用echarts
1、引入插件 在Dcloud插件市场下载echarts插件:插件地址 2、页面使用简单示例: <template><view class"pie-view flex-center"><view style"width: 100%; height: 600rpx"><l-echart ref"chartRef&quo…...
Vue 中 使用 Mixins 解决 多页面共用相同组件的相关问题
1. 需要解决的问题 最近在vue项目中,有多个页面需要用到同一个组件,至于是什么组件,这里不重要,重要的这个组件需要被多个文件引用,而且有组件有一些控制逻辑。 1.1代码展示 <template><div class"ap…...
Rust 学习笔记:Rust 简介
Rust 学习笔记:Rust 简介 Rust 学习笔记:Rust 简介历史与发展历程核心特性优点缺点应用领域 Rust 学习笔记:Rust 简介 Rust 是一种系统级编程语言,由 Mozilla 研究院的 Graydon Hoare 于 2006 年设计,旨在提供内存安全…...
第六节:进阶特性高频题-自定义指令实现场景
示例:v-lazy(图片懒加载)、v-permission(权限控制) 钩子函数:mounted、updated、unmounted等 一、自定义指令核心机制 指令生命周期钩子 const myDirective {// 元素插入父节点时调用(初始化…...
未曾设想的道路1
写在前面: 与其转去读博,倾向自学就业。 中国科学技术大学数学科学学院拥有一支优秀的师资团队,以下是部分教授的简介: 陈发来教授: 荣誉:2024年6月13日,在德国莱布尼茨信息科学中心召开的国际…...
Axure按钮设计分享:打造高效交互体验的六大按钮类型
在产品设计过程中,按钮作为用户与界面交互的核心元素,其设计质量直接影响用户体验与操作效率。Axure作为一款强大的原型设计工具,为设计师提供了丰富的按钮设计选项。本文将围绕基础按钮、禁用按钮、圆角按钮、动态按钮、渐变按钮和图标按钮六…...
MySQL 8.4企业版 安装和配置审计插件
在最新的MySQL 8.4.4企业版上启用审计日志功能 操作系统:Ubuntu 24.04 数据库:8.4.4-commercial for Linux on x86_64 (MySQL Enterprise Server - Commercial) 1.查看安装脚本 下面2个脚本位于mysql安装目录 share 下,一个是window一个是linux可以用…...