常见算法、集合进阶、Lambda表达式
七种查找、十大排序
常见算法的API:Arrays
int[] arr = (1, 2, 3, 4, 5,7,8,9, 10);
//toString: 将数组变成字符串
Arrays.toString(arr)
//[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
//binarySearch:二分查找法查找元素
Arrays.binarySearch(arr, 2)//9
Arrays.binarySearch(arr, 2)//1
Arrays.binarySearch(arr, 2)//-11
//如果要查找的元素是存在的,那么返回的是真实的索引
//但是,如果要查找的元素是不存在的,返回的是 -插入点 - 1
//如果要查找数字0,此时0是不存在的,但是按照上面的规则-插入点,应该就是-0
/*copyOf:拷贝数组
参数一:老数组
参数二:新数组的长度
方法的底层会根据第二个参数来创建新的数组
如果新数组的长度是小于老数组的长度,会部分拷贝
如果新数组的长度是等于老数组的长度,会完全拷贝
如果新数组的长度是大于老数组的长度,会补上默认初始值*/
int[] newArr1 = Arrays.copyOf(arr,20);
//CopyOfRange:拷贝数组(指定范围)
//包头不包尾,包左不包右
int[] newArr2 = Arrays .copyOfRange(0, 9);//1~9
//fill:填充数组
Arrays.fill(arr,100);
//sort:排序
Arrays.sort(arr2);
/*compare方法的形式参数:
参数~ o1:表示在无序序列中,遍历得到的每一个元素
参数二 o2:有序序列中的元素*/
//o1 - o2 :升序排列
//o2 - 01 :降序排序
//Integer[] arr = (2, 3, 1, 5,6,7,8, 4, 9};
//内部类之一:
Arrays.sort(arr,new Comparator<Integer>()){
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
}
Lambda表达式
函数式编程思想,忽略面向对象的复杂语法,强调做什么,而不是谁去做
Arrays.sort(arr,(Integer o1,Integer o2)->{
return o1 - o2;
}
);
()-> {
}
/*()对应着方法的形参
->固定格式
{}对应着方法的方法体
注意点:
1.Lambda表达式可以用来简化匿名内部类的书写
2.Lambda表达式只能简化函数式接口的匿名内部类的写法
3.函数式接口:
有且仅有一个抽象方法的接口叫做函数式接口,接口上方可以加@FunctionalInterface注解*/
Lambda表达式的省略写法
Lambda的省略规则:
- 参数类型可以省略不写。
- 如果只有一个参数,参数类型可以省略,同时()也可以省略。
- 如果Lambda表达式的方法体只有一行,大括号,分号,return可以省略不写,需要同时省略。
Arrays.sort(arr,(o1,o2) -> return o1 - o2);
例子:
//字符串的长度进行排序
String[] arr = ("aaaa","aaa","aa");
Arrays.sort(arr,(o1,o2)-> o1.length() - o2.length());
集合进阶
集合体系结构
Collection :单列集合
Map : 双列集合
Collection
Collection
- List
- ArrayList
- LinkedList
- Vector
- Set
- HashSet
- LinkedHashSet
- TreeSet
- HashSet
其中Collection、List、Set为接口,其余为实现类
- List系列集合:添加的元素是有序、可重复、有索引
- Set系列集合:添加的元素是无序、不重复、无索引
Collection是一个接口,我们不能直接创建他的对象。
学习他的方法时,只能创建他实现类的对象实现类:
ArrayList
Collection<String> coll = new ArrayList<>();
/*把给定的对象添加到当前集合中
细节1: 如果我们要往List系列集合中添加数据,那么方法永远返回true,因为List系列的是允许元素重复的
细节2: 如果我们要往Set系列集合中添加数据,如果当前要添加元素不存在,方法返回true,表示添加成功
如果当前要添加的元素已经存在,方法返回false,表示添加失败,因为Set系列的集合不允许重复。*/
boolean add()
void clear() //清空集合中所有的元素
//细节1:因为Collection里面定义的是共性的方法,所以此时不能通过索引进行删除。只能通过元素的对象进行删除。
//细节2: 方法会有一个布尔类型的返回值,删除成功返回true,刚除失败返回false
boolean remove(E e) //把给定的对象在当前集合中删除
/*因为contains方法在底层依赖equals方法判断对象是否一致的。
如果存的是自定义对象,没有重写equals方法,那么默认使用Object类中的equals方法进行判断,而Object类中equals方法,依赖地址值进行判斯
需求: 如果同姓名和同年龄,就认为是同一个学生。
所以,需要在自定义的Javabean类中,重写equals方法就可以了。*/
boolean contains() //判断当前集合中是否包含给定的对象
boolean isEmpty() //判断当前集合是否为空
int size() //返回集合中无素的个数集合的长度
迭代器遍历
遍历集合不依赖索引
Iterator<E> iterator() //返回迭代器对象,默认指向当前集合的0索引
//Iterator中的常用方法:
boolean hasNext() //判断当前位置是否有元素,有元素返回true ,没有元素返回false
E next() //获取当前位置的元素,并将迭代器对象移向下一个位置
//例:
Iterator<String> it = coll.iterator();
while(it.hasNext()){
String str = it.next();
System.out.printIn(str);
}
细节:
- 报错NoSuchElementException
- 迭代器遍历完毕,指针不会复位
- 循环中只能用一次next方法
- 迭代器遍历时,不能用集合的方法进行增加或者删除
coll.remove("bbb"); //报错
it.remove("bbb"); //迭代器自带的方法
增强for遍历
- 增强for的底层就是迭代器,为了简化迭代器的代码书写的
- 它是IDK5之后出现的,其内部原理就是一个iterator选代器
- 所有的单列集合和数组才能用增强for进行遍历
for(数据类型 变量名:集合/数组){
}
//例
for (String s : list) {
System.out.println(s);
}
修改增强for中的变量,不会改变集合中原本的数据。
Lambda表达式遍历
//匿名内部类
coll.forEach(new Consumer<String>(){
@Override
public void accept(String s) {
System.out.printIn(s);
}
})
//lambda表达式
coll.forEach(s -> System.out.printIn(s));
List集合
//List系列集合独有的方法:
void add(int index,E element) //在此集合中的指定位置插入指定的元素,原来的索引上的元素会依次后移
E remove(int index) //删除指定索引处的元素,返回被剧除的元素
E set(int index,E element) //修改指定索引处的元素,返回被修改的元素
E get(int index) //返回指定索引处的元素
五种遍历方式
- 迭代器
- 列表迭代器
- 增强for
- Lambda表达式
- 普通for循环
//迭代器:
Iterator<String> it = list.iterator();
while(it.hasNext()){
String str = it.next();
System.out.println(str);
}
//增强for
for (String s : list) {
System.out.println(s);
}
//Lambda表达式
list,forEach(s->System.out.println(s) );
//普通for
for (int i = @; i < list.size(); i++) {
String s = list.get(i);
System.out.println(s);
}
//列表迭代器
//额外添加了一个方法:遍历时可以添加元素
ListIterator<String> it = list.listIterator();
while(it.hasNext()){
String str = it.next();
System.out.println(str);
}
在遍历的过程中需要删除元素,请使用迭代器。
在遍历的过程中需要添加元素,请使用列表迭代器。
仅仅想遍历,那么使用增强for或Lambda表达式。
如果遍历的时候想操作索引,可以用普通for。
ArrayList源码分析
- 利用空参创建的集合,在底层创建一个默认长度为0的数组
- 添加第一个元素时,底层会创建一个新的长度为10的数组
- 存满时,会扩容1.5倍
- 如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准
size:元素个数以及下次存入位置
LinkedList集合源码分析
底层数据结构是双链表,查询慢,增删快,但是如果操作的是首尾元素,速度也是极快的,所以多了很多首尾操作的特有API。
迭代器源码分析
泛型深入
泛型:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查
泛型的格式:<数据类型>
注意:泛型只能支持引用数据类型
//如果我们没有给集合指定类型,默认认为所有的数据类型都是object类型
//此时可以往集合添加任意的数据类型。
//带来一个坏处:我们在获取数据的时候,无法使用他的特有行为。
/*泛型的好处:
统一数据类型。
把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为在编译阶段类型就能确定下来。*/
细节:
- 泛型中不能写基本数据类型
- 指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类类型
- 如果不写泛型,类型默认是Object
泛型类
当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类
public class ArrayList<E>{
//创建该类对象时,E就是确定类型
}
泛型方法
public <T> void show (T t) {
}
public static<E> void addAll(ArrayList<E> list,E e1,E e2,E e3){
list.add(e1);
list.add(e2);
list.add(e3);
}
public static<E> void addAll(ArrayList<E> list, E...e){
for (E element : e) {
list.add(element);
}
}
泛型接口
如何使用一个带泛型的接口:
- 实现类给出具体类型
- 实现类延续泛型,创建对象时再确定
public interface List<E>{
}
//1.
public class MyArrayList2 implements List<String>{
}
//2.
public class MyArrayList3<E> implements List<E> {
}
泛型的继承和通配符
泛型不具备继承性,但是数据具备继承性
/*泛型的通配符:
?也表示不确定的类型他可以进行类型的限定
? extends E:表示可以传递E或者E所有的子类类型
? super E:表示可以传递E或者E所有的父类类型*/
public static void method(ArrayList<? extends Ye> list){
}
public static void method(ArrayList<? extends Fu> list){
//Fu或者Fu的父类
}
应用场景:
- 如果我们在定义类、方法、接口的时候,如果类型不确定,就可以定义泛型类、泛型方法、泛型接口。
- 如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以泛型的通配符
- 关键点:可以限定类型的范围。
Set
Set集合的实现类
- HashSet:无序、不重复、无索引
- LinkedHashSet: 有序、不重复、无索引
- TreeSet: 可排序、不重复、无索引
Set<String> s = new HashSet<>();
s.add("a");//第一次添加返回true,第二次添加返回false
s.get()
//迭代器遍历
Iterator<String> it = s.iterator();
while (it.hasNext()){
String str = it.next();
System.out.println(str);
}
//增强for
for (String str : s) {
System.out.println(str);
}
// Lambda表达式
s.forEach(str->System.out.println(str));
HashSet
HashSet集合底层采取哈希表存储数据
无序、不重复、无索引
组成:数组+链表+红黑树
哈希值:
- 根据hashCode方法算出来的int类型的整数
- 该方法定义在Obiect类中,所有对象都可以调用,默认使用地址值进行计算
- 一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值
对象的哈希值特点:
- 如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
- 如果已经重写hashCode方法,不同的对象只要属性值相同,计算出的哈希
- 就是一样的在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样。(哈希碰撞)
加载因子:扩容时机
存取顺序不一样
重写hashCode方法和equals方法数据去重
HashSet<Student> hs = new HashSet<>();
LinkedHashSet
有序、不重复、无索引。
原理:底层数据结构是依然哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序。
LinkedHashSet<student> lhs = new LinkedHashSet<>();
去重默认使用HashSet,有序才使用LinkedHashSet
TreeSet
底层是红黑树
TreeSet<Integer> ts = new TreeSet<>();
//排序
/*方式一:
默认的排序规则/自然排序
Javabean类实现Comparable接口,重写里面的抽象方法,再指定比较规则
*/
//this: 表示当前要添加的元素//o: 表示已经在红黑树存在的元素
//返回值:
//负数:表示当前要添加的元素是小的,存左边
//正数:表示当前要添加的元素是大的,存右边
//0 :表示当前要添加的元素已经存在,舍弃
@Override
public int compareTo(Student o) {
System.out.println("-----");
System.out.println("this:"this);
System.out.printIn("o:" + o);
//指定排序的规则
//只看年龄,我想要按照年龄的升序进行排列
return this.getAge() - o.getAge();
}
TreeSet集合默认的规则:
- 对于数值类型:Integer,Double,默认按照从小到大的顺序进行排序
- 对于字符、字符串类型:按照字符在ASCII码表中的数字升序进行排序
/*方式二:
比较器排序: 创建TreeSet对象时候,传递比较器Comparator指定规则
1.创建集合
o1:表示当前要添加的元素
o2: 表示已经在红黑树存在的元素
返回值规则跟之前是一样的
*/
TreeSet<String> ts = new TreeSet<>(new Comparator<String>(){
@Override
public int compare(String o1, String o2) {
// 按照长度排序
int i = o1.length() - o2.length();
//如果一样长则按照首宁母排序
i = i == 0 ? o1.compareTo(o2) : i;
return i;
}
});
//Lambda表达式:
TreeSet<String> ts = new TreeSet<>(o1, o2) ->{
int i = o1.length() - o2.length();
//如果一样长则按照首宁母排序
i = i == 0 ? o1.compareTo(o2) : i;
return i;
}
例:
/*按照总分从高到低输出到控制台
如果总分一样,按照语文成绩排
如果语文一样,按照数学成绩排
如果数学成绩一样,按照英语成绩排
如果英文成绩一样,按照年龄排
如果年龄一样,按照姓名的字母顺序排如果都一样,认为是同一个学生,不存。*/\
@Override
public int compareTo(Student2 o) {
int sum1 = this.getChinese() + this.getMath() + this.getEnglish();
int sum2 = o.getChinese() + o.getMath() + o.getEnglish();
int i = sum1 - sum2;
i = i == 0 ? this.getChinese() - o.getChinese() : i;
i = i == 0 ? this.getMath() - o.getMath() : i;
i = i == 0 ? this.getEnglish() - o.getEnglish() : i;
i = i == 0 ? this.getAge() - o.getAge() : i;
i = i == 0 ? this.getName().compareTo(o.getName()) : i;
return i;
}
总结:
- 如果想要集合中的元素可重复用ArrayList集合,基于数组的。(用的最多)
- 如果想要集合中的元素可重复,而且当前的增删操作明显多于查询用LinkedList集合,基于链表的
- 如果想对集合中的元素去重用HashSet集合,基于哈希表的。(用的最多)
- 如果想对集合中的元素去重,而且保证存取顺序用LinkedHashSet集合,基于哈希表和双链表,效率低于HashSet
- 如果想对集合中的元素进行排序用TreeSet集合,基于红黑树。后续也可以用List集合实现排序
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!