Java:集合进阶

导读:本篇文章讲解 Java:集合进阶,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

12 集合Collection(12-17)

12.1 集合体系

在这里插入图片描述

12.2 Collection集合概述和使用

  • Collection集合概述

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

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

12.3 Collection集合常用方法

方法名 说明
boolean add(E e) 添加元素
boolean remove(Object o) 从集合中移除指定的元素
boolean removeif(Object o) 根据条件进行删除
void clear() 清空集合
boolean contains(Object o) 判断集合中是否存在指定的元素
boolean isEmpty() 判断集合是否为空
int size() 集合的长度,也就是集合中元素的个数
public static void main(String[] args) {
        Collection<String> c = new ArrayList<>();
        c.add("aaa"); // 添加元素
        System.out.println(c.remove("aaa")); // 删除元素
        c.removeIf( // 删除符合条件的元素
                (String s)->{
                    return s.length() == 3;
                }
        );
        System.out.println(c);
        c.clear();
        System.out.println(c);
        System.out.println(c.contains("aaa"));
        System.out.println(c.size());
    }

12.4 Collection集合迭代器

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

    • Iterator\<E> iterator():返回集合中的迭代器对象,该迭代器对象默认指向当前集合的0索引。
  • Iterator中的常用方法

    • boolean hasNext():判断当前位置是否有元素可以被取出
    • \E next():获取当前位置的元素,并将迭代器对象移向下一个索引位置
    • void remove():删除迭代器的当前元素
public static void main(String[] args) {
        Collection<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");

        // 1、通过list.iterator(),获得迭代器的对象
        // 迭代器对象一旦被创建出来,默认指向集合的0索引处
        Iterator<String> it = list.iterator();

        // 2、利用迭代器里面的方法进行遍历
//        System.out.println(it.hasNext()); // 当前位置是否有元素可以被取出
//        System.out.println(it.next()); //取出当前位置的元素  + 将迭代器往后移动一个索引的位置
//        System.out.println(it.next());
//        System.out.println(it.next());
//        System.out.println(it.next());
//        System.out.println(it.next());
        
        // 获取每个元素
        while(it.hasNext()){
            System.out.println(it.next());
        }
        
        // 删除集合中某个元素
        while(it.hasNext()){
            String s = it.next();
            if("b".equals(s)){
                //指向谁,那么此时就删除谁.
                it.remove();
            }
        }
        System.out.println(list);

    }

12.5 Collection集合增强for循环

  • 增强for:简化数组和Collection集合的遍历
    • 它是JDK5之后出现的,其内部原理是一个Iterator迭代器
    • 实现Iterable接口的类才可以使用迭代器和增强for。
    • 所以:单列可以(Collection继承了Iterable ),双列不可以(Map没继承Iterable )

格式

for(元素数据类型 变量名 : 数组或者Collection集合) {
    //在此处使用变量即可,该变量就是元素
}

示例

ArrayList<String> list = new ArrayList<>();
//1,数据类型一定是集合或者数组中元素的类型
//2,str仅仅是一个变量名而已,在循环的过程中,依次表示集合或者数组中的每一个元素
//3,list就是要遍历的集合或者数组.
for(String s : list) {
	//  在此处使用变量即可,该变量就是元素
    System.out.println(i);
}

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

public static void main(String[] args) {
        ArrayList<String> list =  new ArrayList<>();
        list.add("a");
        list.add("b");

        for(String str : list){
            str = "q";
            System.out.println(str); // 这里打印的确实是q
        }

        System.out.println(list); // 这里打印的是[a,b]
    }

12.6 普通for、迭代器、增强for,三种循环的使用场景

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

13 集合Collection下的List

13.1 概述

  • List集合特点
    • 有序:存储和取出的元素顺序一致
    • 有索引:可以通过索引操作元素
    • 可重复:存储的元素可以重复

13.2 List特有方法

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

13.3 数据结构

  • 栈:先进后出
  • 队列:先进先出
  • 数组:查询快,增删慢的模型
  • 链表:增删快,查询慢的模型(对比数组)
  • 链表结构示意图

单位元素
在这里插入图片描述
链表的过程
在这里插入图片描述

常见数据结构之链表
在这里插入图片描述

13.4 List集合实现类:ArrayList和LinkedList

  • List集合常用子类:ArrayList,LinkedList
    • ArrayList:底层数据结构是数组,查询快,增删慢
    • LinkedList:底层数据结构是链表,查询慢,增删快

ArrayList方法

跟LIst特有方法一样

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

LinkedList特有方法

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

14 集合Collection下的Set

14.1 Set集合特点

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

14.2 Set集合实现类:TreeSet

1 TreeSet集合特点

  • 不包含重复元素的集合
  • 没有带索引的方法
  • 可以将元素按照规则进行排序。有自然排序比较器排序

1 自然排序Comparable的使用

  • 使用空参构造创建TreeSet集合
  • 自定义的Student类实现Comparable接口
  • 重写里面的compareTo​方法

2 自然排序简单原理图

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

3 代码案例

// 主方法
public static void main(String[] args) {
		// 使用`空参构造`创建TreeSet集合
        TreeSet<Student> ts = new TreeSet<>();

        Student s1 = new Student("zhangsan",28);
        Student s2 = new Student("lisi",27);
        Student s3 = new Student("wangwu",29);
        Student s4 = new Student("zhaoliu",28);
        Student s5 = new Student("qianqi",30);

        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);

        System.out.println(ts);
    }

// Student类
// 实现Comparable接口
public class Student implements Comparable<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 +
                '}';
    }
	
    @Override
    // 重写里面的compareTo`方法
    public int compareTo(Student o) {
        //按照对象的年龄进行排序
        //主要判断条件
        // this.age代表当前存入的数据,o.age代表TreeSet已有数据
        int result = this.age - o.age;
        //次要判断条件
        result = result == 0 ? this.name.compareTo(o.getName()) : result;
        return result;
    }
}

4 比较器排序Comparator的使用

  • TreeSet的带参构造方法使用的是比较器排序对元素进行排序的
  • 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare​(T o1,T o2)方法
  • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写

5 代码案例

// 主方法
public static void main(String[] args) {
		// 使用`带参构造`创建TreeSet集合
		// `让集合构造方法接收Comparator的实现类对象`
		// 可以写成匿名内部类的形式,也可以用Lambda表达式写
        TreeSet<Teacher> ts = new TreeSet<>(new Comparator<Teacher>() {
            @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;
            }
        });

        Teacher t1 = new Teacher("zhangsan",23);
        Teacher t2 = new Teacher("lisi",22);
        Teacher t3 = new Teacher("wangwu",24);
        Teacher t4 = new Teacher("zhaoliu",24);

        ts.add(t1);
        ts.add(t2);
        ts.add(t3);
        ts.add(t4);

        System.out.println(ts);
    }

// Teacher类
public class Teacher {
    private String name;
    private int age;

    public Teacher() {
    }

    public Teacher(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 "Teacher{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

6 两种比较方式小结

  • 自然排序:自定义类实现Comparable接口,重写compareTo​方法,根据返回值进行排序。
  • 比较器排序:创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序。
  • 在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,使用比较器排序

两种方式中,关于返回值的规则一致:
+ 如果返回值为负数,表示当前存入的元素是较小值,存左边
+ 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
+ 如果返回值为正数,表示当前存入的元素是较大值,存右边

第三条的释义:
StringInteger等基础类默认实现的Comparable接口不满足需求时,没法直接更改源码,此时就需要使用比较器排序

案例说明

需求:请自行选择比较器排序和自然排序两种方式;
要求:存入四个字符串, “c”, “ab”, “df”, “qwer”
按照长度排序,如果一样长则按照首字母排序

    public static void main(String[] args) {
//        TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
//            @Override
//            public int compare(String o1, String o2) {
//                int result = o1.length() - o2.length();
//                result = result == 0 ? o1.compareTo(o2) : result;
//                return result;
//            }
//        });
		// Lambda表达式写法
		// 当String,Integer基础类默认实现了`Comparable`接口不满足需求时,没法直接更改源码,此时就需要使用`比较器排序`。
        TreeSet<String> ts = new TreeSet<>(
                (String o1, String o2) -> {
                    int result = o1.length() - o2.length();
                    result = result == 0 ? o1.compareTo(o2) : result;
                    return result;
                }
        );

        ts.add("c");
        ts.add("ab");
        ts.add("df");
        ts.add("qwer");


        System.out.println(ts);
    }

2 TreeSet底层数据结构

TreeSet底层数据结构是红黑树

1 二叉树的基本概念

什么叫二叉树?
二叉树中,任意一个节点的度要小于等于2。
什么叫度?
度是指每一个节点的子节点数量
在这里插入图片描述
在这里插入图片描述

2 二叉查找树

二叉查找树,又称二叉排序树或者二叉搜索树。

  • 特点:
    • 每一个节点上最多有两个子节点
    • 左子树上所有节点的值都小于根节点的值
    • 右子树上所有节点的值都大于根节点的值
      在这里插入图片描述

3 平衡二叉树

特点

  • 二叉树左右两个子树的高度差不超过1
  • 任意节点的左右两个子树都是一颗平衡二叉树

平衡二叉树-旋转

  • 利用左旋和右旋机制保证树的平衡
    • 左旋
    • 右旋

旋转触发时机

  • 当添加一个节点之后,该树不再是一颗平衡二叉树

左旋

  • 就是将根节点的右侧往左拉,原先的右子节点变成新的父节点,并把多余的左子节点出让,给已经降级的根节点当右子节点
    • 逆时针旋转
    • 右子节点变成父节点(根节点)
    • 原先的根节点降级变成左子节点
    • 将多余的左子节点出让,给降级的节点作为右子节点

在这里插入图片描述
右旋

  • 将根节点的左侧往右拉,左子节点变成了新的父节点,并把多余的右子节点出让,给已经降级根节点当左子节点
    • 顺时针旋转
    • 左子节点变成父节点(根节点)
    • 原先的根节点降级变成右子节点
    • 将多余的右子节点出让,给降级的节点作为左子节点

4 红黑树

概念
红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构。
特点
红黑树不是高度平衡的,它的平衡是通过"红黑规则"进行实现的,规则如下:

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

添加节点规则
添加的节点默认是红色的。
在这里插入图片描述

14.3 Set集合实现类:HashSet

1 HashSet集合特点

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

哈希值

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

Object类中有一个方法可以获取对象的哈希值
public int hashCode​():返回对象的哈希码值

对象的哈希值特点

  • 同一个对象多次调用hashCode()方法返回的哈希值是相同的
  • 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同

2 HashSet底层数据结构

HashSet底层数据结构是哈希表

哈希表
JDK8之前,底层采用数组+链表实现,可以说是一个元素为链表的数组
JDK8以后,在长度比较长的时候,底层实现了优化

注意:
利用HashSet存储自定义元素,必须重写hashCode和equals方法

15 集合Map

15.1 概述

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

Map集合有点像对象嵌套对象。例如A对象嵌套B对象,C对象,此时的BC对象也叫Entry对象

15.2 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() 集合的长度,也就是集合中键值对的个数
V get(Object key) 根据键获取值
Set keySet() 获取所有键的集合
Set<Map.Entry<K,V>> entrySet() 获取所有键值对对象的集合
K getKey() 获得键
V getValue() 获得值

16 可变参数和不可变参数

16.1 可变参数

可变参数:就是形参的个数是可以变化的
格式:修饰符 返回值类型 方法名(数据类型… 变量名) { }
范例:public static int sum(int… a) { }

可变参数注意事项
这里的变量其实是一个数组
如果一个方法有多个参数,包含可变参数,可变参数要放在最后

16.2 不可变参数

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

在List、Set、Map接口中,都存在of方法,可以创建一个不可变的集合。
这个集合不能添加,不能删除,不能修改。
但是可以结合集合的带参构造,实现集合的批量添加
例如:ArrayList<String> list1 = new ArrayList<>(List.of("张三丰","张无忌","张翠山","王二麻子","张良","谢广坤"));

Map接口中,还有一个ofEntries方法可以提高代码的阅读性。
首先会把键值对封装成一个Entry对象,再把这个Entry对象添加到集合当中。

17 Stream流

有点像JS中的链式表达式,Promise.then().then()

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

17.1 获取Stream流方法

  • 单列集合 : 集合对象.stream();
  • 双列集合 : 不能直接获取,需要间接获取(keySet/entrySet)
    • 集合对象.keySet().stream();
    • 集合对象.entrySet().stream();
  • 数组 :
    • Arrays.stream(数组名);
  • 同种数据类型的多个数据:
    • Stream.of(数据1,数据2,数据3......);
public class MyStream2 {
    public static void main(String[] args) {
        //单列集合
        //method1();
        //双列集合
        //method2();
        //数组
        //method3();
        //同种数据类型的多个数据
        //method4();

    }

    private static void method4() {
        Stream.of(1,2,3,4,5,6,7,8).forEach(s-> System.out.println(s));
    }

    private static void method3() {
        int [] arr = {1,2,3,4,5};
        Arrays.stream(arr).forEach(s-> System.out.println(s));
    }

    private static void method2() {
        HashMap<String,Integer> hm = new HashMap<>();
        hm.put("zhangsan",23);
        hm.put("lisi",24);
        hm.put("wangwu",25);

        //keySet
        //hm.keySet().stream().forEach(s-> System.out.println(s));

        //entrySet
        hm.entrySet().stream().forEach(s-> System.out.println(s));
    }

    private static void method1() {
        ArrayList<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");

//        Stream<String> stream = list.stream();
//        stream.forEach(s-> System.out.println(s));
        list.stream().forEach(s-> System.out.println(s));
    }
}

17.2 Stream流中间方法

方法名 说明
Stream filter​(Predicate predicate) 用于对流中的数据进行过滤
Stream limit​(long maxSize): 截取指定参数个数的数据
Stream skip​(long n): 跳过指定参数个数的数据
static Stream concat​(Stream a, Stream b): 合并a和b两个流为一个流
Stream distinct​(): 去除流中重复的元素。依赖(hashCode和equals方法)
public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("张三丰");
        list.add("张无忌");
        list.add("张翠山");
        list.add("王二麻子");
        list.add("张良");
        list.add("谢广坤");
        list.add("谢广坤");


		list.stream().limit(2).forEach(s-> System.out.println(s));
		
		list.stream().skip(2).forEach(s-> System.out.println(s));
		
		ArrayList<String> list2 = new ArrayList<>();
        list2.add("张三丰");
        list2.add("张无忌");
        list2.add("张翠山");
        list2.add("王二麻子");
        list2.add("张良");
        list2.add("谢广坤");
		Stream<String> stream1 = list.stream();
        Stream<String> stream2 = list2.stream();
        Stream.concat(list.stream(),list2.stream()).forEach(s-> System.out.println(s));

		list.stream().distinct().forEach(s-> System.out.println(s));


    }

17.3 Stream流终结方法

方法名 说明
void forEach​(Consumer action) 对此流的每个元素执行操作
long count​() 返回此流中的元素数
public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("张三丰");
        list.add("张无忌");
        list.add("张翠山");
        list.add("王二麻子");
        list.add("张良");
        list.add("谢广坤");

        list.stream().forEach(s->System.out.println(s));

        long count = list.stream().count();
        System.out.println(count);
    }

17.4 Stream流收集方法

Stream流的收集方法
R collect​(Collector collector)

工具类Collectors提供了具体的收集方式
public static <T> Collector toList​():把元素收集到List集合中
public static <T> Collector toSet​():把元素收集到Set集合中
public static Collector toMap​(Function keyMapper,Function valueMapper):把元素收集到Map集合中

public static void main(String[] args) {
        ArrayList<Integer> list1 = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            list1.add(i);
        }

        list1.add(10);
        list1.add(10);
        list1.add(10);
        list1.add(10);
        list1.add(10);

        //filter负责过滤数据的.
        //collect负责收集数据.
                //获取流中剩余的数据,但是他不负责创建容器,也不负责把数据添加到容器中.
        //Collectors.toList() : 在底层会创建一个List集合.并把所有的数据添加到List集合中.
        List<Integer> list = list1.stream().filter(number -> number % 2 == 0)
                .collect(Collectors.toList());

        System.out.println(list);


        Set<Integer> set = list1.stream().filter(number -> number % 2 == 0)
                .collect(Collectors.toSet());
        System.out.println(set);
    }

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/84937.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!