每日筑基之集合二&&Stream

一.可变参数

就是一种特殊形参,定义在方法,构造器的形参列表里,定义格式是:方法名(数据类型...,形参名称){}

特点:可以不传数据给它;可以传一个或者同时传多个数据给它;也可以传一个数组给它。

好处:常常用来灵活的接收数据。

**可变参数在方法内部就是一个数组

**一个形参列表中可变参数只能有一个

**可变参数必须放在形参列表的最后面

二.Collections

是一个用来操作集合的工具类.

Collection:单列集合的根接口

package com.itheima.b_collections;

import java.util.*;

/*
Collections
    这是一个用于操作单列集合的工具类
    注意跟Collection的区别(Collection是单列集合的根接口)

常用方法
    static <T> boolean addAll(单列集合,可变参数)   批量添加元素
    static void shuffle(List集合)    打乱List集合元素顺序,每次调用都会打乱
    static <T> void sort(List集合)   List集合进行自然排序
    static <T> void sort(List集合,比较器)  List集合进行比较器排序
*/
public class Demo {
    public static void main(String[] args) {
        //static <T> boolean addAll(单列集合,可变参数)   批量添加元素
        List<String > students = new ArrayList<>();
        Collections.addAll(students,"玄奘","悟净","悟空","悟能");
        System.out.println(students);
        //static void shuffle(List集合)
        // 打乱List集合元素顺序,每次调用都会打乱
        System.out.println("+++++");
        Collections.shuffle(students);
        System.out.println(students);
        //static <T> void  sort(List集合)   List集合进行自然排序
        System.out.println("======");
        Collections.sort(students);
        System.out.println(students);
        //排自定义类对象,需要指定排序规则
        List<Student> stuList = new ArrayList<>();
        stuList.add(new Student("zhangsan", 18));
        stuList.add(new Student("wangwu", 22));
        stuList.add(new Student("zhaoliu", 21));
        stuList.add(new Student("lisi", 19));
        stuList.add(new Student("qianqi", 20));

        //static<T> void sort(List集合,比较器);List集合进行比较器排序
        Collections.sort(stuList, ( o1,  o2) -> o1.getAge()-o2.getAge());
        for (Student student : stuList) {
            System.out.println(student);
        }
    }
}


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


三.Map集合

Map集合被称为双列集合,一次需要存储一对数据做为一个元素,格式:{key1=value1,...}

Map集合的元素分为两部分:key和value,key成为键,value称为值,整体叫键值对

Map的键是不允许重复的,但值可以是重复的,键和值是一一对应的.

**当需要存储一一对应的数据时,就可以考虑使用Map集合来做

四.集合的常用API

五.Map的三种遍历方式

1.键找值

先得到键的集合,对该集合进行遍历,得到值,最后遍历输出

2.键值对--entrySet()

3.lambda表达式   (k,v)->{}

     

经典案例

package com.itheima.c_map;

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

/*
现有字符串数组如下:
    String[] bookArr = {"《红楼梦》-曹雪芹","《西游记》-吴承恩","《三国演义》-罗贯中","《水浒传》-施耐庵"};
需求:
    请将字符串中的书名提取为Map集合的键,将作者提取为Map集合的值
    并使用三种不同方式,遍历Map集合打印键值对元素内容
*/
public class Demo3 {
    public static void main(String[] args) {
        String[] bookArr = {"《红楼梦》-曹雪芹","《西游记》-吴承恩","《三国演义》-罗贯中","《水浒传》-施耐庵"};
        HashMap<String, String> map = new HashMap<>();
        for (String s : bookArr) {
            String[] str = s.split("-");
            map.put(str[0],str[1]);
        }
        //第一种
        Set<String> keySet = map.keySet();
        for (String s : keySet) {
            String s1 = map.get(s);
            System.out.println(s+s1);
        }
        //
        Set<Map.Entry<String, String>> entrySet = map.entrySet();
        for (Map.Entry<String, String> stringStringEntry : entrySet) {
            String key = stringStringEntry.getKey();
            String value = stringStringEntry.getValue();
            System.out.println(key+value);
        }
        //
        map.forEach((k,v)->{
            System.out.println(k+v);
        });
    }

}

  六.Map集合体系

1.HashMap

特点:无序,不重复

(1)HashMap的底层原理

它与HashSet的底层原理一样,都是基于哈希表实现的

实际上:Set系列集合的底层就是基于Map实现的,只是Set集合中的元素只要键数据,不要值数据而已

HashMap如何实现键的唯一性的?

依赖hashCode方法和equals方法保证键的唯一。 如果键要存储的是自定义对象,需要重写

hashCode和equals方法。

(2)HashMap 的存储机制

  1. 计算哈希码: 当调用 put 方法时,首先计算键(Key)的哈希码。键对象的 hashCode() 方法返回一个整数,这个整数将被用来确定该键值对在数组中的位置。如果键对象没有重写 hashCode() 方法,那么将使用 Object 类中的默认实现。

  2. 确定索引位置: 计算出哈希码之后,HashMap 使用一个算法(通常是哈希码与数组长度减一的结果取模)来确定键值对在数组中的索引位置。这一步骤是为了将哈希码映射到数组的有效索引范围内。

  3. 解决冲突: 如果多个键的哈希码映射到了数组的同一个位置上,那么这些键值对将会存储在一个链表或红黑树中。具体采用哪种结构取决于 HashMap 的版本和当前桶(bucket)中的元素数量。在 Java 8 中,当某个桶中的链表长度超过一定阈值(默认为 8)时,链表会被转换为红黑树以提高搜索效率。

  4. 插入键值对: 如果目标位置为空,那么键值对直接插入到该位置。如果目标位置已有元素(即发生了哈希冲突),则需要遍历链表或红黑树,使用键的 equals() 方法来检查是否已经有相同的键存在。如果找到相同的键,则更新对应的值;如果没有找到相同的键,则在链表末尾或红黑树中添加新的节点。

  5. 调整大小(扩容)HashMap 具有一个初始容量和一个负载因子(默认为 0.75)。当 HashMap 的大小(键值对的数量)超过了容量乘以负载因子的阈值时,HashMap 会自动调整其容量(通常是当前容量的两倍),并将所有的键值对重新计算哈希值,放入新的数组中。这个过程称为重新散列(rehashing)。

2.LinkedHashMap

特点:有序,不重复

(1)LinkedHashMap的底层原理

底层数据结构依然是基于哈希表实现的,只是每个键值对元素又额外的多了一个双链表的机制记录元素顺序(保证有序)

实际上:原来学习的LinkedHashSet集合的底层原理就是LinkedHashMap。

package teacher.com.itheima.c_map;

import java.util.LinkedHashMap;
import java.util.Map;

/*
LinkedHashMap :有序、不重复
    底层数据结构依然是基于哈希表实现的,只是每个键值对元素又额外的多了一个双链表的机制记录元素顺序(保证有序)。
    实际上:原来学习的LinkedHashSet集合的底层原理就是LinkedHashMap
*/
public class Demo5 {
    public static void main(String[] args) {
        //创建LinkedHashMap
        Map<String,String> map = new LinkedHashMap<>();
        //保存数据
        map.put("玄奘", "001");
        map.put("悟空", "002");
        map.put("悟净", "003");
        map.put("悟能", "004");
        //观察顺序
        map.forEach((k,v)->{
            System.out.println(k + "--" + v);
        });

    }
}

3.TreeMap

特点:不重复,无索引,可排序(按照键的大小默认排序,只能对键进行排序)

(1)底层原理:

与TreeSet底层原理一样,基于红黑树实现

(2)排序:

TreeMap集合同样也支持两种方式来指定排序规则 :

让类实现Comparable接口,重写比较规则。

TreeMap集合有一个有参数构造器,支持创建Comparator比较器对象,以便用来指定比较规则。

package teacher.com.itheima.c_map;

import java.util.Map;
import java.util.TreeMap;

/*
需求:创建一个TreeMap集合,键是学生对象(Student),值是籍贯(String)。
        学生属性姓名和年龄,按照年龄进行排序并遍历。
*/
public class Demo6 {
    private String put;

    public static void main(String[] args) {
        //创建集合
        Map<Teacher, String> map = new TreeMap<>((o1, o2) -> {
            if (o1.getAge() != o2.getAge()) {
                return o1.getAge() - o2.getAge();
            } else {
                return o1.getName().compareTo(o2.getName());
            }
        });

        map.put(new Teacher("张三", 21), "河北");
        map.put(new Teacher("李四", 20), "山东");
        map.put(new Teacher("王五", 19), "山西");
        map.put(new Teacher("赵六", 21), "河南");

        map.forEach((k, v) -> {
            System.out.println(k + "-----" + v);
        });
    }
}

class Teacher {
    //class Teacher implements Comparable<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 +
                '}';
    }

//    @Override
//    public int compareTo(Teacher o) {
//        if (this.age != o.age) {
//            return this.age - o.age;
//        }else {
//            return this.name.compareTo(o.name);
//        }
//    }
}

Map集合案例-省和市

package teacher.com.itheima.c_map;


import java.util.*;

/*
集合嵌套
    要求在程序中记住如下省份和其对应的城市信息,记录成功后,要求可以查询出湖北省的城市信息。

数据
    江苏省 = "南京市","扬州市","苏州市","无锡市","常州市"
    湖北省 = "武汉市","孝感市","十堰市","宜昌市","鄂州市"
    河北省 = "石家庄市","唐山市","邢台市","保定市","张家口市"

分析:
    定义一个Map集合,键用表示省份名称,值表示城市名称,注意:城市会有多个。 Map<String,List<String>>
    根据“湖北省”这个键获取对应的值展示即可。
*/
public class Demo7 {
    public static void main(String[] args) {
        //1、创建一个Map集合。key是一个字符串。value是一个List集合
        Map<String, List<String>> map = new HashMap<>();
        //2、向map中存入 江苏省和其对应的城市
        List<String> list1 = new ArrayList<>();
        Collections.addAll(list1,"南京市","扬州市","苏州市","无锡市","常州市");
        map.put("江苏省",list1);

        //3、向map中存入 湖北省和其对应的城市
        List<String> list2 = new ArrayList<>();
        Collections.addAll(list2,"武汉市","孝感市","十堰市","宜昌市","鄂州市");
        map.put("湖北省",list2);

        //4、向map中存入 河北省和其对应的城市
        List<String> list3 = new ArrayList<>();
        Collections.addAll(list3,"石家庄市","唐山市","邢台市","保定市","张家口市");
        map.put("河北省",list3);

        //5、从集合中查询所有  湖北省  的城市,并打印
        List<String> list = map.get("湖北省");
        System.out.println(list);
    }
}

七.Stream

1.定义:

可以用于操作集合或者数组的数据。

2.Stream流的操作流程:

它就像一条流水线,先获取流---->操作流----->终结流

/*
Stream流
    Jdk8开始新增的一套API,可以用于操作集合或者数组的数据

优势
    代码更简洁,可读性更好

使用流程
    集合/数组/…---->获取Stream流---->对流中的数据进行各种操作---->结果获取
                                   (排序、过滤、去重等等)     (收集、打印、统计)
*/
public class Demo1 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
        //需求:把集合中所有以"张"开头,且是3个字的元素存储到一个新的集合。
        
        //实现1: 使用传统方式实现
        List<String> res = new ArrayList<>();
        //循环所有数据
        for (String name : list) {
            if (name.startsWith("张") && name.length()==3) {
                res.add(name);
            }
        }

        System.out.println(res);

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

        //实现2: 使用Stream流方式实现
        list.stream().filter(name->name.startsWith("张") && name.length()==3)
                .forEach(System.out::println);
    }
}

3.如何获取流

/*
使用流程
    集合/数组/…---->获取Stream流---->对流中的数据进行各种操作---->结果获取
                                   (排序、过滤、去重等等)     (收集、打印、统计)

如何获取Stream流
    Collection集合:
        单列集合都支持一个stream()方法,它可以直接获取集合的Stream流
    数组:
        Arrays.stream(数组)
    零散数据:
        Stream.of(T... values)
    Map
        双列集合并没有提供直接获取Stream流的方法,他需要间接获取
*/
public class Demo2 {
    public static void main(String[] args) {
        //"玄奘", "悟空", "悟能", "悟净"
        List<String> list = new ArrayList<>();
        list.add("玄奘");
        list.add("悟空");
        list.add("悟能");
        list.add("悟净");
        //Collection集合: 单列集合都支持一个stream()方法,它可以直接获取集合的Stream流
        Stream<String> stream1 = list.stream();

        //数组:Arrays.stream(数组)
        String [] strs = {"玄奘", "悟空", "悟能", "悟净"};
        Stream<String> stream2 = Arrays.stream(strs);

        //零散数据:Stream.of(T... values)
        Stream<String> stream3 = Stream.of("张飞", "关羽", "赵云");

        //Map:双列集合并没有提供直接获取Stream流的方法,他需要间接获取
        Map<String, String> map = new HashMap<>();
        map.put("001", "玄奘");
        map.put("002", "悟空");
        map.put("003", "悟能");
        map.put("004", "悟净");

        //可以从map中获取所有的entry
        Set<Map.Entry<String, String>> entries = map.entrySet();
        Stream<Map.Entry<String, String>> stream4 = entries.stream();
    }
}

4.Stream流的中间方法

中间方法
    对stream流进行操作的方法, 他们调用完成后会返回新的Stream流,可以继续使用(支持链式编程)。

常见的中间方法
    Stream<T> filter(Predicate<? super T> p) 按照规则过滤,保留满足条件的元素
    Stream<T> sorted() 升序排序
    Stream<T> sorted(Comparator<? super T> c) 按照规则排序
    Stream<T> limit(long maxSize) 截取
    Stream<T> skip(long n)  跳过
    Stream<R> map(Function<? super T, extends R> mapper) 对元素进行加工,并返回对应的新流
    Stream<T> distinct() 去重
    static <T> Stream<T> concat(Stream a,Stream b) 合并流
*/
public class Demo3 {
    public static void main(String[] args) {
        List<Integer> list = List.of(61, 57, 66, 77, 88, 44, 100, 89, 97, 47, 70);
        //需求1: 找出所有及格的分数,并打印
        System.out.println("=================");
        list.stream().filter(e->e>60).forEach(System.out::println);

        //需求2: 找出所有及格的分数, 正序排列, 打印输出
        System.out.println("=================");
        list.stream()
                .filter(e -> e >= 60)
                .sorted()
                .forEach(System.out::println);


        //需求3: 找出所有及格的分数, 倒序排列, 打印输出
        System.out.println("=================");
        list.stream()
                .filter(e -> e >= 60)
                .sorted((o1, o2) -> o1 < o2 ? 1 : -1)
                .forEach(System.out::println);

        //需求4: 找出所有及格的分数, 倒序排列, 取前3名, 打印输出
        System.out.println("=================");
        list.stream()
                .filter(e -> e >= 60)
                .sorted((o1, o2) -> o1 < o2 ? 1 : -1)
                .limit(3)
                .forEach(System.out::println);

        //需求5: 找出所有及格的分数, 倒序排列, 取前4-6名, 打印输出
        System.out.println("=================");
        list.stream()
                .filter(e -> e >= 60)
                .sorted((o1, o2) -> o1 < o2 ? 1 : -1)
                .skip(3)
                .limit(3)
                .forEach(System.out::println);


        //需求6:  找出所有及格的分数, 倒序排列, 取前4-6名, 将每个人的分数加10分, 打印输出
        System.out.println("=================");
        list.stream()
                .filter(e -> e >= 60)
                .sorted((o1, o2) -> o1 < o2 ? 1 : -1)
                .skip(3)
                .limit(3)
                .map(e -> {return e + 10;})
                .forEach(System.out::println);
                


        //需求7: 将下面两个集合中的元素进行合并去重
//        System.out.println("=================");
//        String [] strs = {"玄奘", "悟空", "悟能", "悟净"};
//        Stream<String> stream1 = Arrays.stream(strs);
//
//        List<String> list1 = new ArrayList<>();
//        list1.add("张飞");
//        list1.add("关羽");
//        list1.add("赵云");
//        Stream<String> stream2 = list1.stream();
//
//        Stream.concat(stream1,stream2).forEach(System.out::println);




    }
}

5.Stream流的终结方法

终结方法
    调用完成后,不会返回新Stream了,没法继续使用流了。
    它的作用是对流中的数据进行筛选(遍历、最大、最小、统计个数)
    
常用方法
    void forEach(Consumer consumer)  遍历流中的数据
    long count() 统计流中元素的个数
    Optional max(Comparator<? super T> comparator) 获取流中的最大值
    Optional min(Comparator<?super T> comparator) 获取流中的最小值
*/
public class Demo4 {
    public static void main(String[] args) {
        List<Student> list = List.of(
                new Student("玄奘", 60, 165.5),
                new Student("悟空", 50, 175.5),
                new Student("悟能", 55, 145.5),
                new Student("悟净", 40, 185.5)
        );

        //1. 打印出集合中所有元素
        list.stream().forEach((e)->{
                    System.out.println(e);
                });

        //2. 统计出   身高不足170的  人数
        long count = list.stream()
                .filter(student -> student.getHeight() < 170)
                .count();
        System.out.println("count = " + count);

        //3. 请找出年龄最大的对象, 并输出(了解)
        Optional<Student> optional = list.stream()
                .max((o1, o2) -> o1.getAge() > o2.getAge() ? 1 : -1);

        Student student = optional.get();
        System.out.println(student);

        //4. 请找出身高最高的对象, 并输出(了解)
        Optional<Student> optional1 = list.stream().
                max((o1, o2) -> o1.getHeight() > o2.getHeight() ? 1 : -1);
        Student student1 = optional1.get();
        System.out.println(student1);
    }
}

class Student {
    private String name;
    private int age;
    private double height;

    public Student() {
    }

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

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

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

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


终结方法
    调用完成后,不会返回新Stream了,没法继续使用流了。
    它的作用是用来收集Stream流的结果转回到集合或者数组中去返回。

常见方法
    R collect(Collector collector); 将流中数据收集到指定集合,参数传递Collectors工具类调用对应方法
        Collectors.toList()
        Collectors.toSet()
        Collectors.toMap()
    Object[] toArray();
*/
public class Demo5 {
    public static void main(String[] args) {
        List<Teacher> list = List.of(
                new Teacher("玄奘", 60, 165.5),
                new Teacher("悟空", 50, 175.5),
                new Teacher("悟空", 50, 175.5),
                new Teacher("悟能", 55, 145.5),
                new Teacher("悟净", 40, 185.5));

        //1. 请找出身高超过170的教师,    并放到一个新数组中
        Object[] array = list.stream()
                .filter(e -> e.getHeight() > 170)
                .toArray();
        System.out.println(Arrays.toString(array));
        System.out.println("--------------------");

        //2. 请找出身高超过170的教师, 并放到一个新List集合中
        List<Teacher> list1 = list.stream()
                .filter(e -> e.getHeight() > 170)
                .collect(Collectors.toList());
        System.out.println(list1);

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

        //3. 请找出身高超过170的教师, 并放到一个新Set集合中
        Set<Teacher> set = list.stream()
                .filter(e -> e.getHeight() > 170)
                .collect(Collectors.toSet());
        System.out.println(set);


        //4. 请找出所有的教师的姓名和身高, 放到一个新Map集合中
        Map<String, Teacher> map = list.stream().distinct()
                .collect(Collectors.toMap(
                        teacher -> teacher.getName(),
                        teacher -> teacher));
        System.out.println(map);
    }
}

class Teacher {
    private String name;
    private int age;
    private double height;

    public Teacher() {
    }

    public Teacher(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }

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

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Teacher teacher = (Teacher) o;
        return age == teacher.age && Double.compare(teacher.height, height) == 0 && Objects.equals(name, teacher.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age, height);
    }

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值