集合框架(Java)

一、Collection(单列集合)

1、Collection接口的体系框架

List系列集合:添加的元素是有序、可重复、有索引

Set系列集合:添加的元素是无序、不重复、无索引

2、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()返回集合中元素的个数/集合的长度
import java.util.ArrayList;
import java.util.Collection;
​
public class CollectionDemo2 {
    /*
    * | public boolean add(E e)             | 把给定的对象添加到当前集合中     |
    * | ----------------------------------- | -------------------------------- |
    * | public void clear()                 | 清空集合中所有的元素             |
    * | public boolean remove(E e)          | 把给定的对象在当前集合中删除     |
    * | public boolean contains(Object obj) | 判断当前集合中是否包含给定的对象 |
    * | public boolean isEmpty()            | 判断当前集合是否为空             |
    * | public int size()                   | 返回集合中元素的个数/集合的长度  |
    *
    * 注意:
    * Collection是一个接口,我们不能直接创建它的对象。因此,现在我们学习它的方法时,只能创建它实现类的对象。
    * 实现类:ArrayList
    * */
    public static void main(String[] args) {
        Collection<String> coll = new ArrayList<>();
​
        //1、添加元素
        //细节1:如果我们要往List系列集合中添加数据,那么方法永远返回true,因为List系列的是允许元素重复的。
        //细节2:如果我们往Set系列集合中添加数据,如果当前添加元素不存在,方法返回true,表示添加成功。
        //                                  如果当前要添加的元素已经存在,方法返回false,表示添加失败。
        //                                  因为Set系列集合不允许重复
        coll.add("aaa");
        coll.add("bbb");
        coll.add("ccc");
        System.out.println(coll);
​
        //3.删除
        //细节1:因为Collection里面定义的是共性的方法,所以此时不能通过索引进行删除。只能通过元素的对象进行删除。
        //细节2:方法会有一个布尔类型的返回值,删除成功返回true,删除失败返回false.如果要删除的元素不存在,就会删除失败
        coll.remove("aaa");
        System.out.println(coll);
​
        //4.判断元素是否包含
        //细节:底层是依赖equals方法进行判断是否存在。
        //所以,如果集合中存储的是自定义对象,也是通过contains方法来判断是否包含,那么javabean类中,一定要重写equals方法
        boolean contains = coll.contains("aaa");
        System.out.println(contains);
​
        //5.判断集合是否为空
        boolean empty1 = coll.isEmpty();
        System.out.println(empty1);
​
        //6.获取集合长度
        int size = coll.size();
        System.out.println(size);
​
        //2.清空
        coll.clear();
​
        System.out.println(coll);
​
        //5.判断集合是否为空
        boolean empty = coll.isEmpty();
        System.out.println(empty);
​
    }
}
运行结果:
[aaa, bbb, ccc]
[bbb, ccc]
false
false
2
[]
true

public boolean contains(Object obj)中的细节:

底层是依赖equals方法进行判断是否存在。

import com.collection.exer.collection.bean.Student;
​
import java.util.ArrayList;
import java.util.Collection;
​
public class CollectionDemo3 {
    public static void main(String[] args) {
        //1.创建集合的对象
        Collection<Student> coll = new ArrayList<>();
        //2.创建三个学生对象
        Student s1 = new Student("张三", 23);
        Student s2 = new Student("李四", 24);
        Student s3 = new Student("王五", 26);
        //将学生对象添加到集合当中
        coll.add(s1);
        coll.add(s2);
        coll.add(s3);
        //判断集合中某一个学生对象是否包含
        Student s4 = new Student("张三", 23);
        //因为contains方法在底层依赖equals方法判断对象是否一致的
        //如果存在自定义对象,没有重写equals方法,那么默认使用Object类中的equals方法进行判断,而Object类中equals方法,依赖地址值进行判断。
        //需求:如果同名和同年龄,就认为是同一学生
        //所以,需要在自定义的javaBean类中,重写equals方法就可以了
        System.out.println(coll.contains(s4));
    }
}
​
​
Student类
    import java.util.Objects;
​
public class Student {
    private String name;
    private int age;
​
​
    public Student() {
    }
​
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
​
    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }
​
    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }
​
    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }
​
    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }
​
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }
​
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
​
    @Override
    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}
 
3、Collection系列集合的三种通用遍历的方式

之前的for循环遍历只有List的子类可以进行遍历,因为有索引,但Set的子类没有索引,因此采用以下几种通用遍历方式。

(1)、迭代器遍历

迭代器不依赖索引。迭代器在java中的类是Iterator,迭代器是集合专用的变量方式。在遍历的过程中需要删除元素,请使用迭代器。

Collection集合获取迭代器

方法名称说明
Iterator<E> iterator返回迭代器对象,默认指向当前集合的0索引

Iterator中常用的方法

方法名说明
boolean hasNext()判断当前位置是否有元素,有元素返回true,没有元素返回false
E next()获取当前位置的元素,并将迭代器对象移向下一个位置
default void remove()从底层集合中删除此迭代器返回的最后一个元素
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
​
public class IteratorDemo2 {
    /*
    * Collection系列集合的三种通用的遍历方式:
    *               1、迭代器遍历
    *               2、增强for遍历
    *               3、lambda表达式遍历
    *
    *迭代器遍历的三个方法:
    *               Iterator<E> iterator() :获取一个迭代器对象
    *               boolean hasNext()      :判断当前指向位置是否有元素
    *               E next()               :获取当前位置指向的元素并移动到下一位置
    * */
    public static void main(String[] args) {
        //1、创建集合并添加元素
        Collection<String> c1 = new ArrayList<>();
        c1.add("张三1");
        c1.add("张三2");
        c1.add("张三3");
        c1.add("张三4");
        c1.add("张三5");
        System.out.println(c1);
        //2.获取迭代器对象
        //通过Iterator迭代遍历集合
        Iterator iterator = c1.iterator();
        //3.利用循环不断去获取集合中的每一个元素
        while (iterator.hasNext()){//判断是否为下一个元素
            //条件成立 获取下一个元素 并移动游标位置
            //4.next方法的两件事情:获取元素并移动指针
            Object next = iterator.next();
            System.out.println(next);
        }
    }
}

细节注意点:

1、如果迭代器已经指向了集合的末端,没有元素了,再强行的调用next()方法获取元素,那么就会报错NoSuchElementException.

2、迭代器遍历完毕,指针不会复位

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
​
public class IteratorDemo2 {
    /*
    * Collection系列集合的三种通用的遍历方式:
    *               1、迭代器遍历
    *               2、增强for遍历
    *               3、lambda表达式遍历
    *
    *迭代器遍历的三个方法:
    *               Iterator<E> iterator() :获取一个迭代器对象
    *               boolean hasNext()      :判断当前指向位置是否有元素
    *               E next()               :获取当前位置指向的元素并移动到下一位置
    * */
    public static void main(String[] args) {
        //1、创建集合并添加元素
        Collection<String> c1 = new ArrayList<>();
        c1.add("张三1");
        c1.add("张三2");
        c1.add("张三3");
        c1.add("张三4");
        c1.add("张三5");
        System.out.println(c1);
        //2.获取迭代器对象
        //通过Iterator迭代遍历集合
        Iterator iterator = c1.iterator();
        //3.利用循环不断去获取集合中的每一个元素
        while (iterator.hasNext()){//判断是否为下一个元素
            //条件成立 获取下一个元素 并移动游标位置
            //4.next方法的两件事情:获取元素并移动指针
            Object next = iterator.next();
            System.out.println(next);
        }
        //当上面循环结束之后,迭代器的指针已经指向了最后没有元素的位置
        //System.out.println(iterator.next());//NoSuchElementException
​
        //迭代器遍历完毕,指针不会复位
        System.out.println(iterator.hasNext());
​
        //如果我们要继续第二次遍历,只能再次获取一个迭代器对象
        Iterator<String> it = c1.iterator();
        while (it.hasNext()){
            String s = it.next();
            System.out.println(s);
        }
    }
}

3、循环中只能用一次next方法

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
​
public class IteratorDemo4 {
    /*
    * Collection系列集合的三种通用的遍历方式:
    *               1、迭代器遍历
    *               2、增强for遍历
    *               3、lambda表达式遍历
    *
    *迭代器遍历的三个方法:
    *               Iterator<E> iterator() :获取一个迭代器对象
    *               boolean hasNext()      :判断当前指向位置是否有元素
    *               E next()               :获取当前位置指向的元素并移动到下一位置
    *
    * 迭代器的细节注意点:
    *           1、报错NoSuchElementException
    *           2、迭代器遍历完毕,指针不会复位
    *           3、循环中只能用一次next方法
    *           4、迭代器遍历时,不能用集合的方法进行增加或删除
    * */
    public static void main(String[] args) {
        //1、创建集合并添加元素
        Collection<String> c1 = new ArrayList<>();
        c1.add("张三1");
        c1.add("张三2");
        c1.add("张三3");
        c1.add("张三4");
        c1.add("张三5");
        System.out.println(c1);
        //2.获取迭代器对象
        //通过Iterator迭代遍历集合
        Iterator iterator = c1.iterator();
        //3.利用循环不断去获取集合中的每一个元素
        while (iterator.hasNext()){//判断是否为下一个元素
            //条件成立 获取下一个元素 并移动游标位置
            //4.next方法的两件事情:获取元素并移动指针
            System.out.println(iterator.next());//张三1 张三3 张三5
            System.out.println(iterator.next());//张三2 张三4 NoSuchElementException
        }
​
    }
}

4、迭代器遍历时,不能用集合的方法进行增加或删除

暂时当作一个结论先行记忆,在今天我们会讲解源码详细的再来分析 ​ 如果实在要删除;那么可以用迭代器提供的remove方法进行删除。 ​ 如果要添加,暂时没有办法。

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
​
public class IteratorDemo5 {
    /*
    * Collection系列集合的三种通用的遍历方式:
    *               1、迭代器遍历
    *               2、增强for遍历
    *               3、lambda表达式遍历
    *
    *迭代器遍历的三个方法:
    *               Iterator<E> iterator() :获取一个迭代器对象
    *               boolean hasNext()      :判断当前指向位置是否有元素
    *               E next()               :获取当前位置指向的元素并移动到下一位置
    *
    * 迭代器的细节注意点:
    *           1、报错NoSuchElementException
    *           2、迭代器遍历完毕,指针不会复位
    *           3、循环中只能用一次next方法
    *           4、迭代器遍历时,不能用集合的方法进行增加或删除
    *                   暂时当作一个结论先行记忆,在今天我们会讲解源码详细的再来分析
    *                   如果实在要删除;那么可以用迭代器提供的remove方法进行删除。
    *                   如果要添加,暂时没有办法。
    * */
    public static void main(String[] args) {
        //1、创建集合并添加元素
        Collection<String> c1 = new ArrayList<>();
        c1.add("张三1");
        c1.add("张三2");
        c1.add("张三3");
        c1.add("张三4");
        c1.add("张三5");
        System.out.println(c1);
        //2.获取迭代器对象
        //通过Iterator迭代遍历集合
        Iterator<String> it = c1.iterator();
        //3.利用循环不断去获取集合中的每一个元素
        while (it.hasNext()){//判断是否为下一个元素
            //条件成立 获取下一个元素 并移动游标位置
            //4.next方法的两件事情:获取元素并移动指针
            String str = it.next();//ConcurrentModificationException 并发修改异常
​
            if (str.equals("张三2")) {
                it.remove();
            }
        }
        System.out.println(c1);
​
    }
}
(2)、增强for遍历

增强for的底层就是迭代器,为了简化迭代器的代码书写。它是JDK5以后出现的,其内部原理就是一个Iterator迭代器,所有的单列集合数组才能用增强for进行遍历。

格式:

for(元素的数据类型 变量名:数组或集合){
​
}

增强for的细节

修改增强for中的变量,不会改变集合中原本的数据。

import java.util.ArrayList;
import java.util.Collection;
​
public class ForEachDemo2 {
    /*
     * Collection系列集合的三种通用的遍历方式:
     *               1、迭代器遍历
     *               2、增强for遍历
     *               3、lambda表达式遍历
     *
     * 增强for格式:
     *   for(数据类型 变量名:集合/数组){
     *
     *     }
     * 快速生成方式:
     *          集合名字+for 回车
    * */
    public static void main(String[] args) {
        Collection<String> c1 = new ArrayList();
        c1.add("张三1");
        c1.add("张三2");
        c1.add("张三3");
        c1.add("张三4");
        c1.add("张三5");
        System.out.println(c1);
        //通过foreach循环来实现遍历
        //注意点:
        //str其实就是一个第三方变量,在循环的过程中依次表示集合中的每一个数据
        for (String str:c1) {
            str = "qqqq";//str第三方变量,集合中的数据不会改变
        }
        System.out.println(c1);
    }
}
(3)、Lamdba表达式遍历

得益于JDK8开始的新技术Lambda表达式,提供了一种更简单、更直接的遍历集合的方式。

方法名称说明
default void forEach(Consumer<? super T> action):结合lambda遍历集合

匿名内部类(匿名内部类的对象 )

格式

  new 类名或者接口名(){
​
            重写方法;
​
    }

创建并继承或实现 某类或某接口

Lambda表达式的标准格式

Lambda表达式是JDK8开始后的一种新语法形式。

()->{
​
}
​
 ()    对应着方法的形参
 ->    固定格式
 {}    对应方法的方法体

注意点:

Lambda表达式可以用来简化匿名内部类的书写

Lambda表达式只能简化函数式接口的匿名内部类的写法

函数式接口:有且仅有一个抽象方法的接口叫做函数式接口,接口上方可以加@FunctionalInterface注解

利用匿名内部类的形式去调用下面的方法

调用一个方法的时候,如果方法的形参是一个接口,那么我们要传递这个接口的实现类对象

如果实现类对象只要用到一次,就可以用匿名内部类的形式进行书写

二、List系列集合

1、List集合的特点

有序:存和取的元素顺序一致

有索引:可以通过索引操作元素

可重复:存储的元素可以重复

2、List集合的特有方法

Collection的方法List都继承了

List集合因为有索引,所以多了很多索引操作的方法。

方法名称说明
void add(int index,E element)在此集合中的指定位置插入指定的元素
E remove(int index)删除指定索引处的元素,返回被删除的元素
E set(int index,E element)修改指定索引处的元素,返回被修改的元素
E get(int index)返回指定索引处的元素
import java.util.ArrayList;
import java.util.List;
​
public class ListDemo1 {
​
    public static void main(String[] args) {
        /*
        * | void add(int index,E element) | 在此集合中的指定位置插入指定的元素     |
        * | ----------------------------- | -------------------------------------- |
        * | E remove(int index)           | 删除指定索引处的元素,返回被删除的元素 |
        * | E set(int index,E element)    | 修改指定索引处的元素,返回被修改的元素 |
        * | E get(int index)              | 返回指定索引处的元素                   |
        * */
​
        //1.创建集合
        List<String> list = new ArrayList<>();
​
        //2.添加元素
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        list.add("ddd");
        list.add("eee");
        System.out.println("原集合中的元素"+list);
        //void add(int index,E element) | 在此集合中的指定位置插入指定的元素
        list.add(3,"QQQ");
        System.out.println("添加元素之后的集合元素"+list);
        //E remove(int index)           | 删除指定索引处的元素,返回被删除的元素
        list.remove(1);
        System.out.println("删除索引为1的元素之后的集合元素"+list);
        //E set(int index,E element)    | 修改指定索引处的元素,返回被修改的元素
        String set = list.set(4, "MMMM");
        System.out.println("修改的元素为"+set);
        System.out.println("修改之后的集合元素"+list);
        String s = list.get(3);
        System.out.println("集合中获取到的元素:"+s);
        System.out.println(list);
    }
}

注意:删除的特有细节

import java.util.ArrayList;
import java.util.List;
​
public class ListDemo2 {
​
    public static void main(String[] args) {
        /*
        * List系列集合中的两个删除的方法
        *           1、直接删除元素
        *           2、通过索引进行删除               |
        * */
        //1.创建集合并添加元素
        List<Integer> integers = new ArrayList<>();
​
        integers.add(1);
        integers.add(2);
        integers.add(3);
        integers.add(4);
        integers.add(5);
​
        //2.删除元素
        //请问:此时删除的是1这个元素,还是1索引上的元素?
        //为什么?
        //因为在调用方法的时候,如果方法出现了重载现象
        //优先调用,实参跟形参类型一致的那个方法。
​
        integers.remove(1);
        //手动装箱,手动把基本数据类型的1,变成Integer类型
        Integer i = Integer.valueOf(1);
        integers.remove(i);
​
​
        System.out.println(integers);
    }
}
3、List集合的五种遍历方式

(1)、迭代器遍历

(2)、列表迭代器遍历

(3)、增强for遍历

(4)、Lambda表达式

(5)、普通for循环遍历

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.function.Consumer;
​
public class ListDemo3 {
    /*
    List循环遍历的五种遍历方式:
    * (1)、迭代器遍历
    * (2)、列表迭代器遍历
    * (3)、增强for遍历
    * (4)、Lambda表达式
    * (5)、普通for循环遍历
    *
    * */
    public static void main(String[] args) {
        //创建结合并添加元素
        List<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        list.add("ddd");
        list.add("eee");
​
        //1、迭代器遍历
        //通过list.iterator获取迭代器对象
        System.out.println("迭代器遍历结果为:");
        Iterator<String> it = list.iterator();
        while (it.hasNext()){
            String nextStr = it.next();
            System.out.println(nextStr);
        }
        //2.增强for循环遍历
        //下面的变量s,其实就是一个第三方的变量。
        //在循环的过程中,依次表示集合中的每一个元素
        System.out.println("通过增强for循环遍历的集合:");
        for (String forStr : list) {
            System.out.println(forStr);
        }
        //3.Lambda表达式
        //forEach方法的底层其实就是一个遍历,依次得到集合中的每一个元素
        //并把每一个元素传递给下面的accept方法
        //accept方法的形参s,依次表示集合中的每一个元素
        System.out.println("内部类的方式:");
        list.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
        System.out.println("Lambda表达式:");
        list.forEach(s -> System.out.println(s));
​
        //4.普通for进行遍历
        //size方法跟get方法还有循环结合的方式,利用索引获取到集合中的每一个元素
        System.out.println("通过普通for循环进行遍历:");
        for (int i = 0; i < list.size(); i++) {
            //i:依次表示集合中的每一个索引
            String s = list.get(i);
            System.out.println(s);
        }
​
        //5.列表迭代器
        //获取列表迭代器的对象,里面的指针也是默认也是指向0索引的
​
        //额外添加了一个方法:在遍历过程中,可以添加元素
        ListIterator<String> strList = list.listIterator();
        while (strList.hasNext()){
            String str = strList.next();
            if (str.equals("bbb")) {
                strList.add("QQQ");
            }
        }
        System.out.println(list);
    }
}
4、五种遍历方式对比
遍历名称需求
迭代器遍历在遍历的过程中需要删除元素,请使用迭代器。
列表迭代器在遍历的过程中需要添加元素,请使用列表迭代器。
增强for遍历/Lambda表达式仅仅想遍历,那么使用增强for或Lambda表达式。
普通for如果遍历的时候像操作索引,可以用普通for。

(一)、数据结构(栈、队列、数组、链表)

1、数据结构概述

数据结构是计算机底层存储,组织数据的方式。

是指数据相互之间是以什么方式排列在一起的。

数据结构是为了更加方便的管理和使用数据,需要结合具体业务场景来进行选择。

一般情况下,精心选择的数据结构可以带来更高的运行或存储效率。

2、常见的数据结构

栈、队列、数组、链表、二叉树、二叉查找树、平衡二叉树、红黑树

(1)、栈

特点:后进先出,先进后出

数据进入到栈模型的过程称为:压/进栈

数据离开栈模型的过程称为:弹/出栈

(2)、队列

特点:先进先出,后进后出

数据从后端进入队列模型的过程称为:入队列

数据从前端离开队列模型的过程称为:出队列

(3)、数组

特点(查询快,增删慢):

查询速度快:查询数据通过地址值和索引定位,查询任意数据耗时相同。(元素在内存中是连续存储的)

删除效率低:要将原始数据删除,同时后面每个数据前移。

添加效率极低:添加位置后的每个元素后移,再添加元素。

(4)、链表

结点:链表中的每一个元素称之为结点。

特点(查询慢,增删快):

链表中的结点是独立的对象,在内存中不是连续的,每个结点包含数据值和下一个结点的地址。

增删相对较快

(二)、ArrayList集合

ArrayList集合底层原理

(1)、利用空参创建的集合,在底层创建一个默认长度为0的数组(elementData)

(2)、添加第一个元素时,底层会创建一个新的长度为10的数组(size表示集合元素的个数,也表示下次存入位置)

(3)、存满时,会扩容为原来的1.5倍

(4)、如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准

(三)、LinkedList集合

特有方法说明
public void addFirst(E e)在该列开头插入指定的元素
public void addLast(E e)将指定的元素追加到此列表的末尾
public E getFirst()返回此列表的第一个元素
public E getLast()返回此列表中的最后一个元素
public E removeFrist()从此列表中删除并返回第一个元素
public E removeLast()从此列表中删除并返回最后一个元素
LinkedList集合底层原理

底层数据结构是双链表,查询慢,增删快,但是如果操作的是首尾元素,速度也是极好的。

如何避免并发修改异常

在使用迭代器或者增强for遍历集合的过程中,不要使用集合的方法去添加或删除元素即可

(四)、泛型深入

1、泛型

是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。

泛型的格式:<数据类型>

注意:泛型只能支持引用数据类型

没有泛型的时候,集合如何存储数据?

如果没有给集合指定类型,默认认为所有的数据类型都是Object类型,此时可以往集合添加任意的数据类型。

带来一个坏处:我们在获取数据的时候,无法使用其特有行为。

泛型可以在添加数据的时候就把类型进行统一,而且在获取数据时,也可以省略强转了,非常方便

泛型的好处:

统一数据类型

把运行时期的问题提前到了编译期间,避免了强制转换可能出现的异常,因为在编译阶段就能确定下来。

扩展知识点:Java中的泛型是伪泛型

泛型的细节:

  • 泛型中不能写基本数据类型

  • 指定泛型的具体类型后,传递数据时,可以传入该类型或者其子类型

  • 如果不写泛型,类型默认时Object

2、泛型类(类后面)

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

格式:

修饰符 class 类名<类型>{
​
}

类型中可以写成:T、E、K、V等。类型可以理解为变量,但是不是用来记录是数据的,而是记录数据的类型

import java.util.Arrays;
​
public class MyArrayList<E> {
    /*
    * 当编写一个类时,如果不确定类型,那么这个类就可以定义为泛型类。
    * */
     Object[] obj = new Object[10];
     int size;
     /*
     * E:表示不确定的类型。该类型在类名后面已经定义过了。
     * e:形参的名字,变量名
     *
     * */
     public boolean add(E e){
          obj[size] = e;
          size++;
          return true;
     }
​
     public E get(int index){
          return (E)obj[index];
     }
​
     @Override
     public String toString() {
          return Arrays.toString(obj);
     }
}
3、泛型方法(方法上面)

方法中形参类型不确定时,可以使用类名后面定义的泛型<E>(所有方法都能使用),也可以在方法申明上定义自己的泛型(只能本方法能使用)。

格式:

修饰符 <类型> 返回值类型 方法名(类型 变量名){
​
}

注意:类型可以了理解为变量,但是不是用来记录数据的,而是记录类型的,可以写成:T、E、K、V等

import java.util.ArrayList;

public class ListUtil { private ListUtil(){}

/*
* 类中定义一个静态方法addAll,用来添加多个集合的元素。
* */
​
/*
* 参数一:集合
* 参数二:最后要添加的元素
* */
public static<E> void addAll(ArrayList<E> list,E e1,E e2,E e3,E e4){
    list.add(e1);
    list.add(e2);
    list.add(e3);
    list.add(e4);
}
/*public static<E> void addAll1(ArrayList<E> list,E...e){
    for (E element : e){
        list.add(element);
    }
}*/
​
public void show(){
    System.out.println("最爱吃兽奶!!!!");
    }
}
4、泛型接口(接口后面)

格式:

修饰符 interface 接口名<类型>{
​
}

重点:

如何使用带泛型的接口

方式1:实现类给出具体类型

方式2:实现类延续泛型,创建对象时再确定类型

5、泛型的继承和通配符

泛型不具备继承性,但是数据具备继承性

泛型的通配符

?也表示不确定类型,它可以进行类型的限定

?extends E: 表示可以传递E或者E所有的子类类型

?super E:表示可以传递E或E所以的父类类型

应用场景

如果我们定义的类、方法、接口的时候,如果类型不确定,就可以定义泛型类、泛型方法、泛型接口。

如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以使用泛型的通配符

泛型的通配符:

关键点:可以限定类型的范围。

import java.util.ArrayList;
​
public class GenericsDemo4 {
    public static void main(String[] args) {
        /*
         *泛型不具备继承性,但是数据具备继承性
         * */
        //创建集合的对象
        ArrayList<Ye> list1 = new ArrayList<>();
        ArrayList<Fu> list2 = new ArrayList<>();
        ArrayList<Zi> list3 = new ArrayList<>();
        //调用method方法
        //method(list1);
        //method(list2);
        //method(list3);
​
        list1.add(new Fu());
        list1.add(new Ye());
        list1.add(new Zi());
​
    }
    /*
    * 此时,泛型里面写的是什么类型,那么只能传递什么类型的数据。
    * */
    public static void method(ArrayList<Ye> list){
​
    }
}
class Ye{}
class Fu extends Ye{}
class Zi extends Fu{}
package com.collection.exer.collection.mylist;
​
​
import java.util.ArrayList;
​
public class GenericsDemo5 {
    public static void main(String[] args) {
        /*
         *需求:
         *      定义一个方法,形参是一个集合,但是集合中的数据类型不确定。
         * */
        //创建集合的对象
        ArrayList<Ye1> list1 = new ArrayList<>();
        ArrayList<Fu1> list2 = new ArrayList<>();
        ArrayList<Zi1> list3 = new ArrayList<>();
​
        ArrayList<Student> list4 = new ArrayList<>();
        //调用method方法
        method(list1);
        method(list2);
        method(list3);
        //method(list4);
​
        list1.add(new Fu1());
        list1.add(new Ye1());
        list1.add(new Zi1());
​
    }
    /*
    * 此时,泛型里面写的是什么类型,那么只能传递什么类型的数据。
    * 弊端:
    *       利用泛型方法有一个小弊端,此时它可以接收任意数据类型
    * 希望:本方法虽然不确定数据类型,但是以后我希望只能传递Ye Fu Zi
    *
    * 此时我们就可以使用泛型的通配符:
    *           ?也表示不确定的类型
    *           它可以进行类型限定
    *           ? extends E:表示可以传递E或者E所有的子类类型
    *           ? super E:表示可以传递E或着E所有的父类类型
    * */
    public static void method(ArrayList<? extends Ye1> list){
​
    }
}
class Ye1{}
class Fu1 extends Ye1{}
class Zi1 extends Fu1{}
class Student{}

(五)、数据结构(二叉树、二叉查找树、平衡二叉树)

1、树

:每一个结点子节点的数量

二叉树:任意结点的度<=2

树高:树的总层数

根节点:最顶端的结点

左子结点:左下方的结点

右子结点:右下方的结点

2、二叉查找树(又称二叉排序树或者二叉搜索树)

特点:

每一个结点上最多有两个子节点

任意结点左子树上的值都小于当前结点

任意结点右子树上的值都大于当前结点

添加结点规则:

小的存在左边

大得存在右边

一样的不存

查找结点

查找慢,效率低

首先先跟根节点进行比较,再跟其子节点进行比较

二叉查找树的遍历

前序遍历:从根节点开始,然后按照根节点,左子节点,右子节点的顺序遍历

遍历顺序为:20->18->16->19->23->22->24

中序遍历:从最左边的子节点开始,然后按照左子节点,根节点,右子节点的顺序

遍历顺序为:16->18->19->20->22->23->24

后序遍历:从最左边的子节点开始,然后按照左子节点,右子节点,根节点的顺序遍历

遍历顺序为:16->19->18->22->24->23->20

层序遍历:从根节点开始一层一层的遍历

遍历顺序为:20->18->23->16->19->22->24

二叉查找树弊端

如果二叉树出现子树高度差较大的这种树,二叉查找树查找效率会降低

3、平衡二叉树

规则:以二叉查找树为基础,任意节点左右左子树高度差不超过1

平衡机制:通过旋转机制保持平衡。

规则1:左旋

确定指点:从添加的节点开始,不断的往父节点找不平衡的节点

步骤:以不平衡的点作为支点

把指点左旋降级,变成左子节点

如果原先的右子节点没有左子节点,晋升原来的右子节点变成父节点;如果原先的右子节点有左子节点,原先 的右子节点变成新的父节点,并把多余的左子节点出让,给已经降级的根节点当右子节点

规则2:右旋

确定指点:从添加的节点开始,不断的往父节点找不平衡的节点

步骤:以不平衡的点作为支点

把指点右旋降级,变成右子节点

如果原先的左子节点没有右子节点,晋升原来的左子节点变成父节点;如果原先的左子节点有右子节点,原先 的左子节点变成新的父节点,并把多余的右子节点出让,给已经降级的根节点当右子节点

触发机制:当添加一个节点之后,该树不再是一颗平衡二叉树

数据结构(平衡二叉树)需要旋转的四种情况

左左:当根节点左子树的左子树有节点插入,导致二叉树不平衡(一次右旋)

左右:当根节点左子树的右子树有节点插入,导致二叉树不平衡(先局部左旋,再整体一次右旋)

右右:当根节点右子树的右子树有节点插入,导致二叉树不平衡(一次左旋)

右左:当根节点左子树的左子树有节点插入,导致二叉树不平衡(先局部右旋,再整体一次左旋)

4、树的演变

(六)、数据结构(红黑树)

  • 红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构。

  • 1972年初出现,当时被称之为平衡二叉树B树。后来,1978年被修改为如今的“红黑树”。

  • 它是一种特殊的二叉树,红黑树的每一个节点上都有存储位表示节点的颜色,

  • 每个节点可以是红或者黑;红黑树不是高度平衡的,它的平衡是通过“红黑规则”进行实现的

  • 红黑树增删改查的性能都很好

平衡二叉树与红黑树的比较
平衡二叉树:

高度平衡

当左右子树高度差超过1小时,通过旋转保持平衡

红黑树:

是一个二叉查找树

但是不是高度平衡的

条件:特有的红黑规则

红黑规则

1、每个节点或是红色的,或者是黑色的

2、根节点必须是黑色的

3、如果一个节点没有子节点或者父节点,则该节点相应的指针属性位Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的

4、如果某一个节点是红色的,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)

5、对每一个节点,从该节点到其所有的后代叶节点的简单路径上,均包含相同数目的黑色节点;

添加节点规则

默认颜色:添加节点默认是红色的(效率高)

调整方案

二、Set系列集合

特点:

  • 添加的元素是无序、不重复、无索引

  • Set接口中的方法上基本上与Collection的API一致。

无序:存取顺序不一致

不重复:可以去除重复

无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素

Set集合实现类

HashSet:无序、不重复、无索引

LinkedHashSet:有序、不重复、无索引

TreeSet:可排序、不重复、无索引

(一)、HashSet

HashSet底层原理

HashSet集合底层采用哈希表存储数据

哈希表是一种对于增删查改数据性能都较好的结构

哈希表组成

JDK8之前:数组+链表

JDK8开始:数组+链表+红黑树

哈希值:对象的整数表现形式

根据hashCode方法算出来的int类型的整数

该方法定义在Object类中,所有的对象都可以调用,默认使用地址值进行计算

一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值

对象的哈希值特点

如果没有重写hashCode方法,不同对象计算出的哈希值是不同的

如果已经重写hashCode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的

在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样(哈希碰撞)

HashSet JDk8以前底层原理

(1)、创建一个默认长度16,默认加载因子0.75的数组,数组名table(加载因子实质为HashSet扩容时机,当数组 里面存了16*0.75 = 12的时候,数组就会扩容到元素组的2倍)

(2)、根据元素的哈希值跟数组的长度计 算出应存入的位置

int index = (数组长度-1) & 哈希值

(3)、判断当前位置是否为null,如果是null直接存入;如果位置不为null,表示有元素,则调用eqauls方法比较属性 值。

(4)、一样:不存 不一样:存入数组,形成链表

JDK8以前:新元素存入数组,老元素挂在新元素下面

JDK8以后:新元素直接挂在老元素下面,当链表长度超过8,而且数组长度大于等于64时,自动转换为红黑 树。

如果集合中存储的是自定义对象,必须要重写hashCode和equals方法。(重写hashCode的目的根据其属性值 计算哈希值)

HashSet的三个问题

HashSet为什么存和取的顺序不一样?

HashSet为什么没有索引?

Hash是利用什么机制保证数据去重的

(二)、LinkedHashSet

LinkedHashSet底层原理

有序、不重复、无索引

这里的有序指的是保证存储和取出的元素顺序一致

原理:底层数据结构依然是哈希表,只是每个元素又额外的多了一个双链表机制记录存储的顺序。

(三)、TreeSet

特点:

不重复、无索引、可排序

可排序:按照元素的默认规则(由小到大)排序。

TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能较好。

TreeSet集合默认的规则

对于数值类型:Integer,Double,默认按照从小到大的顺序进行排序。

对于字符、字符串类型:按照字符在ASCII码表中的数字升序进行排序。

TreeSet的两种比较方式

默认排序/自然排序:Javabean类实现Comparable接口指定比较规则

public class Student implements Comparable<Student>{
    /*
    * 需求:创建TreeSet集合,并添加3个学生对象,要求按照年龄进行排序
    * */
    private String name;
    private int age;
​
​
    public Student() {
    }
​
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
​
    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }
​
    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }
​
    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }
​
    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }
​
​
    @Override
    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
​
    @Override
    public int compareTo(Student o) {
        //指定排序规则
        //只看年龄,想要年龄的升序进行排序
        return this.getAge() - o.getAge();
        //this:表示当前要添加的元素
        //o:表示已经在红黑树存在的元素
​
        //返回值:
            //负数:认为要添加的元素是小的,存左边
            //正数:认为要添加的元素是大的,存右边
            //0:认为要添加的元素已经存在,舍弃
    }
}
import com.collection.exer.collection.bean.Student;
​
import java.util.TreeSet;
​
public class TreeSetDemo1 {
    public static void main(String[] args) {
        Student s1 = new Student("张三", 23);
        Student s2 = new Student("李四", 18);
        Student s3 = new Student("王五", 34);
​
        TreeSet<Student> students = new TreeSet<>();
        students.add(s1);
        students.add(s2);
        students.add(s3);
        System.out.println(students);
    }
}
​
运行结果:
    [Student{name = 李四, age = 18}, Student{name = 张三, age = 23}, Student{name = 王五, age = 34}]
比较器排序:创建TreeSet集合时,自定义Comparator比较器对象,指定比较规则

public class Student{
    private String name;
    private int age;
​
​
    public Student() {
    }
​
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
​
    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }
​
    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }
​
    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }
​
    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }
​
​
    @Override
    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
​
}
import java.util.Comparator;
import java.util.TreeSet;
​
public class TreeSetDemo2 {
    /*
    * 需求:请自行选择比较器排序和自然排序两种方式:
    * 要求:存入五个字符串,"c","ab","ef","qwer","acer"
    * */
​
    public static void main(String[] args) {
        TreeSet<String> strings = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                //按长度进行排序 ctrl + p
                int i = o1.length() - o2.length();
                return i == 0 ? o1.compareTo(o2) : i;//如果i=0,那么就用原来的比较方式进行比较。
            }
        });
​
        strings.add("c");
        strings.add("ab");
        strings.add("ef");
        strings.add("qwer");
        strings.add("acer");
​
        System.out.println(strings);
    }
}
​
运行结果:
    [c, ab, ef, acer, qwer]

如果两种方式同时存在以方式二比较器排序为准。

二、Map(双列集合)

双列集合的特点

(1)、双列集合一次需要存一对数据,分别为键和值

(2)、键不能重复,值可以重复

(3)、键和值是一一对应的,每一个键只能找到自己对应的值

(4)、键+值这个整体 我们称之为”键值对“或者”键值对对象“,在Java中叫做”Entry对象“

双列集合的体系结构

Map的常见API

Map是双列集合的顶层接口,它的功能是全部双列集合都可以继承使用的

方法名称说明
V put(K key,V value)添加元素。在添加元素的时候,如果键不存在,那么直接把键值对对象添加到map集合中。如果键存在,那么会将原有的键值对对象覆盖,会把被覆盖的值进行返回
V remove(Object key)根据键删除键值对元素
void clear()移除所有的键值对元素
boolean containsKey(Object key)判断集合是否包含指定的键
boolean containsValue(Object value)判断集合是否包含指定的值
boolean isEmpty()判断集合是否为空
int size()集合的长度,也就是集合键值对的个数
import java.util.HashMap;
import java.util.Map;
​
public class MyMapDemo {
    /*
    * | V put(K key,V value)                | 添加元素                           |
    * | ----------------------------------- | ---------------------------------- |
    * | V remove(Object key)                | 根据键删除键值对元素               |
    * | void clear()                        | 移除所有的键值对元素               |
    * | boolean containsKey(Object key)     | 判断集合是否包含指定的键           |
    * | boolean containsValue(Object value) | 判断集合是否包含指定的值           |
    * | boolean isEmpty()                   | 判断集合是否为空                   |
    * | int size()                          | 集合的长度,也就是集合键值对的个数 |
    *
    * */
    public static void main(String[] args) {
        //创建Map集合的对象
        Map<String, String> m = new HashMap<>();
        //添加元素
        //put方法的细节:
​
        //添加/覆盖
        //在添加数据的时候,如果键不存在,那么直接把键值对对象添加到map集合中
        //在添加数据的时候哦,如果键存在,那么会把原有的键值对对象覆盖,会把被覆盖的值进行返回。
        m.put("郭靖", "黄蓉");
        m.put("韦小宝", "沐剑屏");
        m.put("尹志平", "小龙女");
​
        //String put = m.put("韦小宝", "双儿");
        //System.out.println(put);//沐剑屏
​
        //删除
        //String re = m.remove("郭靖");
        //System.out.println(re);//黄蓉
​
        //清空集合
        //m.clear();
        //判断是否包含
        /*
        boolean keyResult = m.containsKey("郭靖");
        System.out.println(keyResult);//true
​
        boolean valueResult = m.containsValue("小龙女");
        System.out.println(valueResult);//true
        */
        //判断集合是否为空
        boolean empty = m.isEmpty();
        System.out.println(empty);//false
​
        int size = m.size();
        System.out.println(size);
​
        //打印集合
        System.out.println(m);
    }
    运行结果
       false
3
{韦小宝=沐剑屏, 尹志平=小龙女, 郭靖=黄蓉} 
Map的遍历方式
键找值
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
​
public class MyMapDemo1 {
    public static void main(String[] args) {
        //Map集合的第一种方式,键找值
        //1.创建Map集合的对象
        Map<String, String> map = new HashMap<>();
​
        //2.添加元素
        map.put("郭靖", "黄蓉");
        map.put("韦小宝", "沐剑屏");
        map.put("尹志平", "小龙女");
​
        //3.通过键找值
        //3.1 获取所有的键,把这些键放到一个单列集合中
        Set<String> keySet = map.keySet();
        //3.2 遍历单列集合,得到每一个键
        for (String key : keySet) {
            //System.out.println(s);
            //3.3 利用map集合中的键获取对应的值
            String value = map.get(key);
            System.out.println(key + "=" + value);
        }
​
    }
}
键值对
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
​
public class MyMapDemo4 {
    public static void main(String[] args) {
        //遍历map集合第二种方式:键值对
        //1.创建对象
        Map<String, String> map = new HashMap<>();
​
        //2.添加元素
        map.put("标枪选手","马超");
        map.put("人物挂件","明世隐");
        map.put("御龙骑士","尹志平");
​
        //通过entrySet方法获取每个键值对对象,并返回一个Set集合
        Set<Map.Entry<String, String>> entries = map.entrySet();
        //通过增强for对Set集合进行遍历
        for (Map.Entry<String, String> entry : entries) {
            System.out.println(entry.getKey() + "=" + entry.getValue());
            //System.out.println(entry);
        }
    }
}
Lambda表达式
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
​
public class MyMapDemo7 {
    public static void main(String[] args) {
        //Map集合的第三种遍历方式:Lambdda表达式
        //创建Map集合对象
        Map<String, String> map = new HashMap<>();
​
        //添加元素
        map.put("鲁迅", "这句话是我说的");
        map.put("曹操", "不可能绝对不可能");
        map.put("刘备", "接着奏乐接着舞");
        map.put("柯镇恶", "看我眼色行事");
​
        //3.利用Lambda表达式进行遍历
        //底层:
        //forEach其实就是利用第二种方式进行遍历,一次得到每一个键和值,再调用accept方法
        map.forEach((key,value) -> System.out.println(key + "=" + value));
​
        System.out.println("=====================");
        map.forEach(new BiConsumer<String, String>() {
            @Override
            public void accept(String key, String value) {
                System.out.println(key+"="+value);
            }
        });
    }
}
Map接口的架构体系

(一)、HashMap

HashMap的特点

(1)、HashMap是Map里面的一个实现类。

(2)、没有额外需要学习的特有方法,直接使用Map里面的方法就可以了。

(3)、特点都是由键决定的:无序、不重复、无索引

(4)、HashMap跟HashSet底层原理是一摸一样的,都是哈希表结构

HashMap底层原理

首先创建长度为16,默认加载因子为0.75,根据键计算哈希值,跟值无关

HashMap底层是哈希表结构

依赖hashCode方法和equals方法保证键的唯一

如果键存储的是自定义对象,需要重写hashCode方法和equals方法

如果值存储自定义对象,不需要重写hashCode和equals方法

(二)、LinkedHashMap

LinkedHashMap

由键决定:有序、不重复、无索引

这里的有序指的是保证存储和取出的元素顺序是一致

原理:底层数据结构是依然哈希表,只是每个键值对元素又额外多了一个双链表1的机制记录存储的顺序。

(三)、TreeMap

TreeMap跟TreeSet底层原理一样,都是红黑树结构的。

由键决定特性:不重复、无索引、可排序

可排序:对键进行排序

注意:默认按照键的从小到大进行排序,也可以自己规定键的排序规则

代码书写两种排序规则

实现Comparable接口,指定比较规则。

创建集合时传递Comparator比较器对象,指定比较规则。

可变参数

可变参数:

其本质为一个数组,方法参数的个数是可以发生改变的,在形参中接收多个数据。格式:属性类型...参数名

注意

1、在方法的形参中最多只能写一个可变参数

2、在方法的形参中,如果出现了可变参数以外,还有其他的形参,那么可变参数要写在最后

Collections

java.util.Collections:是集合工具类

作用:Collections不是集合,而是集合的工具类。

Collections常用的API
方法名称说明
public static <T> boolean addAll(Collection<T> c,T... elements)批量添加元素
public static void shuffle(List<?> list)打乱List集合元素的顺序

不可变集合

不可变集合:不可以被修改的集合

运用场景

不想让别人修改集合中的内容。

创建不可变集合的书写格式

方法名称说明
static <E> List<E> of(E...elements)创建一个具有指定元素的List集合对象
static <E> Set <E>of(E...elements)创建一个具有指定元素的Set集合对象,元素不能重复
static <K,V> Map<K,V> of(E...elements)创建一个具有指定元素的Map集合对象。注意:元素不能重复,键值对不能超过10个。如果超过10个使用ofEntries方法,如果JDK版本大于10,用copyOf()方法

注意:这个集合不能添加,不能删除,不能修改。

Stream流

Stream流的作用:结合了Lambda表达式,简化集合、数组的操作

Stream流的使用步骤

先得到一条Stream流(流水线),并把数据放上去,利用Stream流中的API进行各种操作

中间方法:方法调用完毕之后,还可以调用其他方法

终结方法:最后一步,调用完毕之后,不能调用其他方法

(1)、先得到一条Stream流(流水线),并把数据放上去

获取方式方法名说明
单列集合default Stream<E> stream()Collection中大的默认的方法stream
双列集合无法直接使用stream流
数组public static <T> Stream<T> stream(T[] array)Arrays工具类中的静态方法stream
一堆零散数据public static<T>Stream<T> of(T...values)Stream接口中的静态方法of

Stream流的中间方法

方法名称说明
Stream<T> filter(Predicate<? super T> predicate)过滤
Stream<T> limit(long maxSize)获取前几个元素
Stream<T>skip(long n)跳过前几个元素
Stream<T>distinct()元素去重,依赖(hashCode和equals方法)
static <T> Stream<T> concat(Stream a,Stream b)合并a和b两个流为一个流
Stream<R>map(Function<T,R> mapper)转换流中的数据类型

注意1:中间方法,返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程

注意2:修改Stream流中的数据,不会影响原来集合或者数组中的数据

import java.util.ArrayList;
import java.util.Collections;
​
public class StreamDemo2 {
    public static void main(String[] args) {
        /*
        * | Stream<T> filter(Predicate<? super T> predicate) | 过滤           |
        * | ------------------------------------------------ | -------------- |
        * | Stream<T> limit(long maxSize)                    | 获取前几个元素 |
        * | Stream<T>skip(long n)                            | 跳过前几个元素 |
        * */
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"张无忌","周芷若","张敏","赵明","张三丰","张翠山","张良");
​
        list.stream()
                .filter(s -> s.startsWith("张"))
                .filter(s -> s.length() == 3)
                .forEach(s -> System.out.println(s));
        System.out.println("-------------------------------------------------------------------------------");
        list.stream().limit(3).forEach(s -> System.out.println(s));
​
        list.stream().skip(4).forEach(s -> System.out.println(s));
        System.out.println("------------------------------------------------------------------");
        list.stream().skip(1).limit(3).forEach(s -> System.out.println(s));
    }
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.stream.Stream;
​
public class StreamDemo3 {
    public static void main(String[] args) {
        /*
        * | tream<T>distinct()                             | 元素去重,依赖(hashCode和equals方法) |
        * | ---------------------------------------------- | -------------------------------------- |
        * | static <T> Stream<T> concat(Stream a,Stream b) | 合并a和b两个流为一个流                 |*/
        ArrayList<String> list1 = new ArrayList<>();
        Collections.addAll(list1,"张无忌","周芷若","张敏","赵明","张三丰","张翠山","张良");
        ArrayList<String> list2 = new ArrayList<>();
        Collections.addAll(list2,"张三丰","张翠山","张良","王麻子");
        Stream.concat(list1.stream(),list2.stream())
                .distinct()//去重
                .forEach(s -> System.out.println(s));
​
    }
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Function;
​
public class StreamDemo4 {
    public static void main(String[] args) {
        /*
        * Stream<R>map(Function<T,R> mapper)转换流中的数据类型
        * */
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"张无忌-15","周芷若-14","张敏-54","赵明-34","张三丰-20","张翠山-23","张良-48");
​
        list.stream().map(new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                String[] split = s.split("-");
                String ageString = split[1];
                int age = Integer.parseInt(ageString);
                return age;
            }
        }).forEach(s-> System.out.println(s));
        list.stream()
                .map(s->Integer.parseInt(s.split("-")[1]))
                .forEach(s-> System.out.println(s));
    }
}

Stream流的终结方法

名称说明
void forEach(Consumer action)遍历
long count统计
toArray()收集流中的数据,放到数组中
collect(Collector collector)收集流中的数据,放到集合中
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.function.IntFunction;
​
public class StreamDemo5 {
    public static void main(String[] args) {
        /*
        * | void forEach(Consumer action) | 遍历                       |
        * | ----------------------------- | -------------------------- |
        *  | long count                    | 统计                       |
        *  | toArray()                     | 收集流中的数据,放到数组中 |
        * */
​
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"张无忌","周芷若","张敏","赵明","张三丰","张翠山","张良");
​
        list.stream().forEach(s-> System.out.println(s));
​
        //long count()
        long count = list.stream().count();
        System.out.println(count);
​
        //toArray()  收集流中的数据,放到数组中
        Object[] arr1 = list.stream().toArray();
        System.out.println(Arrays.toString(arr1));
​
        //IntFunction的泛型:具体类型的数组
        //apply的形参:流中数据的个数,要跟数组的长度保持一致
        //apply的返回值:具体类型的数组
        //方法体:就是创建数组
​
        //toArray方法的参数的作用:负责创建一个指定类型的数组
        //toArray方法的底层:会依次得到流里面的每一个数据,并把数据放到数组当中
        //toArray方法的返回值,是一个装着流里面所有的数据的数组
        String[] arr = list.stream().toArray(new IntFunction<String[]>() {
            @Override
            public String[] apply(int value) {
                return new String[value];
            }
        });
        System.out.println(Arrays.toString(arr));
​
        String[] arr2 = list.stream().toArray(value -> new String[value]);
        System.out.println(Arrays.toString(arr2));
​
    }
}


                
  • 10
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值