【Java】集合进阶

个人主页:Hello Code.
本文专栏:Java零基础指南
更多相关内容请点击前往 Java零基础指南 查看
如有问题,欢迎指正,一起学习~~



Collection

集合和数组的对比

  • 数组的长度不可变,集合的长度可变
  • 数组可以存储基本数据类型和引用数据类型

集合只能存储引用数据类型。如果要存储基本数据类型,需要存储对应的包装类

集合体系结构

单列结构

  • List结构:可出现重复元素
  • Set结构:不可以出现重复的元素

双列结构

在这里插入图片描述

Collection集合概述

  • 是单列集合的顶层接口,它表示一组对象,这些对象也成为Collection的元素
  • JDK不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现

创建Collection集合的对象

  • 多态的方式
  • 具体的实现类ArrayList

常见成员方法

方法名 说明
boolean add(E e) 添加元素
boolean remove(Object o) 从集合中移除指定的元素
boolean removeif(Object o) 根据条件进行删除
void clear() 清空集合
boolean contains(Object o) 判断集合中是否存在指定的元素
boolean isEmpty() 判断集合是否为空
int size() 集合的长度,也就是集合中元素的个数

Collection集合的遍历

Iterator:迭代器,集合的专用遍历方式

Iterator iterator():返回集合中的迭代器对象,该迭代器对象默认指向当前集合的0索引

Iterator中的常用方法

  • boolean hasNext():判断当前位置是否有元素可以被取出
  • E next():获取当前位置的元素,并将迭代器对象移向下一个索引位置

步骤

  1. 获取迭代器的对象:Iterator<E> it = list.iterator();
  2. 利用迭代器的成员方法进行遍历

迭代器原理

增强for循环

增强for:简化数组和Collection集合的遍历

  • 它是JDK5之后出现的,其内部原理是一个Iterator迭代器
  • 实现Iterable接口的类才可以使用迭代器和增强for

格式

for(元素数据类型 变量名 : 数组或者Collection集合){
    
    
	// 在此处使用变量即可,该变量就是元素
}
// 数据类型一定是集合或者数组元素的类型
// 变量名在循环的过程中,依次表示集合或者数组中的每一个元素

注意点:在增强for中,修改第三方变量的值不会影响到集合中的元素

三种循环的使用场景

  • 如果需要操作索引,使用普通for循环
  • 如果在遍历的过程中需要删除元素,请使用迭代器
  • 如果仅仅想遍历,那么使用增强for

案例

// 需求:创建一个Collection集合存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合

// ----------Student类--------------------
public class Student{
    
    
	private String name;
    private int age;

    public Student() {
    
    
    }

    public Student(String name, int age) {
    
    
        this.name = name;
        this.age = age;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public int getAge() {
    
    
        return age;
    }

    public void setAge(int age) {
    
    
        this.age = age;
    }

    @Override
    public String toString() {
    
    
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


// ----------Test类--------------------
import java.util.ArrayList;
import java.util.Iterator;

public class test {
    
    
    public static void main(String[] args) {
    
    
        ArrayList<Student> list = new ArrayList<>();

        Student s1 = new Student("张三", 23);
        Student s2 = new Student("李四", 16);
        Student s3 = new Student("王五", 31);
        list.add(s1);
        list.add(s2);
        list.add(s3);

        // 迭代器遍历
        Iterator<Student> it = list.iterator();
        while (it.hasNext()) {
    
    
            Student s = it.next();
            System.out.println(s);
        }

        System.out.println("----------------------");

        // 增强for遍历
        for (Student s : list) {
    
    
            System.out.println(s);
        }
    }
}


List与LinkedList

概述

  • 有序集合,这里的有序指的是存取顺序
  • 用户可以精确控制列表中每个元素的插入位置,用户可以通过整数索引访问元素,并搜索列表中的元素
  • 与Set集合不同,列表通常允许重复的元素

常用方法

方法名 说明
void add(int index, E element) 在此集合中的指定位置插入指定的元素
E remove(int index) 删除指定索引处的元素,返回被删除的元素
E set(int index, E element) 修改指定索引处的元素,返回被修改的元素
E get(int index) 返回指定索引处的元素

数据结构

数据结构是计算机存储、组织数据的方式。是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率

  • 栈是一种数据先进后出的模型
  • 只允许在一端进行操作
  • 数据进栈的过程称为压/进栈
  • 数据进栈的过程称为弹/出栈
队列
  • 队列是一种先进先出的模型
  • 在两端进行操作
  • 数据进栈的过程称为入队
  • 数据进栈的过程称为出队
数组

数组是一个查询快、增删慢的模型

  • 查询数据通过地址值和索引定位,查询任意数据耗时相同,查询速度快
  • 删除数据时,要将原始数据删除,同时后面每个数据前移,删除效率低
  • 添加数据时,添加位置后的每个数据后移,再添加元素,添加效率极低
链表

链表是一个查询慢、增删快的模型(对比数组)

List常用实现类

ArrayList:底层数据结构是数组,查询快、增删慢

LinkedList:底层数据结构是链表,查询慢、增删快

  • 特有功能
方法名 说明
public void addFirst(E e) 在该列表开头插入指定的元素
public void addLast(E e) 将指定的元素追加到此列表的末尾
public E getFirst() 返回此列表中的第一个元素
public E getLast() 返回此列表中的最后一个元素
public E removeFirst() 从此列表中删除并返回第一个元素
public E removeLast() 从此列表中删除并返回最后一个元素

泛型

泛型:是JDK5中的特性,它提供了编译时类型安全检测机制

泛型的好处

  • 把运行时期的问题提前到了编译期间
  • 避免了强制类型转换

泛型的使用

  • 类后面 ---------------> 泛型类
  • 方法声明上 ---------------> 泛型方法
  • 接口后面 ---------------> 泛型接口

使用泛型时,只能是引用数据类型

泛型类

如果一个类后面有<E>,表示这个类是一个泛型类
在创建泛型类对象时,必须要给这个泛型确定具体的数据类型

泛型的定义格式

  • <类型>:指定一种类型的格式

尖括号里面可以任意书写,按照变量的定义规则即可。一般只写一个字母。
比如:<E>、<T>、<Q>、<K,M>

  • 泛型类的定义格式:修饰符 class 类名<类型>{ }
public class MyGenericityClass<E> {
    
    
    private E element;

    public E getElement() {
    
    
        return element;
    }

    public void setElement(E element) {
    
    
        this.element = element;
    }

    @Override
    public String toString() {
    
    
        return "MyGenericityClass [element=" + element + "]";
    }

}
  

泛型方法

  • 定义格式:修饰符 <类型> 返回值类型 方法名(类型 变量名){ }

  • 范例:public <T> void show(T t){ }

// 定义一个泛型方法,传递一个集合和四个元素,将元素添加到集合中并返回
public class GenericityMethod {
    
    
    public static void main(String[] args) {
    
    
        addElement(new ArrayList<String>, "a", "b", "c", "d");
    }

    public static <T> ArrayList<T> addElement(ArrayList<T> list, T t1, T t2, T t3, T t4) {
    
    
        list.add(t1);
        list.add(t2);
        list.add(t3);
        list.add(t4);
        return list;
    }
}

泛型接口

使用方式

  • 实现类也不给泛型
  • 实现类确定具体的数据类型

泛型接口的定义格式修饰符 interface 接口名<类型>{ }

public class GenericityInterface {
    
    
    public static void main(String[] args) {
    
    
        GenericityImpl<String> genericity = new GenericityImpl<>();
        genericity.method("ccccccc");
    }
}

interface Genericity<E> {
    
    
    public abstract void method(E e);
}


class GenericityImpl<E> implements Genericity<E> {
    
    
    public void method(E e) {
    
    
        System.out.println(e);
    }
}

通配符

类型通配符:<?>

ArrayList<?>:表示元素类型未知的ArrayList,它的元素可以匹配任何的类型
但是并不能把元素添加到ArrayList中了,获取出来的也是父类类型

类型通配符上限<? extends 类型>

比如:ArrayList<? extends Number>:它表示的类型是Number或者其子类型

类型通配符下限<? super 类型>

比如:ArrayList<? super Number>:他表示传进来的类型可以是Number类型,也可以是Number的父类类型

import java.util.ArrayList;

public class tongpeifu {
    
    

    public static void main(String[] args) {
    
    
        // TODO Auto-generated method stub
        ArrayList<Integer> list1 = new ArrayList<>();
        ArrayList<String> list2 = new ArrayList<>();
        list1.add(11);
        list2.add("ssacs");
        printList(list1);
        printList(list2);
    }

    private static void printList(ArrayList<?> list) {
    
    
        // TODO Auto-generated method stub
        System.out.println(list);

    }
}
  

Set&TreeSet

Set集合概述和特点

  • 可以去除重复
  • 存取顺序不一致
  • 没有带索引的方法,所以不能使用普通for遍历循环,也不能通过索引来获取、删除Set集合里面的元素

Set集合练习

public class MySet {
    
    
    public static void main(String args[]) {
    
    
        Set<String> set = new TreeSet<>();
        set.add("ccc");
        set.add("aaa");
        set.add("aaa");
        set.add("bbb");
        // 迭代器遍历
        Iterator<String> it = set.iterator();
        while (it.hasNext()) {
    
    
            String s = it.next();
            System.out.println(s);
        }

        System.out.println("---------------------------");

        // 增强for遍历
        for (String s : set) {
    
    
            System.out.println(s);
        }
    }
}

TreeSet

特点

  • 不包含重复元素的集合
  • 没有带索引的方法
  • 可以将元素按照规则进行排序(使用TreeSet时,要制定排序规则)

自然排序Comparable的使用

  • 使用空参构造创建TreeSet集合
  • 自定义的Student类实现Comparable接口

重写里面的compareTo方法

@Override
public int compareTo(Student o){
    
    
	int result = this.age - o.age;
	return result;
}

原理

  • 如果返回值为负数,表示当前存入的元素是较小值,存左边
  • 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
  • 如果返回值为正数,表示当前存入的元素是较大值,存右边

比较器排序Comparator的使用

  • TreeSet的带参构造方法使用的是比较器排序对元素进行排序的
  • 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1, T o2)方法
  • 重写方法时,一定要注意排序规则必须要按照要求的主要条件和次要条件来写
@Override
public int compare(Teacher o1,Teacher o2){
    
    
        // o1表示现在要存入的那个元素
        // o2表示已经存入到集合中的元素


        // 主要条件
        int result=o1.getAge()-o2.getAge();
        // 次要条件
        result=result==0?o1.getName().compareTo(o2.getName()):result;
        return result;
}

两种比较方式小结

  • 自然排序:自定义类实现Comparable接口,重写compareTo方法,根据返回值进行排序
  • 比较器排序:创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序

在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,使用比较排序


数据结构&平衡二叉树

二叉树

  • 二叉查找树,又称二叉排序树或者二叉搜索树
  • 特点
    • 每一个节点上最多有两个子节点
    • 每一个节点的左子节点都是小于自己的
    • 每一个节点的右子节点都是大于自己的
  • 平衡二叉树
    • 二叉树左右两个子树的高度差不超过1
    • 任意节点的左右两个子树都是一颗平衡二叉树
  • 左旋
    • 触发时机:当添加一个节点后,可能已经破坏平衡,就会触发旋转机制
    • 左旋就是将根节点的右侧往左拉,原先的右子节点变成新的父节点,并把多余的左子节点出让,给已经降级的根节点当右子节点
  • 右旋:将根节点的左侧往右拉,左子节点变成了新的父节点,并把多余的右子节点出让,给已经降级的根节点当左子节点

红黑树&HashSet

红黑树

  • 红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构,又叫平衡二叉B树
  • 它是一种特殊的二叉查找树,红黑树的每一个节点上都有存储位表示节点的颜色
  • 每一个节点可以是红或者黑;红黑树不是高度平衡的,它的平衡是通过“红黑规则”进行实现的

红黑规则

  • 每一个节点或是红色的,或者是黑色的
  • 根节点必须是黑色
  • 如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的
  • 如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
  • 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点

添加节点

  • 添加节点的颜色,可以是红色的,也可以是黑色的
  • 添加节点时,默认颜色为红色效率更高
  • 当添加的节点为根节点时,直接变为黑色
  • 当其父节点为根节点时,则不需要做任何操作
  • 其父节点为红色,叔叔节点也是红色
    • 将父节点设为黑色,将叔叔节点也设为黑色
    • 将祖父节点设为红色
    • 如果祖父节点为根节点,则将根节点再次变为黑色
  • 其父节点为黑色,不需要进行任何操作
  • 其父节点为红色,叔叔节点也是黑色
    • 将父节点设为黑色
    • 将祖父节点设为红色
    • 以祖父节点为支点进行旋转

练习:创建3个学生对象,属性为姓名,语数英成绩,按照总分从小到大顺序打印到控制台

// Student类

public class Student implements Comparable<Student> {
    
    
    private String name;
    private int chinese;
    private int english;
    private int math;

    public Student(String name, int chinese, int english, int math) {
    
    
        super();
        this.name = name;
        this.chinese = chinese;
        this.english = english;
        this.math = math;
    }

    public Student() {
    
    
        super();
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public int getChinese() {
    
    
        return chinese;
    }

    public void setChinese(int chinese) {
    
    
        this.chinese = chinese;
    }

    public int getEnglish() {
    
    
        return english;
    }

    public void setEnglish(int english) {
    
    
        this.english = english;
    }

    public int getMath() {
    
    
        return math;
    }

    public void setMath(int math) {
    
    
        this.math = math;
    }

    public int getSum() {
    
    
        int res;
        res = chinese + math + english;
        return res;
    }

    @Override
    public String toString() {
    
    
        return "Student [name=" + name + ", chinese=" + chinese + ", english=" + english + ", math=" + math + "]" + "总分为:" + getSum();
    }

    @Override
    public int compareTo(Student o) {
    
    
        // TODO Auto-generated method stub
        int res = this.getSum() - o.getSum();
        res = res == 0 ? this.getChinese() - o.getChinese() : res;
        res = res == 0 ? this.getMath() - o.getMath() : res;
        res = res == 0 ? this.getName().compareTo(o.getName()) : res;
        return res;
    }
}

// 测试类

import java.util.TreeSet;

public class Test {
    
    

    public static void main(String[] args) {
    
    
        // TODO Auto-generated method stub
        TreeSet<Student> stu = new TreeSet<>();
        Student s1 = new Student("李四", 80, 80, 80);
        Student s2 = new Student("张三", 80, 80, 80);
        Student s3 = new Student("王五", 80, 80, 80);
        stu.add(s1);
        stu.add(s2);
        stu.add(s3);
        for (Student student : stu) {
    
    
            System.out.println(student);
        }
    }
}

HashSet

HashSet集合特点

  • 底层数据结构是哈希表
  • 不能保证存储和取出顺序完全一致
  • 没有带索引的方法,不能使用普通for循环遍历
  • 由于是Set集合,所以元素唯一

基本使用

import java.util.HashSet;
import java.util.Iterator;

//import java.util.TreeSet;

public class Hash {
    
    

    public static void main(String[] args) {
    
    
        // TODO Auto-generated method stub
        HashSet<String> hs = new HashSet<>();
        hs.add("hello");
        hs.add("world");
        hs.add("java");
        hs.add("java");
        hs.add("java");
        hs.add("java");

        Iterator<String> it = hs.iterator();
        while (it.hasNext()) {
    
    
            String s = it.next();
            System.out.println(s);
        }
    }
}

哈希值(哈希码值):是JDK根据对象的地址或者属性值,算出来的int类型的整数

  • Object类中有一个方法可以获取对象的哈希值(public int hashCode())
  • public int hashCode():根据对象的地址值计算出来的哈希值

对象的哈希值特点

  • 如果没有重写hashCode方法,那么是根据对象的地址值计算出的哈希值
  • 同一个对象多次调用hashCode()方法返回的哈希值是相同的
  • 不同对象的哈希值是不一样的
  • 如果重写了hasCode()方法,一般是通过对象的属性值计算出哈希值
  • 如果不同的对象属性值是一样的,那么计算出来的哈希值也是一样的

Map&HashMap&TreeMap

Map

Map集合概述和使用

  • Interface Map<K, V> K:键的数据类型 V:值的数据类型
  • 键不能重复,值可以重复
  • 键和值是一一对应的,每一个键只能找到自己对应的值
  • (键+值)这个整体我们称之为“键值对” 或者“键值对对象”,在Java中叫做“Entry对象”
  • 双列集合一次可以存两个元素(一对数据)

创建Map集合的对象

  • 多态的方式
  • 具体的实现类HashMap
import java.util.HashMap;
import java.util.Map;

public class Base {
    
    

    public static void main(String[] args) {
    
    
        // TODO Auto-generated method stub
        Map<String, String> stu = new HashMap<>();
        stu.put("2020033001", "张三");
        stu.put("2020033002", "李四");
        stu.put("2020033003", "王五");
        System.out.println(stu);
    }
}

Map集合的基本功能

方法名 说明
V put(K key, V value) 添加元素
V remove(Object key) 根据键删除值对元素
void clear() 移除所有的键值对元素
boolean containsKey(Object key) 判断集合是否包含指定的键
boolean containsValue(Object value) 判断集合是否包含指定的值
boolean isEmpty() 判断集合是否为空
int size() 集合的长度,也就是集合中键值对的个数

put方法中,如果要添加的键不存在,则会把键值对都添加到集合中;如果键存在,则会把原先的值覆盖,并当作返回值返回

遍历Map集合

  • Map集合的获取功能
方法名 说明
Set keySet() 获取所有键的集合
V get(Object key) 根据键获取值
Set<Map.Entry<K, V>> entrySet() 获取所有键值对对象的集合
K getKey() 获得键
V getValue() 获得值

HashMap

  • HashMap是Map里面的一个实现类。
  • 没有额外需要学习的特有方法,直接使用Map里面的方法就可以了
  • HashMap和HashSet的底层原理都是哈希表结构
  • 依赖hashCode方法和equals方法保证的唯一
  • 如果要存储的是自定义对象,需要重写hashCode和equals方法

案例

需求:创建一个HashMap集合,键是学生对象(Student),值是籍贯(String)。存储三个键值对元素,并遍历

// Student类

public class Student {
    
    
    private String name;
    private int age;

    public Student() {
    
    
        super();
    }

    public Student(String name, int age) {
    
    
        super();
        this.name = name;
        this.age = age;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public int getAge() {
    
    
        return age;
    }

    public void setAge(int age) {
    
    
        this.age = age;
    }

    @Override
    public String toString() {
    
    
        return "Student [name=" + name + ", age=" + age + "]";
    }

    @Override
    public int hashCode() {
    
    
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
    
    
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Student other = (Student) obj;
        if (age != other.age)
            return false;
        if (name == null) {
    
    
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
}
// 实现类

import java.util.HashMap;
import java.util.Set;
import java.util.Map;

public class HashDemo {
    
    

    public static void main(String[] args) {
    
    
        // TODO Auto-generated method stub
        HashMap<Student, String> hm = new HashMap<>();
        Student s1 = new Student("张三", 23);
        Student s2 = new Student("李四", 22);
        Student s3 = new Student("王五", 22);
        hm.put(s1, "江苏");
        hm.put(s2, "西安");
        hm.put(s3, "北京");

        // 遍历方式一:获取所有的键,再找对应的值
        Set<Student> keys = hm.keySet();
        for (Student key : keys) {
    
    
            String value = hm.get(key);
            System.out.println(key + "------" + value);
        }
        System.out.println("====================");

        // 遍历方式二:先获取到所有的键值对对象,再获取到里面的每一个键和值
        Set<Map.Entry<Student, String>> entries = hm.entrySet();
        for (Map.Entry<Student, String> entry : entries) {
    
    
            Student key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "------" + value);
        }
        System.out.println("====================");

        // 遍历方式三:
        hm.forEach(
                (Student key, String value) -> {
    
    
                    System.out.println(key + "-----" + value);
                }
        );
    }
}

TreeMap

  • TreeMap是Map里面的一个实现类
  • 没有额外需要学习的特有方法,直接使用Map里面的方法就可以了
  • TreeMap和TreeSet一样,底层都是红黑树结构的
  • 依赖自然排序或者比较器排序,对键进行排序
  • 如果键存储的是自定义对象,需要实现Comparable接口或者在创建TreeMap对象时侯给出比较器排序规则

练习:创建一个TreeMap集合,键是学生对象(Student),值是籍贯(String),学生属性姓名和年龄,按照年龄进行排序并遍历

// Student类
public class Student implements Comparable<Student> {
    
    
    private String name;
    private int age;

    public Student() {
    
    
        super();
    }

    public Student(String name, int age) {
    
    
        super();
        this.name = name;
        this.age = age;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public int getAge() {
    
    
        return 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) {
    
    
        // 按照年龄进行排序
        int result = this.getAge() - o.getAge();
        // 次要条件,按照姓名排序
        result = result == 0 ? this.getName().compareTo(o.getName()) : result;
        return result;
    }
}
// 实现类

import java.util.TreeMap;

public class TreeMapTest {
    
    

    public static void main(String[] args) {
    
    
        // TODO Auto-generated method stub
        TreeMap<Student, String> tm = new TreeMap<>();

        Student s1 = new Student("张三", 23);
        Student s2 = new Student("李四", 22);
        Student s3 = new Student("王五", 22);

        tm.put(s1, "江苏");
        tm.put(s2, "北京");
        tm.put(s3, "天津");

        tm.forEach(
                (Student key, String value) -> {
    
    
                    System.out.println(key + "----" + value);
                }
        );
    }
}

可变参数

// 需求:定义一个方法求N个数的和

// 在JDK5之前,会把所有的数据都先放到一个数组中,自定义方法形参只要写一个数组就可以了

public static void main(String[] args){
    
    
    int[] arr = {
    
    1, 2, 3, 4, 5};
    int sum1 = getSum(arr);
    System.out.println(sum1);
}
public static int getSum(int[] arr){
    
    
    int sum = 0;
    for(int i = 0; i < arr.length; i++){
    
    
        sum += arr[i];
    }
    return sum;
}

可变参数:就是形参的个数是可以变化的

格式修饰符 返回值类型 方法名(数据类型...变量名){ }

注意事项

  • 可变参数的底层其实就是一个数组
  • 如果一个方法有多个参数,包含可变参数,那么可变参数要放在最后
public static void main(String[]args){
    
    
        int sum=getSum(1,2,3,4,5,6,7);
        System.out.println(sum);
}
public static int getSum(int...arr){
    
    
        int sum=0;
        for(int i=0;i<arr.length;i++){
    
    
        sum+=arr[i];
        }
        return sum;
}

创建不可变集合

  • 在List、Set、Map接口中,都存在of方法,可以创建一个不可变的集合。

这个集合不能添加、删除、修改

方法名 说明
static List of(E…elements) 创建一个具有指定元素的List集合对象
static Set of(E…elements) 创建一个具有指定元素的Set集合对象
static <K, V> Map<K, V> of(E…elements) 创建一个具有指定元素的Map集合对象

应用场景

集合元素的批量添加:ArrayList<String> list = new ArrayList<>(List.of("a", "b", "c", "d"));


Stream流

Stream流的三类方法

获取Stream流

创建一条流水线,并把数据放到流水线上准备进行操作

中间方法

流水线上的操作
一次操作完毕之后,还可以继续进行其他操作

终结方法

一个Stream流只能有一个终结方法
是流水线上的最后一个方法

Stream流的获取方法

单列集合

可以使用Collection接口中的默认方法stream()生成流:集合对象.stream();
default Stream<E> stream()

双列集合

间接的生成流
可以先通过keySet或者entrySet获取一个Set集合,再获取Stream流:集合对象.keySet().stream(); 集合对象.entrySet().stream();

数组

Arrays中的静态方法stream生成流:Arrays.stream(数组名);

同种数据类型的多个数据

1, 2, 3, 4, 5, 6…
“aaa”, “bbb”, “ccc”, …
使用Stream.of(T…values)生成流

中间方法

  • Stream filter(Predicate predicate):用于对流中的数据进行过滤
    Predicate接口中的方法
    boolean test(T t):对给定的参数进行判断,返回一个布尔值
  • Stream limit(long maxSize):截取指定参数个数的数据
  • Stream skip(long n):跳过指定参数个数的数据
  • static Stream concat(Stream a, Stream b):合并a和b两个流为一个流
  • Stream distinct():除流中复的元素,底层依赖(hashCode和equals方法)

终结方法

  • void forEach(Consumer action):对此流的每个元素执行操作
    Consumer接口中的方法 void accept(T t):对给定的参数执行此操作
  • long count():返回此流中的元素数

在Stream流中无法直接修改集合,数组等数据源中的数据

收集方法

  • R collect(Collector collector)
  • 工具类Collectors提供了具体的收集方式
    • public static Collector toList():把元素收集到List集合中
    • public static Collector toSet():把元素收集到Set集合中
    • public static Collector toMap(Function keyMapper, Function valueMapper):把元素收集到Map集合中
  • collect方法只能获取到流中剩余的每一个数据,在底层不能创建容器,也不能把数据添加到容器中,需要用Collectors

练习

  • 现在有两个ArrayList集合,分别存储6名男演员和6名女演员,要求完成以下操作
    • 男演员只要名字为3个字的前两人
    • 女演员只要姓杨的,并且不要第一个
    • 把过滤后的男演员和女演员姓名合并到一起
    • 把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据
package Stream;

import java.util.ArrayList;
import java.util.stream.Stream;

public class Demo{
    
    
    public static void main(String[] args){
    
    
        ArrayList<String> manList = new ArrayList<>();
        manList.add("张三");
        manList.add("李四");
        manList.add("王五");
        manList.add("马六");
        manList.add("赵七");
        manList.add("刘八");
        ArrayList<String> womanList = new ArrayList<>();
        womanList.add("吴九");
        womanList.add("乐十");
        womanList.add("杨十一");
        womanList.add("廖牛马");
        womanList.add("汶十二");
        womanList.add("苏十三");
        
        Stream<String> stream1 = manList.stream().filter(name -> name.length() == 3).limit(2);
        
        Stream<String> stream2 = womanList.stream().filter(name -> name.startsWith("杨")).skip(1);
        
        Stream.concat(stream1, stream2).forEach(name -> System.out.println(name));;
    }
}

推荐阅读:【Java】IO流详解

猜你喜欢

转载自blog.csdn.net/qq_24980365/article/details/121774800