Map集合、Stream流、File类、递归

一,JDK8新特性:Stream

1,认识Stream
  • 也叫Stream流,是jdk8开始新增的一套API (java.util.stream.*),可以用于操作集合或者数组的数据。

  • 优势: Stream流大量的结合了Lambda的语法风格来编程,提供了一种更加强大,更加简单的方式操作集合或者数组中的数据,代码更简洁,可读性更好。

案例需求:有一个List集合,元素有"河南大学","郑州大学","河南科技大学","黄淮学院","华北水利水电大学","河南工业大学","河南农业大学",找出河南开头,且是6个字的校名,存入到一个新集合中去。

HashSet<String> set = new HashSet<>();
Collections.addAll(set,"河南大学", "郑州大学", "河南科技大学", "黄淮学院", "华北水利水电大学", "河南工业大学", "河南农业大学");

用传统方式来做,代码是这样的

// 找出河南开头,且是6个字的校名,存入到一个新集合中去。
List<String> list = new ArrayList<>();
for (String name : set) {
    if(name.startsWith("张") && name.length() == 3){
        list.add(name);
    }
}
System.out.println(list);

用Stream流来做,代码是这样的(ps: 是不是想流水线一样,一句话就写完了)

List<String> list = set.stream().filter(s -> s.startsWith("河南") && s.length() == 6).collect(Collectors.toList());
	System.out.println(list);
}

在这里插入图片描述

下面我们就把目光定睛在Stream流的获取,对流的处理,以及总结方法。

2,Stream流的创建
主要掌握下面四点:
    1、如何获取List集合的Stream流?
    2、如何获取Set集合的Stream流?
    3、如何获取Map集合的Stream流?
    4、如何获取数组的Stream流?
// 1、如何获取List集合的Stream流?
List<String> names = new ArrayList<>();
Collections.addAll(names, "张三丰","张无忌","周芷若","赵敏","张强");
Stream<String> stream = names.stream();

// 2、如何获取Set集合的Stream流?
Set<String> set = new HashSet<>();
Collections.addAll(set, "刘德华","张曼玉","蜘蛛精","马德","德玛西亚");
Stream<String> stream1 = set.stream();
stream1.filter(s -> s.contains("德")).forEach(s -> System.out.println(s));

// 3、如何获取Map集合的Stream流?
Map<String, Double> map = new HashMap<>();
map.put("古力娜扎", 172.3);
map.put("迪丽热巴", 168.3);
map.put("马尔扎哈", 166.3);
map.put("卡尔扎巴", 168.3);

Set<String> keys = map.keySet();
Stream<String> ks = keys.stream();

Collection<Double> values = map.values();
Stream<Double> vs = values.stream();

Set<Map.Entry<String, Double>> entries = map.entrySet();
Stream<Map.Entry<String, Double>> kvs = entries.stream();
kvs.filter(e -> e.getKey().contains("巴"))
    .forEach(e -> System.out.println(e.getKey()+ "-->" + e.getValue()));

// 4、如何获取数组的Stream流?
String[] names2 = {"张翠山", "东方不败", "唐大山", "独孤求败"};
Stream<String> s1 = Arrays.stream(names2);
Stream<String> s2 = Stream.of(names2);

总结:Collection接口的实现类都可以通过对象调用stream( ) 方法来获取Stream对象。而对于map这个特殊的集合只能先获取其keys或values的对象,得到一个集合然后在调用其中的Stream对象 。当然也可以通过内部对键值对的封装的Set集合获取Stream对象。
而对于数组可以通过Arrays.stream()方法来获取。

对于不太确定或者已经确定的数据都可以视同Stream类的静态方法 of()来获取对象。很香。类比于Collections的addAll方法。

Stream<String> dx = Stream.of("河南大学", "郑州大学", "河南科技大学", "华北水利水电大学", "河南工业大学");
3,Stream的常用方法

上面学习了Stream流的获取,下面再学习 第二步,Stream流常见的中间方法。

Stream****提供的常用中间方法说明
Stream filter(Predicate<? super T> predicate)用于对流中的数据进行过滤。
Stream sorted()对元素进行升序排序
Stream sorted(Comparator<? super T > comparator)按照指定规则排序
Stream limit(long maxSize)获取前几个元素
Stream skip(long n)跳过前几个元素
Stream distinct()去除流中重复的元素。
Stream map(Function <? super T ,? extends R> mapper)对元素进行加工,并返回对应的新流
static Stream concat(Stream a, Stream b)合并a和b两个流为一个流

通过下面的代码做一个练习:

下面的代码大量的运用lambda,如果没有学,可以看前面的笔记,有详细的介绍。对于lambda表达式中filter方法。可以这样理解其他的也类似。s -> s >= 60 前面代表当前对象,后面的代表的是条件,也就是只有当后面成立的时候(为true)才会将当前的元素,添加到Stream流中。基本都是这种思想

        List<Double> scores = new ArrayList<>();
        Collections.addAll(scores, 88.5, 100.0, 60.0, 99.0, 9.5, 99.6, 25.0);
        // 需求1:找出成绩大于等于60分的数据,并升序后,再输出。
        scores.stream().filter(s -> s >= 60).sorted().forEach(s -> System.out.println(s));

        List<Student> students = new ArrayList<>();
        Student s1 = new Student("lucky", 26, 172.5);
        Student s2 = new Student("Lucy", 26, 172.5);
        Student s3 = new Student("bob", 23, 167.6);
        Student s4 = new Student("jack", 25, 169.0);
        Student s5 = new Student("张一", 35, 183.3);
        Student s6 = new Student("之辈", 34, 168.5);
        Collections.addAll(students, s1, s2, s3, s4, s5, s6);
        // 需求2:找出年龄大于等于23,且年龄小于等于30岁的学生,并按照年龄降序输出.
        students.stream().filter(s -> s.getAge() >= 23 && s.getAge() <= 30)
                .sorted((o1, o2) -> o2.getAge() - o1.getAge())
                .forEach(s -> System.out.println(s));

        // 需求3:取出身高最高的前3名学生,并输出。
        students.stream().sorted((o1, o2) -> Double.compare(o2.getHeight(), o1.getHeight()))
                .limit(3).forEach(System.out::println);
        System.out.println("-----------------------------------------------");

        // 需求4:取出身高倒数的2名学生,并输出。   s1 s2 s3 s4 s5 s6
        students.stream().sorted((o1, o2) -> Double.compare(o2.getHeight(), o1.getHeight()))
                .skip(students.size() - 2).forEach(System.out::println);

        // 需求5:找出身高超过168的学生叫什么名字,要求去除重复的名字,再输出。
        students.stream().filter(s -> s.getHeight() > 168).map(Student::getName)
                .distinct().forEach(System.out::println);

        // distinct去重复,自定义类型的对象(希望内容一样就认为重复,重写hashCode,equals)
        students.stream().filter(s -> s.getHeight() > 168)
                .distinct().forEach(System.out::println);

        Stream<String> st1 = Stream.of("张三", "李四");
        Stream<String> st2 = Stream.of("张三2", "李四2", "王五");
        Stream<String> allSt = Stream.concat(st1, st2);
        allSt.forEach(System.out::println);

还有一个map方法:这个方法用于对值进行操作然后得到返回之后的数据添加到Stream流中的。也很常用

// 需求,将字符串中的数据先切分,然后找出其中的偶数并输出
        String str = "1,2,3,4,5,6,8,4,6,3,56,3,5,3";
        String[] split = str.split(",");
        Stream<String> stream = Stream.of(split);
        stream.map(Integer::parseInt).filter(s -> s % 2 == 0).forEach(System.out::println);
4,Stream流终结方法(重点)

终结方法指的是调用完成后,不会返回新Stream了,没法继续使用流了。

Stream****提供的常用终结方法说明
void forEach(Consumer action)对此流运算后的元素执行遍历
long count()统计此流运算后的元素个数
Optional max(Comparator<? super T> comparator)获取此流运算后的最大值元素
Optional min (Comparator<? super T> comparator)获取此流运算后的最小值元素
5,收集Stream流

收集Stream流:就是把Stream流操作后的结果转回到集合或者数组中去返回。

Stream流:方便操作集合/数组的手段; 集合/数组:才是开发中的目的

Stream 提供的常用终结方法说明
R collect(Collector collector)把流处理后的结果收集到一个指定的集合中去
Object[] toArray()把流处理后的结果收集到一个数组中去
Collectors 工具类提供了具体的收集方式说明
public static Collector toList()把元素收集到List集合中
public static Collector toSet()把元素收集到Set集合中
public static Collector toMap(Function keyMapper ,
Function valueMapper)
把元素收集到Map集合中
两个参数分别为每一个k和每一个v

二,File类

要使用类和之前的思路一样,肯定是要获取到对象。两种方法,一种是通过静态方法,另一种就是通过构造方法。File不能通过静态方法获取的对象。那么就是构造方法啦,下面就来学习构造方法

构造器说明
public File(String pathname)根据文件路径创建文件对象
public File(String parent, String child)根据父路径和子路径名字创建文件对象
public File(File parent, String child)根据父路径对应文件对象和子路径名字创建文件对象

注意:

  1. File既可代表文件,也可以代表文件夹。File封装的对象仅仅是一个路径名称,这个路径可以是存在也可以是不存在的,也允许<这个到后面的io就会理解,因为如果不存在那么java会帮我们创建>
  2. 需求我们注意的是:路径中"\“要写成”\\“, 路径中”/"可以直接用

示例代码:

// 1、创建一个File对象,指代某个具体的文件。
        // 路径分隔符
        // File f1 = new File("D:/resource/ab.txt");
        // File f1 = new File("D:\\resource\\ab.txt");
        File f1 = new File("D:" + File.separator +"resource" + File.separator + "ab.txt");
        System.out.println(f1.length()); // 文件大小

        File f2 = new File("D:/resource");
        System.out.println(f2.length());

        // 注意:File对象可以指代一个不存在的文件路径
        File f3 = new File("D:/resource/aaaa.txt");
        System.out.println(f3.length());
        System.out.println(f3.exists()); // false

        // 我现在要定位的文件是在模块中,应该怎么定位呢?
        // 绝对路径:带盘符的
        // File f4 = new File("D:\\code\\javasepromax\\file-io-app\\src\\itheima.txt");
        // 相对路径(重点):不带盘符,默认是直接去工程下寻找文件的。
        File f4 = new File("file-io-app\\src\\itheima.txt");
        System.out.println(f4.length());

在学习方法之前还需要了解两个概念绝对路径和相对路径:<在后面常用>:

绝对路径:

相对于盘符,即 C盘,D盘

File file1 = new File(D:\\demo\\a.txt”); 
相对路径:

在idea中是相对于项目目录:

File file3 = new File(“模块名\\a.txt”); 

下面就来学习相对方法:

常用方法1:判断文件类型,获取文件信息
方法名称说明
public boolean exists()判断当前文件对象,对应的文件路径是否存在,存在返回true
public boolean isFile()判断当前文件对象指代的是否是文件,是文件返回true,反之。
public boolean isDirectory()判断当前文件对象指代的是否是文件夹,是文件夹返回true,反之。
public String getName()获取文件的名称(包含后缀)
public long length()获取文件的大小,返回字节个数
public long lastModified()获取文件的最后修改时间。
public String getPath()获取创建文件对象时,使用的路径 <也就是构造器中的路径,给相对就是相对>
public String getAbsolutePath()获取绝对路径

示例代码:

// 1.创建文件对象,指代某个文件
File f1 = new File("D:/resource/ab.txt");
//File f1 = new File("D:/resource/");

// 2、public boolean exists():判断当前文件对象,对应的文件路径是否存在,存在返回true.
System.out.println(f1.exists());

// 3、public boolean isFile() : 判断当前文件对象指代的是否是文件,是文件返回true,反之。
System.out.println(f1.isFile());

// 4、public boolean isDirectory()  : 判断当前文件对象指代的是否是文件夹,是文件夹返回true,反之。
System.out.println(f1.isDirectory());

// 除了这些判断功能还有一些获取的功能
File f1 = new File("D:/resource/ab.txt");

// 5.public String getName():获取文件的名称(包含后缀)
System.out.println(f1.getName());

// 6.public long length():获取文件的大小,返回字节个数
System.out.println(f1.length());

// 7.public long lastModified():获取文件的最后修改时间。
long time = f1.lastModified();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
System.out.println(sdf.format(time));

// 8.public String getPath():获取创建文件对象时,使用的路径
File f2 = new File("D:\\resource\\ab.txt");
File f3 = new File("file-io-app\\src\\itheima.txt");
System.out.println(f2.getPath());
System.out.println(f3.getPath());

// 9.public String getAbsolutePath():获取绝对路径
System.out.println(f2.getAbsolutePath());
System.out.println(f3.getAbsolutePath());
常用方法2:创建文件,删除文件
方法名称说明
public boolean createNewFile()创建一个新的空的文件
public boolean mkdir()只能创建一级文件夹
public boolean mkdirs()可以创建多级文件夹
方法名称说明
public boolean delete()删除文件、空文件夹
// 1、public boolean createNewFile():创建一个新文件(文件内容为空),创建成功返回true,反之。
File f1 = new File("D:/resource/itheima2.txt");
System.out.println(f1.createNewFile());

// 2、public boolean mkdir():用于创建文件夹,注意:只能创建一级文件夹
File f2 = new File("D:/resource/aaa");
System.out.println(f2.mkdir());

// 3、public boolean mkdirs():用于创建文件夹,注意:可以创建多级文件夹
File f3 = new File("D:/resource/bbb/ccc/ddd/eee/fff/ggg");
System.out.println(f3.mkdirs());

// 3、public boolean delete():删除文件,或者空文件,注意:不能删除非空文件夹。
System.out.println(f1.delete());
System.out.println(f2.delete());
File f4 = new File("D:/resource");
System.out.println(f4.delete());
1.mkdir(): 只能创建单级文件夹、
2.mkdirs(): 才能创建多级文件夹
3.delete(): 文件可以直接删除,但是文件夹只能删除空的文件夹,文件夹有内容删除不了。并且删除后的文件不仅回收站
常用方法3:遍历文件夹
方法名称说明
public String[] list()获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回。
public File[] listFiles()获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点)
// 1、public String[] list():获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回。
File f1 = new File("D:\\course\\待研发内容");
String[] names = f1.list();
for (String name : names) {
    System.out.println(name);
}

// 2、public File[] listFiles():(重点)获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点)
File[] files = f1.listFiles();
for (File file : files) {
    System.out.println(file.getAbsolutePath());
}

File f = new File("D:/resource/aaa");
File[] files1 = f.listFiles();
System.out.println(Arrays.toString(files1));
   

文件的遍历中的几个注意事项:

1.当主调是文件时,或者路径不存在时,返回null
2.当主调是空文件夹时,返回一个长度为0的数组
3.当主调是一个有内容的文件夹时,将里面所有一级文件和文件夹路径放在File数组中,并把数组返回
4.当主调是一个文件夹,且里面有隐藏文件时,将里面所有文件和文件夹的路径放在FIle数组中,包含隐藏文件
5.当主调是一个文件夹,但是没有权限访问时,返回null

三,递归

1,认识递归

什么是递归?

递归是一种算法,从形式上来说,方法调用自己的形式称之为递归。

递归的形式: 有直接递归、间接递归,如下面的代码。

public class RecursionTest1 {
    public static void main(String[] args) {
        test1();
    }

    // 直接方法递归
    public static void test1(){
        System.out.println("----test1---");
        test1(); // 直接方法递归
    }

    // 间接方法递归
    public static void test2(){
        System.out.println("---test2---");
        test3();
    }

    public static void test3(){
        test2(); // 间接递归
    }
}
// 上面只是对递归的形式做一个说明,不具有实际意义

直接递归就是自己调用自己的方法。间接递归是自己调用别的方法,但是别的方法又调用了自己的方法。

从需求来学习:之前我们可能学过阶层,可以通过基础的java代码得到结果,那么怎样通过递归来获取呢?

假设f(n)表示n的阶乘,那么我们可以推导出下面的式子
    f(5) = 1+2+3+4+5
    f(5) = f(4)+5
    f(4) = f(3)+4
    f(3) = f(2)+3
    f(2) = f(1)+2
    f(1) = 1

可以推导是一个公式:

即: f(n) = f(n-1) * n

我们也会发现一个出口:

就是f(1) =1的时候,这个时候我们就无法在套用公式,这个就是我们的递归出口。

总结一下,递归的三个条件

1. 要有递归公式
2. 要有递归出口 / 终止条件
3. 递归的方法必须走向递归出口

比如计算阶层的方法可以这样写:

    public static void main(String[] args) {
        System.out.println("5的阶乘是:" + f(5));
    }

    //求n个数的阶乘
    public static int f(int n){
        // 递归出口
        if(n == 1){
            return 1;
        }else {
         // 递归体
            return f(n - 1) * n;
        }
    }

综合案例:

从指定的文件夹找出所有符合或者包含条件的文件:并打印

public static void main(String[] args) throws Exception {
          searchFile(new File("D:/") , "QQ.exe");
    }

    /**
     * 去目录下搜索某个文件
     * @param dir  目录
     * @param fileName 要搜索的文件名称
     */
    public static void searchFile(File dir, String fileName) throws Exception {
        // 1、把非法的情况都拦截住
        if(dir == null || !dir.exists() || dir.isFile()){
            return; // 代表无法搜索
        }

        // 2、dir不是null,存在,一定是目录对象。
        // 获取当前目录下的全部一级文件对象。
        File[] files = dir.listFiles();

        // 3、判断当前目录下是否存在一级文件对象,以及是否可以拿到一级文件对象。
        if(files != null && files.length > 0){
            // 4、遍历全部一级文件对象。
            for (File f : files) {
                // 5、判断文件是否是文件,还是文件夹
                if(f.isFile()){
                    // 是文件,判断这个文件名是否是我们要找的
                    if(f.getName().contains(fileName)){
                        System.out.println("找到了:" + f.getAbsolutePath());
                        Runtime runtime = Runtime.getRuntime();
                        runtime.exec(f.getAbsolutePath());
                    }
                }else {
                    // 是文件夹,继续重复这个过程(递归)
                    searchFile(f, fileName);
                }
            }
        }
    }
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yfs1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值