一、初始集合与应用
1.1 周介绍
1.学习六部分内容:
(1)常见集合与应用
(2)泛型及高阶应用
(3)多线程的安全与应用
(4)通过I/O实现文件的读取与写入
(5)综合案例-多线程下载器
(6)JDK不同版本的新特性-基础篇
2.重要知识点:
(1)常见集合与应用
集合,是利用多种数据结构,代替数组,存储多个数据。
(2)泛型及高阶应用
(3)多线程的安全与应用
很多初学者的难点。
(4)通过I/O实现文件的读取与写入
(5)综合案例-多线程下载器
(6)JDK不同版本的新特性-基础篇
随着时代发展,现在用的JDK一定会被淘汰。学习新的语法新特性,有助于解决实际开发中的问题。
1.2 课程介绍
本章主要学习常见集合与应用。分四部分内容:
(1)List集合体系及应用
(2)Set集合体系及应用
(3)Map映射体系及其应用
(4)应用Collections实现集合排序
1.3 初识集合与应用的使用场景
1.集合概念
以前,进行数据存储的是数组。缺点是创建数组时必须声明确定数组的长度。但是,实际应用中,一开始并不确定数组有多少个元素。
现在,Java中有一些工具类,可以用来替代数组,弥补上述缺点,称为集合类。
集合类包含如下四个体系:
- List
- Set
- Map
- Queue------队列
集合类主要由两个接口派生而出:
- Collection
- Map
2.四种存储结构
- List------代表有序、可重复的集合
- Set------代表无序、不可重复的集合
- Map-----代表存储映射关系的集合
- Queue--代表队列的特性
3.Collection接口及实现
4.Map接口及实现
以上是粗略的知识脉络。
1.4 IDEA编译器的安装与项目创建
1.IDEA编译器
以前,用的是Eclipse。现在,用IDEA。
全程IntelliJ IDEA
,是java编程公认最好的IDE。
特点:
- 功能更强大,提供大量智能工具,例如语法提示、代码分析、格式化。
- 最核心的特色:快捷键,能极大提高开发效率
2.IDEA的安装
(1)JDK8的安装
前置要求:电脑上要安装JDK8及以上版本
目前,国际及中国范围内,70%以上的软件项目,仍然使用java8进行。
在系统cmd命令行中,检查JDK是否安装成功:
(2)安装IntelliJ IDEA
:
国内主流用的版本是2020:
勾选:
成功安装:
二、List集合体系及应用
2.1 List接口
1.List集合:
元素有序、可重复的集合,每个元素都有其对应的顺序索引。
- 有序:天然按照放入时的前后顺序,进行排列
- 重复:元素可以重名,因为可以索引访问指定位置的元素
2.图示:
实际开发中,对于List接口,应用最多的是ArrayList
和LinkedList
两种实现类。
2.2 ArrayList
实现类的特点及方法应用
1.特点
- 基于数组实现的List类,是Java数组的有效替代品
- 自动扩容,多数情况下不用指定最大长度
- 数据在内存中的存储是紧密连续的,因此,数据访问速度非常快
一般,数组就要在实例化数组的时候必须指定数组的长度。
2.使用方法
add()
get()
import java.util.ArrayList; //所有关于集合的类和接口,都统一存储在java.util工具包下
...
ArrayList<String> bookList=new ArrayList<String>(); //实例化类ArrayList //<String>:说明保存在bookList对象里的数据都是字符串 //不用指定大小
bookList.add("三国演义"); //新增元素,尾部追加
String bookName1=bookList.get(0); //获取第一个元素,索引从0开始
3.程序操作tips
(1)想回到工程的默认首界面:选择界面
File--->Close Project
(2)调整目录结构显示方式
让目录结构更清晰
(3)创建类
4.ArrayList`实现类的方法应用
add()
方法IDEA源代码并不需要像Eclipse那样,手动保存文件。因为文件变化时,IDEA会自动帮我们保存文件
package com.imooc.collection.list;
import java.util.ArrayList;
public class ArrayListSample {
public static void main(String[] args) { //"psvm":用IntelliJ IDEA的快捷键来生成主方法:
ArrayList<String> bookList=new ArrayList<String>();
bookList.add("三国演义"); //"Ctrl+Shift+回车":自动增加结尾的分号;如果有分号,就自动回车,保证连续书写
bookList.add("水浒传");
bookList.add("三国演义");
System.out.println(bookList); //"sout":自动生成打印输出的代码段
}
}
--->
[三国演义, 水浒传, 三国演义] //List集合的元素有序、允许重复的体现
get()
方法
add()
方法:第二种应用set()
方法
public class ArrayListSample {
public static void main(String[] args) {
ArrayList<String> bookList=new ArrayList<String>();
//add方法1:直接插入元素,默认尾部追加
bookList.add("三国演义");
bookList.add("水浒传");
System.out.println(bookList);
//get方法:
String bookName1=bookList.get(1);
System.out.println(bookName1);
// bookList.get(10); //"Ctrl+/":注释当前行
//add方法2:在指定位置,插入新的数据
bookList.add(1,"红楼梦");
System.out.println(bookList);
//add方法,返回值是bool型
Boolean result1 = bookList.add("西游记");
System.out.println("列表是否发生变化:" + result1);
//set方法:更新设置
String before = bookList.set(3, "西游记后传");
System.out.println(before);
System.out.println(bookList);
}
}
--->
[三国演义, 水浒传]
水浒传
[三国演义, 红楼梦, 水浒传]
列表是否发生变化:true
西游记
[三国演义, 红楼梦, 水浒传, 西游记后传]
除了get()
方法,上述的add(int,Object)
方法也会出现如下错误:数组下标越界:IndexOutOfBoundsException
异常
原因:向不连续的位置,添加元素。如下:
底层原理:ArrayList底层是数组。它会对元素的下标(index)和当前集合的元素数量(size)进行对比,如果下标比元素数量多,就会被认为是不合法。
应对措施:在添加元素之前,先判断下标是否超出范围。如下:
remove()
方法size()
方法
public class ArrayListSample {
public static void main(String[] args) {
ArrayList<String> bookList=new ArrayList<String>();
bookList:[三国演义, 红楼梦, 水浒传, 西游记后传]
//remove的用法1:直接删除对象,返回的是bool值
boolean result2 = bookList.remove("西游记后传");
System.out.println(result2);
System.out.println(bookList);
//remove的用法2:删除某索引位置上的元素,返回的是被删除的元素
String item = bookList.remove(0);//传入的是具体的索引位置
System.out.println(item);
System.out.println(bookList);
//size:求元素的数量
int count = bookList.size();
System.out.println(count);
//包含size方法的复合方式
bookList.set(bookList.size() - 1, "测试数据"); //与set更新方法相结合
System.out.println(bookList);
bookList.remove(bookList.size() - 1); //与remove移除方法想结合
System.out.println(bookList);
}
}
--->
true
[三国演义, 红楼梦, 水浒传] //用法1:移除元素成功
三国演义
[红楼梦, 水浒传] //用法2:移除元素成功
2
[红楼梦, 测试数据]
[红楼梦]
2.3 LinkedList
实现类的特点及方法应用
1.特点:
- 实现了Deque和List两个接口;
- 在元素有序、允许重复的基础上,可以作为队列在队首、队尾快速追加数据
- 底层是链表,所以数据的存储是分散的。数据插入速度快,但是数据访问速度慢
2.方法应用
从使用角度,与ArrayList
实现类的方法应用基本相同。
public class LinkedListSample {
public static void main(String[] args) {
LinkedList<String> bookList = new LinkedList<String>();
bookList.add("三国演义");
bookList.add(0,"水浒传"); //"Ctrl+D":复制当前行
bookList.add("西游记");
bookList.add("红楼梦");
System.out.println(bookList);
bookList.addFirst("蒸汽革命");
bookList.addLast("黄金时代");
System.out.println(bookList);
}
}
--->
[水浒传, 三国演义, 西游记, 红楼梦]
[蒸汽革命, 水浒传, 三国演义, 西游记, 红楼梦, 黄金时代]
通过查看源码,追溯上述方法的源头:
按住Ctrtl+鼠标点击想要查看源码的类:
但是,从底层的数据存储上,却是大不相同。
3.ArrayList
与LinkedList
实现类的存储结构对比
ArrayList
实现类:本质是数组
特点:物理存储连续紧密---->数据的访问读取速度非常快
适用场景:数据比较稳定,需要大量的数据读取
LinkedList
实现类:本质是链表
特点:物理存储分散,中间用指针来连接---->数据的插入新增速度非常快
适用场景:频繁的数据插入
2.3 通过三种不同的遍历方式获取集合中的数据
1.增强型for循环
public class ListLoopSample {
public static void main(String[] args) {
List<String> bookList=new ArrayList<String>(); //将接口的引用,指向接口的实现类
bookList.add("三国演义");
bookList.add("水浒传");
bookList.add("西游记");
bookList.add("红楼梦"); //对象的初始化:添加数据
for(String book:bookList){ //遍历方式1
System.out.println(book);
}
}
}
--->
三国演义
水浒传
西游记
红楼梦
2.forEach方法遍历
利用对象自带的forEach方法+配合基于JDK1.8中的lambda表达式
写法简洁,用的最多。
public class ListLoopSample {
public static void main(String[] args) {
List<String> bookList=new ArrayList<String>();
bookList.add("三国演义");
bookList.add("水浒传");
bookList.add("西游记");
bookList.add("红楼梦");
bookList.forEach(book->{ //遍历方式2
System.out.println(book);
});
}
}
--->
三国演义
水浒传
西游记
红楼梦
3.Iterator迭代器对象遍历
目前用的越来越少。
迭代器本身的使用是一次性的。如果需要多次使用迭代器对集合进行遍历,那么每次都得需要重新获取迭代器对象。
public class ListLoopSample {
public static void main(String[] args) {
List<String> bookList=new ArrayList<String>();
bookList.add("三国演义");
bookList.add("水浒传");
bookList.add("西游记");
bookList.add("红楼梦");
Iterator<String> itr = bookList.iterator(); //指针一开始指向第一个元素的前面 //遍历方式3
while(itr.hasNext()){ //是否还有下一个元素
String book=itr.next(); //获取下一个元素,同时将指针向后移动
System.out.println(book);
}
}
}
--->
三国演义
水浒传
西游记
红楼梦
三、Set集合体系及应用
3.1 Set接口
Set集合:元素无序、不可重复的集合
Set集合与List集合使用方法基本相同
两种最常见的实现类:HashSet与TreeSet
Set集合的体系图示:
3.2 HashSet
实现类的特点及方法应用
public class HashSetSample {
public static void main(String[] args) {
Set<String> mobileSet=new HashSet<String>(); //前面是接口,后面是实现类
mobileSet.add("13311112222");
mobileSet.add("13333334444");
mobileSet.add("13355556666");
System.out.println(mobileSet);
boolean isChanged=mobileSet.add("13377778888");
System.out.println("Set集合是否发生改变:"+isChanged);
isChanged=mobileSet.add("13377778888");
System.out.println("Set集合是否发生改变:"+isChanged); //对于重复的数据,二次写入会失败
System.out.println(mobileSet);
//Set集合可以使用所有Collection接口定义的方法
int count = mobileSet.size();
boolean result = mobileSet.contains("13377778888");
System.out.println(result);
}
}
--->
[13333334444, 13355556666, 13311112222] //证明:Set集合是元素无序的
Set集合是否发生改变:true
Set集合是否发生改变:false //证明:Set集合是不允许重复的
[13333334444, 13355556666, 13377778888, 13311112222]
true
源码查看:
因为是元素无序,所以,所有通过下标索引操作数据的方法,都是不支持的。
也就是说,get等索引获取数据的方法属于List接口,因此Set实现类无法使用。
3.3 Set集合的数据的唯一性
1.Set集合如何确保数据的唯一性?
Set集合在新增数据时,先判断数据的hashCode( )是否存在,
若hashCode( )在Set集合存在,再调用equals( )进行比较;
若hashCode( )和equals( )都存在的情况下,Set集合才认为数据已存在,不予新增。
2.为什么要用对象的hashCode( ),直接用equals( )判断不行吗?
出于执行效率考虑。
hashCode( )返回的整数结果,决定了Set集合中的存放位置,hashCode( )计算速度很快(可能出现哈希碰撞);
equals( )则是对值进行比较,处理速度相对较慢。
每个实现类,都有HashCode方法。类不同,方法也不一样。
3.4 HashSet与TreeSet的存储原理
1.HashSet
(1)特点:
- HashSet是Set接口的典型实现,使用最多
- HashSet按照Hash算法来决定集合元素的顺序,具有很好的查找性能
- 存储位置由对象的hashCode散列值来决定
(2)Hash的概念:
哈希、散列、杂凑,目的是把任意长度的数据,通过哈希算法变换成固定的输出。该输出就是散列值。
int hash=(h="a".hashCode())^(h>>>16);
--->
97
(3)HashSet示例:
Set set=new HashSet();
set.add("d");
set.add("a");
set.add("b");
set.add("c");
System.out.println(set);
--->
[a,b,c,d] //按照Hash算法来决定集合元素的顺序,
底层原理:只介绍HashSet存储顺序的逻辑:
底层有个算法,通过不同的散列值,就能得到稳定的数组的存放位置。
2.LinkedHashSet:
- 是HashSet的子类。除了具有HashSet的特性外,同时使用链表维护元素的次序,以保障按照插入顺序来提取数据。
3.TreeSet:
- 是SortedSet接口的实现类,用于保证集合元素处于排序状态
- 利用红黑树的数据结构,来存储集合元素
- 默认采用自然排序,对集合元素升序排列,也可以实现Comparable接口自定义排序方法
3.5 HashSet与TreeSet的代码应用
1.HashSet
案例演示LinkedHashSet按照我们插入时的顺序,对Set集合中的数据进行提取
public class LinkedHashSetSample {
public static void main(String[] args) {
//案例演示LinkedHashSet按照我们插入时的顺序,对Set集合中的数据进行提取
Set<String> mobileSet=new LinkedHashSet<String>();
mobileSet.add("13311112222");
mobileSet.add("13333334444");
mobileSet.add("13355556666");
mobileSet.add("13377778888");
System.out.println(mobileSet);
}
}
--->
[13311112222, 13333334444, 13355556666, 13377778888]
2.TreeSet
- 案例演示TreeSet默认按照对象的字面数值、文本的字面数值,来排序
public class TreeSetSample {
public static void main(String[] args) {
//案例演示TreeSet按照对象的字面数值、文本的字面数值,来排序
Set<Integer> set = new TreeSet<Integer>();//Integer通过包装类的形式,保存整型数据
set.add(100);
set.add(140);
set.add(180);
set.add(200);
System.out.println(set);
}
}
--->
[100, 140, 180, 200] //TreeSet默认采用自然排序
- 当然,案例演示TreeSet如何按照自定义规则进行排序
public class TreeSetSample {
//新建一个内部类,重写比较方法,自定义比较规则
class IntegerComparator implements Comparator<Integer>{
@Override
public int compare(Integer o1, Integer o2) { //通过上面Comparator接口,来提醒程序员这里必须重写接口中的抽象方法
return o2-o1; //降序排列
}
}
public void sort(){
Set<Integer> set = new TreeSet(new IntegerComparator()); //构造方法中传入实例
set.add(100);
set.add(140);
set.add(180);
set.add(200);
System.out.println(set);
}
public static void main(String[] args) {
//案例演示TreeSet如何按照自定义规则进行排序
new TreeSetSample().sort();
}
}
--->
[200, 180, 140, 100] //TreeSet成功的自定义规则进行排序
四、Map集合体系及其应用
以上讲的List和Set集合,存储的往往是单个数据本身。但是,在生活中,除了要存取数据本身,还要存取数据的含义。
生活中的映射:数据-数据对应的含义
如果放在java语言中,如何表达这种映射关系呢?
Map映射,应运而生。
有点像python中的字典 :)
4.1 初识Map接口及Hash Map实现类的常用方法
1.Map映射的特点
(1)Map用于保存具有映射关系的数据。每组映射都是Key(键)与Value(值)组合而成。
叫法:键值对,或kv
Map就像一整张登记表,一个Map中包含很多个不同的键值对
(2)键值对 可以是任意数据类型,但是Key通常时String
(3)key是全局唯一、不可重复.一旦重复,后者覆盖前者。
2.Map体系
最常用的是HashMap。
- HashMap是Map接口的典型实现类,它保证对Key进行无序、唯一的存储
- 本质就是Hash对Key进行算法处理,换算成固定的输出
3.HashMap与HashSet的关系与区别:
Java先有Map后有Set,HashSet从HashMap精简而来。正是这样的设计,会发现两者是高度相似的。
- HashMap
HashSet
可以理解为,下面传入的只是键值对的Key,而PRESENT其实是个占位符,一个固定的静态常量。
4.2 Map实现类:HashMap与LinkedMap的区别
HashMap
实现类是Map映射体系下,使用最多的实现类。
1.HashMap
实现类的使用方法:
import java.util.HashMap;
//前面写了泛型,后面就不用写了
HashMap<String,Object> student=new HashMap(); //value的类型各种各样,处于兼容性需要,用远古父类Object
student.put("name","张三");
String name=(String)student.put("name","李四"); //因为put方法的返回值是被覆盖的value,是Object类型,所以要强制类型转换
System.out.println(name+"已被替换为李四");
student.put("age",18);
student.put("height",182);
student.put("weight",60);
System.out.println(student);
System.out.println("当前kv的数量:"+student.size());
2.HashMap的案例演示:
public class HashMapSample {
public static void main(String[] args) {
//HashMap<String,Object> student=new HashMap<String,Object>();//标准写法,麻烦
//HashMap<String,Object> student=new HashMap<>(); //泛型可以只写左边,右边可以省略
HashMap<String,Object> student=new HashMap(); // 更简洁、易懂的写法
//1.通过put方法,向Map中放入键值对
student.put("name", "张三");
//多次为同一个Key赋值,新的Value会覆盖旧Value,同时将旧Value返回
String name = (String) student.put("name", "李四"); //先进行赋值修改,再返回
System.out.println(name+"已被替换为李四");
//Map可以存储多组键值对,且Value可以是不同类型
student.put("age",18); //虽然这里是整型,但是根据<String,Object>,键值对都是对象形式。即基本数据类型,会自动变为包装类
student.put("height",182);
student.put("weight",60);
System.out.println(student);
//2.使用get方法,获取指定Key的Value
String n = (String) student.get("name"); //纯粹的进行查询
System.out.println(n);
//3.是否包含该Key?
boolean k1 = student.containsKey("name");
System.out.println(k1);
boolean k2 = student.containsKey("name2");
System.out.println(k2);
//是否包含该value?
boolean v1 = student.containsValue(60);
System.out.println(v1);
boolean v2 = student.containsValue(61);
System.out.println(v2);
//4.当前键值对的总数是?
int count = student.size();
System.out.println(count);
//5.不想要某个key了?
Integer w = (Integer) student.remove("weight");
System.out.println("weight项已被移除,其value值为:"+w);
System.out.println(student); //将整个HashMap,打印出来
}
}
--->
张三已被替换为李四
{name=李四, weight=60, age=18, height=182} //无序
李四
true
false
true
false
4
weight项已被移除,其value值为:60
{name=李四, age=18, height=182}
3.LinkedMap的案例演示:
与上面标准的HashMap使用,基本一样。只是增加对于链表特性的实现。以保证提取数据显示的顺序与放入的顺序保持一致。
- 查看下源码:
- 代码
public class LinkedHashMapSample {
public static void main(String[] args) {
LinkedHashMap<String,Object> student=new LinkedHashMap<>();
student.put("name", "张三");
student.put("age",18);
student.put("height",182);
student.put("weight",60);
System.out.println(student);
}
}
--->
{name=张三, age=18, height=182, weight=60} //提取数据显示的顺序与放入的循序保持一致
4.实际开发应用中,在多个子接口引用指向实现类时,左边往往会简化为只写父类接口:
不管是HashMap,还是LinkedHashMap:
体现多态性:
public class LinkedHashMapSample {
public static void main(String[] args) {
// LinkedHashMap<String,Object> student=new LinkedHashMap<>();
Map<String,Object> student=new LinkedHashMap<>(); //左边简化为使用父类接口Map
4.3 Map实现类:初识TreeMap
1.TreeMap特点:
- TreeMap在存储Key-Value时,可以根据Key节点进行排序
- TreeMap支持两种排序:自然排序、定制排序
- 与TreeSet相同,TreeMap也是基于红黑树结构,对数据进行排序
其中,自然排序就是按照数字或字母顺序,升序排序
2.案例演示
默认的自然排序
public class TreeMapSample { public static void main(String[] args){ Map<String,Object> record=new TreeMap<>(); record.put("A1", "1"); record.put("C3", "2"); record.put("B5", "3"); record.put("X1", "4"); record.put("C1", "5"); record.put("B1", "6"); System.out.println(record); } } ---> {A1=1, B1=6, B5=3, C1=5, C3=2, X1=4} //默认的自然排序
- 成功实现了降序排列
public class TreeMapSample {
//新定义一个内部类,实现Comparator接口,以重写比较规则
class RecordComparator implements Comparator<String> {//这里是String,因为比较的是Map<String,Object> record中的Key类型
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1); //因为比较双方是字符串,所以使用compareTo方法 //降序排列
}
}
//新定义一个实例方法,将添加元素和打印元素的逻辑封装进去
public void sort(){
Map<String,Object> record=new TreeMap(new RecordComparator());//将对Key降序排列的内部类对象,放进构造方法中
record.put("A1", "1");
record.put("C3", "2");
record.put("B5", "3");
record.put("X1", "4");
record.put("C1", "5");
record.put("B1", "6");
System.out.println(record);
}
public static void main(String[] args) {
TreeMapSample sample=new TreeMapSample();
sample.sort();
}
}
--->
{X1=4, C3=2, C1=5, B5=3, B1=6, A1=1} //成功实现了降序排列
4.4 Map集合的三种遍历方式
HashMap----------------乱序
LinkedHashMap--------按照插入顺序
TreeMap------------------自定义顺序
将集合中的数据全部一个一个打印出来,而不是输出整个集合形式:
与List遍历方式相同,也有三种遍历方式。
1.增强型for循环
public class LoopSample {
//新定义一个实例方法,将for循环遍历的逻辑封装进去
public void doForLoop(Map map){ //将map对象传入
Set<String> keys = map.keySet(); //通过内置的keySet方法,得到Key的Set集合
for(String k:keys){
System.out.println(k+":"+map.get(k));
}
}
public static void main(String[] args) {
Map<String,Object> student=new LinkedHashMap<>(); //student:是包含很多键值对的Map映射集合的对象
student.put("name", "张三");
student.put("age",18);
student.put("height",182);
student.put("weight",60);
System.out.println(student);
LoopSample loopSample = new LoopSample();
loopSample.doForLoop(student);
}
}
--->
{name=张三, age=18, height=182, weight=60}
name:张三
age:18
height:182
weight:60
2.forEach方法遍历
利用对象自带的forEach方法+配合基于JDK1.8中的lambda表达式
写法简洁,可读性好,用的最多。
public class LoopSample {
//新定义一个实例方法,将forEach遍历的逻辑封装进去
public void doForEach(Map map){
map.forEach((key,value)->{
System.out.println(key+":"+value);
});
}
public static void main(String[] args) {
Map<String,Object> student=new LinkedHashMap<>(); //student:是包含很多键值对的Map映射集合的对象
student.put("name", "张三");
student.put("age",18);
student.put("height",182);
student.put("weight",60);
System.out.println(student);
LoopSample loopSample = new LoopSample();
loopSample.doForEach(student);
}
}
--->
{name=张三, age=18, height=182, weight=60}
name:张三
age:18
height:182
weight:60
3.Iterator迭代器对象遍历
最麻烦,早期版本多。
public class LoopSample {
//新定义一个实例方法,将Iterator迭代器对象遍历的逻辑封装进去
public void doIterator(Map map){
Iterator<Map.Entry<String, Object>> itr = map.entrySet().iterator(); //每一组键值对,称为entry对象
while(itr.hasNext()){
Map.Entry<String, Object> entry = itr.next();
System.out.println(entry.getKey()+":"+entry.getValue());
}
}
public static void main(String[] args) {
Map<String,Object> student=new LinkedHashMap<>();//student:是包含很多键值对的Map映射集合的对象
student.put("name", "张三");
student.put("age",18);
student.put("height",182);
student.put("weight",60);
System.out.println(student);
LoopSample loopSample = new LoopSample();
loopSample.doIterator(student);
}
}
--->
name:张三
age:18
height:182
weight:60
五、应用类Collections实现集合排序
5.1 类Collections实现集合排序
- 已有的集合List
public class ListSorter {
public static void main(String[] args) {
List<Integer> list=new ArrayList();
list.add(70);
list.add(90);
list.add(30);
list.add(50);
System.out.println(list);
}
}
--->
[70, 90, 30, 50]
- 通过
Collections.sort()
方法,可以对已有的List进行重排序
public class ListSorter {
//定义一个实例方法,用于对list集合进行排序
public List<Integer> sort(List<Integer> list){
Collections.sort(list); //是对list内部进行加工排列,并不会产生新的list
System.out.println(list);
return list; //这个list就是传入的参数list
}
public static void main(String[] args) {
List<Integer> list=new ArrayList<>();
list.add(70);
list.add(90);
list.add(30);
list.add(50);
System.out.println(list);
ListSorter listSorter=new ListSorter();
List<Integer> list1 = listSorter.sort(list); //调用实例方法
System.out.println(list);
System.out.println(list1);
}
}
--->
[70, 90, 30, 50]
[30, 50, 70, 90]
[30, 50, 70, 90] //原始的list
[30, 50, 70, 90] //现在的list,与上述原始的list一样。
这说明,工具类Collections.sort()
方法只是对原始的集合进的内部调整,并不会产生新的list
- Comparator接口的实现自定义比较的逻辑
public class ListSorter {
//新定义一个内部类,用于包含比较集合的逻辑
class SampleComparator implements Comparator<Integer>{
//70 90 30 50
//结果>0,则交换位置‘
//结果=0或<0,则位置不变
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1; //降序排列
}
}
//在实例方法中,将上述包含比较逻辑的内部类的对象,传进来
public List<Integer> sort(List<Integer> list){
Collections.sort(list,new SampleComparator()); //传入比较器
System.out.println(list);
return list;
}
public static void main(String[] args) {
List<Integer> list=new ArrayList<>();
list.add(70);
list.add(90);
list.add(30);
list.add(50);
System.out.println(list);
ListSorter listSorter=new ListSorter();
List<Integer> list1 = listSorter.sort(list);
System.out.println(list);
System.out.println(list1);
}
}
--->
[70, 90, 30, 50]
[90, 70, 50, 30] //成功实现了降序排列
[90, 70, 50, 30]
[90, 70, 50, 30]
5.2 自定义类的集合排序
上述是基于基础数据类型的排序,如果遇到自定义类的集合呢?
(1)实现的接口Comparator后面的泛型,必须是自定义类
(2)指定排序规则时,需要先获得具体的属性,然后对属性进行比较
//自定义的商品类
public class Goods {
private String sn; //商品的条码系列号
private String title; //商品的标题
public Goods(String sn,String title){
this.sn=sn;
this.title=title;
}
//重写了toString方法,方便程序调试
@Override
public String toString(){
return "Goods{"+"sn='"+sn+'\''+",title='"+title+'\''+'}';
}
public String getSn(){
return sn;
}
public String getTitle(){return title;}
}
public class CustomObjectSortSample {
//新定义一个内部类,实现父类接口,重写父类接口的比较规则
private class CustomComparator implements Comparator<Goods>{ // (1)
@Override
public int compare(Goods o1, Goods o2) {
return o1.getSn().compareTo(o2.getSn()); // (2)
//return o1.getTitle().compareTo(o2.getTitle()); 如果按照商品的题目进行排序,就调用题目的get方法,获得属性值
}
}
//新定义一个实例方法,将比较逻辑封装进去
public List<Goods> sort(List<Goods> list) {
CustomComparator comp=new CustomComparator();
Collections.sort(list, comp); //将集合对象、比较器对象,一起传参进去
return list;
}
public static void main(String[] args) {
List<Goods> goodList=new ArrayList<>(); //注意:新定义的集合,其元素类型是自定义的类Goods的对象
goodList.add(new Goods("18827382190", "商品A"));
goodList.add(new Goods("83219078978", "商品B"));
goodList.add(new Goods("83241781237", "商品C"));
goodList.add(new Goods("18579831426", "商品D"));
System.out.println("排序前:");
System.out.println(goodList);
System.out.println("排序后:");
List<Goods> list = new CustomObjectSortSample().sort(goodList);
System.out.println(list);
}
}
--->
排序前:
[Goods{sn='18827382190',title='商品A'}, Goods{sn='83219078978',title='商品B'}, Goods{sn='83241781237',title='商品C'}, Goods{sn='18579831426',title='商品D'}]
排序后:
[Goods{sn='18579831426',title='商品D'}, Goods{sn='18827382190',title='商品A'}, Goods{sn='83219078978',title='商品B'}, Goods{sn='83241781237',title='商品C'}]
Comments | NOTHING