shuijibaobao

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
统计
 

常见算法、集合进阶、Lambda表达式

七种查找、十大排序

常见算法的API:Arrays

img

    int[] arr = (1, 2, 3, 4, 5,78,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的省略规则:

  1. 参数类型可以省略不写。
  2. 如果只有一个参数,参数类型可以省略,同时()也可以省略。
  3. 如果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

其中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);
    }

细节:

  1. 报错NoSuchElementException
  2. 迭代器遍历完毕,指针不会复位
  3. 循环中只能用一次next方法
  4. 迭代器遍历时,不能用集合的方法进行增加或者删除
    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)                 //返回指定索引处的元素

五种遍历方式

  1. 迭代器
  2. 列表迭代器
  3. 增强for
  4. Lambda表达式
  5. 普通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源码分析

  1. 利用空参创建的集合,在底层创建一个默认长度为0的数组
  2. 添加第一个元素时,底层会创建一个新的长度为10的数组
  3. 存满时,会扩容1.5倍
  4. 如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准

size:元素个数以及下次存入位置

LinkedList集合源码分析

底层数据结构是双链表,查询慢,增删快,但是如果操作的是首尾元素,速度也是极快的,所以多了很多首尾操作的特有API。

迭代器源码分析

泛型深入

泛型:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查
泛型的格式:<数据类型>
注意:泛型只能支持引用数据类型

//如果我们没有给集合指定类型,默认认为所有的数据类型都是object类型
//此时可以往集合添加任意的数据类型。
//带来一个坏处:我们在获取数据的时候,无法使用他的特有行为。

/*泛型的好处:
统一数据类型。
把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为在编译阶段类型就能确定下来。*/

细节:

  1. 泛型中不能写基本数据类型
  2. 指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类类型
  3. 如果不写泛型,类型默认是Object

泛型类

当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类

    public class ArrayList<E>{
        //创建该类对象时,E就是确定类型
    }

img

泛型方法

    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);
        }
    }

泛型接口

如何使用一个带泛型的接口:

  1. 实现类给出具体类型
  2. 实现类延续泛型,创建对象时再确定
    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的父类
    }

应用场景:

  1. 如果我们在定义类、方法、接口的时候,如果类型不确定,就可以定义泛型类、泛型方法、泛型接口。
  2. 如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以泛型的通配符
  3. 关键点:可以限定类型的范围。

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;
    }

总结:

  1. 如果想要集合中的元素可重复用ArrayList集合,基于数组的。(用的最多)
  2. 如果想要集合中的元素可重复,而且当前的增删操作明显多于查询用LinkedList集合,基于链表的
  3. 如果想对集合中的元素去重用HashSet集合,基于哈希表的。(用的最多)
  4. 如果想对集合中的元素去重,而且保证存取顺序用LinkedHashSet集合,基于哈希表和双链表,效率低于HashSet
  5. 如果想对集合中的元素进行排序用TreeSet集合,基于红黑树。后续也可以用List集合实现排序
posted on   水吉z  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
 
点击右上角即可分享
微信分享提示