目录
Map
HashMap集合的底层原理
HashMap跟HashSet的底层原理是一模一样的,都是基于哈希表实现的。
实际上:原来学的Set系列集合的底层就是基于Map实现的,只是Set集合中的元素只要键数据,不要值数据而已。
但是它是无序,不能重复,没有索引支持的(由键决定特点)
HashMap的键依赖hashCode方法和equals方法保证键的唯一
如果键存储的是自定义类型的对象,可以通过重写hashCode和equals方法,这样可以保证多个对象内容一样时,HashMap集合就能认为是重复的。
总结:
LinkedHashMap
由键决定,有序,无重复,无索引
LinkedHashMap集合原理
底层数据结构依然是基于哈希表实现的,只是每个键值对元素又额外的多了一个双链表的机制记录元素顺序(保证有序)。
实际上:原来学习的LinkedHashSet集合的底层原理就是LinkedHashMap.
TreeMap
TreeMap(由键决定特点):按照键的大小默认升序排序、不重复、无索引。
原理:TreeaSet和TreeMap的底层原理一样,都是基于红黑树实现的排序
TreeMap集合同样也支持两种方式来指定排序规则
让类实现Comparable接口,重写比较规则。
TreeMap集合有一个有参数构造器,支持创建Comparator比较器对象,以便用来指定比较规则。
总结:
集合的嵌套
指的是集合中的元素又有一个集合
/**
* 目标:理解集合的嵌套。
* 江苏省 = "南京市","扬州市","苏州市“,"无锡市","常州市"
* 湖北省 = "武汉市","孝感市","十堰市","宜昌市","鄂州市"
* 河北省 = "石家庄市","唐山市", "邢台市", "保定市", "张家口市"
*/
public class Test {
public static void main(String[] args) {
// 1、定义一个Map集合存储全部的省份信息,和其对应的城市信息。
ArrayList<String> cities1 = new ArrayList<>();
ArrayList<String> cities2 = new ArrayList<>();
ArrayList<String> cities3 = new ArrayList<>();
Collections.addAll(cities1, "南京市","扬州市","苏州市","无锡市","常州市");
// System.out.println("cities1 = " + cities1);
Collections.addAll(cities2, "武汉市","孝感市","十堰市","宜昌市","鄂州市");
Collections.addAll(cities3, "石家庄市","唐山市", "邢台市", "保定市", "张家口市");
//Map去保存省和对应市的关系
HashMap<String, ArrayList<String>> provinces = new HashMap<>();
provinces.put("江苏省", cities1);
provinces.put("湖北省", cities2);
provinces.put("河北省", cities3);
//遍历
Set<Map.Entry<String, ArrayList<String>>> entries = provinces.entrySet();
for (Map.Entry<String, ArrayList<String>> entry : entries) {
String key = entry.getKey();
ArrayList<String> value = entry.getValue();
System.out.println(key + " = " + value);
}
}
}
JDK8新特性:Stream
什么是Stream:
也叫Stream流,是ldk8开始新增的一套APl (java.util.stream.*),可以用于操作集合或者数组的数据。
优势:Stream流大量的结合了Lambda的语法风格来编程,提供了一种更加强大,更加简单的方式操作集合或者数组中的数据,代码更简洁,可读性更好。
Stream流的使用步骤
总结:
1.获取Stream流
/**
* 目标:掌握Stream流的创建。
*
* List 和Set集合都是Collection的子类型,都可以直接用stream方法获取流
* 2.Map双列集合都没有直接获取流的方法,需要转为单列集合
* keySet 处理键
* values 处理值
* entrySet 键和值都要处理
*/
public class StreamTest2 {
public static void main(String[] args) {
// 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);
//处理键
map.keySet().stream().forEach(System.out::println);
//处理值
map.values().stream().forEach(System.out::println);
//处理键和值
map.entrySet().stream().forEach(System.out::println);
System.out.println("===============");
map.entrySet().stream().forEach(entry -> {
System.out.println(entry.getKey() + "-->" + entry.getValue());
});
// 4、如何获取数组的Stream流?
System.out.println("==============");
String[] names2 = {"张翠山", "东方不败", "唐大山", "独孤求败"};
//方案一:
Arrays.stream(names2).forEach(System.out::println);
//方案二:
Stream.of(names2).forEach(System.out::println);
}
}
2.Stream流常见的中间方法
中间方法指的是调用完成后会返回新的Stream流,可以继续使用(支持链式编程)
/**
* 目标:掌握Stream流提供的常见中间方法。
*/
public class StreamTest3 {
public static void main(String[] args) {
// 需求1:已知如下数据,找出成绩大于等于60分的数据,并升序后,再输出。
List<Double> scores = new ArrayList<>();
Collections.addAll(scores, 88.5, 100.0, 60.0, 99.0, 9.5, 99.6, 25.0);
scores.stream().filter(score -> score >= 60)
// .sorted()//默认按照元素的排序规则升序排序
.sorted((s1,s2) -> s2.compareTo(s1)).forEach(System.out::println);
System.out.println("==================");
List<Student> students = new ArrayList<>();
Student s1 = new Student("蜘蛛精", 26, 172.5);
Student s2 = new Student("蜘蛛精", 26, 172.5);
Student s3 = new Student("紫霞", 23, 167.6);
Student s4 = new Student("白晶晶", 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:已知students学生集合,找出年龄大于等于23,且年龄小于等于30岁的学生,并按照年龄降序输出.
students.stream()
.filter(student -> student.getAge() >= 23 && student.getAge() <= 30)
.sorted((stu1, stu2) -> Integer.compare(stu2.getAge(), stu2.getAge()))
.forEach(System.out::println);
System.out.println("=================");
// 需求3:已知students学生集合,取出身高最高的前3名学生,并输出。
students.stream()
.sorted((stu1, stu2) -> Double.compare(stu2.getHeight(), stu2.getHeight()))
.limit(3).forEach(System.out::println);
System.out.println("===============");
// 需求4:取出身高倒数2名学生,并输出学生信息。
students.stream()
.sorted((stu1, stu2) -> Double.compare(stu2.getHeight(), stu2.getHeight()))
.skip(students.size()-2).forEach(System.out::println);
System.out.println("===============");
// 需求5:找出身高超过168的学生叫什么名字,要求去除重复的名字,再输出。
// distinct去重复,自定义类型的对象(希望内容一样就认为重复,重写hashCode,equals)
students.stream().filter(student -> student.getHeight() >= 168)
.distinct()//去重的是学生对象
.forEach(System.out::println);
System.out.println("=============");
//需求6:把学生集合中所有的名字拿出来,并把重复的名字去重
students.stream().map((student) -> student.getName())//把流中的学生对象转换为了名字字符串对象
.distinct()//去重的是字符串
.forEach(System.out::println);
System.out.println("=================");
//需求7:已知第一组的名字和第二组的名字,把两个组生成的流合并为一个流
Stream<String> stream1 = Stream.of("张三丰", "张无忌", "张翠山");
Stream<String> stream2 = Stream.of("灭绝师太", "周芷若", "赵敏");
Stream.concat(stream1, stream2).forEach(System.out::println);
}
}
3.Stream的终结方法
终结方法指的是调用完成后,不会返回新Stream了,没法继续使用流了。
收集stream流: 就是把stream流操作后的结果转回到集合或者数组中去返回。
Stream流: 方便操作集合/数组的手段;
集合/数组: 才是开发中的目的。
/**
* 目标:Stream流的终结方法
*/
public class StreamTest4 {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
Student s1 = new Student("蜘蛛精", 26, 172.5);
Student s2 = new Student("蜘蛛精", 26, 172.5);
Student s3 = new Student("紫霞", 23, 167.6);
Student s4 = new Student("白晶晶", 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);
//已知students集合存储了若干数据
// 需求1:请计算出身高超过168的学生有几人。
long count = students.stream().filter(student -> student.getHeight() >= 168)
.count();
System.out.println("count = " + count);
// 需求2:请找出身高最高的学生对象,并输出。
Student student1 = students.stream().max((stu1, stu2) -> Double.compare(stu1.getHeight(), stu2.getHeight()))
.get();
System.out.println("student1 = " + student1);
// 需求3:请找出身高最矮的学生对象,并输出。
Student student2 = students.stream().min((o1, o2) -> Double.compare(o1.getHeight(), o2.getHeight())).get();
System.out.println("student2 = " + student2);
// 需求4:请找出身高超过170的学生对象,并放到一个新集合中去返回。
List<Student> list = students.stream().filter(student -> student.getHeight() >= 170)
.collect(Collectors.toList());
System.out.println("list = " + list);
// 需求5:请找出身高超过170的学生对象,并把学生对象的名字和身高,存入到一个Map集合返回。
//Map<String,Double>
Map<String, Double> map = students.stream().filter(student -> student.getHeight() > 170)
.distinct().collect(Collectors.toMap(Student::getName, student -> student.getHeight()));
System.out.println("map = " + map);
//变数组
Object[] objects = students.stream().filter(student -> student.getHeight() > 170)
.toArray();
System.out.println(Arrays.toString(objects));
//
Student[] array = students.stream().filter(student -> student.getHeight() > 170).toArray(len -> new Student[len]);
System.out.println("Arrays.toString(array) = " + Arrays.toString(array));
}
}
File、IO流(一)
他们都是内存中的数据容器,他们记住的数据会在断电时永久丢失。
有些数据想要长久的保存起来的方法
文件是非常重要的存储方式,在计算机硬盘中。
即便断电,或者程序终止了,存储在硬盘文件中的数据也不会丢失。
File(代表文本)
File是java.io.包下的类,File类的对象,用于代表当前操作系统的文件(可以是文件、或文件夹)。
注意:
File类只能对文件本身进行操作,不能读写文件里面存储的数据。
创建对象
绝对路径:从盘符开始
相对路径:不带盘符,默认直接到当前工程目录下的目标寻找文件
总结:
常用方法1:判断文件类型,获取文件信息
File提供的判断文件类型、获取文件信息功能
/**
目标:掌握File提供的判断文件类型、获取文件信息功能
*/
public class FileTest2 {
public static void main(String[] args) throws UnsupportedEncodingException {
// 1.创建文件对象,指代某个文件
File file1 = new File("D:\\abc\\HelloWord.txt");
File file2 = new File("D:\\abc");
File file3 = new File("D:\\abc\\zhuxian.txt");
// 2、public boolean exists():判断当前文件对象,对应的文件路径是否存在,存在返回true.
System.out.println("file1.exists() = " + file1.exists());//T
System.out.println("file2.exists() = " + file2.exists());//T
System.out.println("file3.exists() = " + file3.exists());//F
System.out.println("==================");
// 3、public boolean isFile() : 判断当前文件对象指代的是否是文件,是文件返回true,反之。
System.out.println("file1.isFile() = " + file1.isFile());//F
System.out.println("file2.isFile() = " + file2.isFile());//T
System.out.println("file3.isFile() = " + file3.isFile());//F
System.out.println("================");
// 4、public boolean isDirectory() : 判断当前文件对象指代的是否是文件夹,是文件夹返回true,反之。
System.out.println("file1.isDirectory() = " + file1.isDirectory());//F
System.out.println("file2.isDirectory() = " + file2.isDirectory());//T
System.out.println("file3.isDirectory() = " + file3.isDirectory());//F
System.out.println("================");
// 5.public String getName():获取文件的名称(包含后缀)
System.out.println("file1.getName() = " + file1.getName());//HelloWord.txt
System.out.println("file2.getName() = " + file2.getName());//abc
System.out.println("file3.getName() = " + file3.getName());//zhuxian.txt
System.out.println("================");
// 6.public long length():获取文件的大小,返回字节个数
System.out.println("file1.length() = " + file1.length());
System.out.println("file2.length() = " + file2.length());
System.out.println("file3.length() = " + file3.length());
System.out.println("================");
// 7.public long lastModified():获取文件的最后修改时间。
System.out.println("================");
// 8.public String getPath():获取创建文件对象时,使用的路径
System.out.println("file1.getPath() = " + file1.getPath());
System.out.println("file2.getPath() = " + file2.getPath());
System.out.println("file3.getPath() = " + file3.getPath());
// 9.public String getAbsolutePath():获取绝对路径
System.out.println("file1.getAbsolutePath() = " + file1.getAbsolutePath());
}
}
常用方法2:创建文件,删除文件
File类创建文件的功能
File类删除文件的功能
注意:delete方法默认只能删除文件和空文件夹,删除后的文件不会进入回收站。
/**
* 目标:掌握File创建和删除文件相关的方法。
*/
public class FileTest3 {
public static void main(String[] args) throws Exception {
// 1、public boolean createNewFile():创建一个新文件(文件内容为空),创建成功返回true,反之。
File file = new File("D:\\abc");
//如果文件所在的父路径不存在就会报错
//如果文件存在,再次创建会返回false
System.out.println(file.createNewFile());
// 2、public boolean mkdir():用于创建文件夹,注意:只能创建一级文件夹
File file2 = new File("D:\\abc\\music");
System.out.println("file2.mkdir() = " + file2.mkdir());
// 3、public boolean mkdirs():用于创建文件夹,注意:可以创建多级文件夹
File file3 = new File("D:\\abc\\movie\\周星驰");
System.out.println("file3.mkdirs() = " + file3.mkdirs());
// 3、public boolean delete():删除文件,或者空文件,注意:不能删除非空文件夹。
File file4 = new File("D:\\abc\\HelloWord.txt");
System.out.println("file4.delete() = " + file4.delete());
//删除文件夹(文件夹需要是空的)
File file5 = new File("D:\\abc\\music");
System.out.println("file5.delete() = " + file5.delete());
}
}
常用方法3:遍历文件夹
/**
* 目标:掌握File提供的遍历文件夹的方法。
*/
public class FileTest4 {
public static void main(String[] args) {
// 1、public String[] list():获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回。
File file = new File("day08map-stream-app/src/com/itheima/d4_file");
System.out.println("file.exists() = " + file.exists());
String[] list = file.list();
Stream.of(list).forEach(System.out::println);
// 2、public File[] listFiles():(重点)获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点)
File[] files = file.listFiles();
Stream.of(files).forEach(file1 -> System.out.println(file.getAbsolutePath()));
//当主调是一个有内容的文件夹时,将里面所有一级文件和文件夹的路径放在File数组中返回
File file2 = new File("xxxx");
File file3 = new File("day08map-stream-app/itheima/d4_file");
System.out.println("file2.listFiles() = " + file2.listFiles());//null
System.out.println("file3.listFiles() = " + file3.listFiles());//null
}
}
使用listFiles方法时的注意事项:
当主调是文件,或者路径不存在时,返回null
当主调是空文件夹时,返回一个长度为0的数组
当主调是一个有内容的文件夹时,将里面所有一级文件和文件夹的路径放在File数组中返回
当主调是一个文件夹,且里面有隐藏文件时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏文件
当主调是一个文件夹,但是没有权限访问该文件夹时,返回null
总结:
前置知识:方法递归
认识递归的形式
什么是方法递归?
从形式上说:方法调用自身的形式称为方法递归( recursion)。
递归的形式
直接递归:方法自己调用自己。
间接递归:方法调用其他方法,其他方法又回调方法自己
使用方法递归时需要注意的问题:
递归如果没有控制好终止,会出现递归死循环,导致栈内存溢出错误。
应用、执行流程、算法思想
public static long f(int n) {
if (n == 1) {
return 1;
} else {
return f(n - 1) * n;
}
}
递归算法三要素:
递归公式:f(n) = f(n - 1) * n
递归的终结点:f(1)
递归的方向必须走向终结点
public static long sum(int n) {
if (n == 1) {
return 1;
} else {
return sum(n - 1) + n;
}
}
总结:
其他应用:文件搜索
/**
* 目标:掌握文件搜索的实现。
*/
public class RecursionTest3 {
public static void main(String[] args) {
//查找
// findFile(new File("day01oop-app1"), "HelloWord.java");
//删除
deleteFile(new File("day08map-stream-app\\movie"));
}
/**
* @param dir 所在的文件夹
* @param fileName 要查找的文件名
*/
public static void findFile(File dir, String fileName) {
//1.判断路径的合法性
if (dir.isDirectory()) {
//合法目录
//2.遍历该文件夹中所有的子文件
File[] files = dir.listFiles();
//3.遍历文件夹中每一个文件对象 判断是不是为文件
for (File file : files) {
if (file.isFile()) {
//是一个文件,判断是不是我要找的文件
if (file.getName().equals(fileName)) {
//找到了
System.out.println("找到了" + file.getAbsolutePath());
}
} else if (file.isDirectory()) {
//文件夹
//递归调用自身
findFile(file, fileName);
}
}
} else {
throw new RuntimeException("不是合法的文件夹");
}
}
public static void deleteFile(File file) {
//1.判断文件的合法性
if (file.exists()) {
if (file.isFile()) {
System.out.println("删除了文件:" + file.getPath());
file.delete();
} else {
//先删除文件夹的子文件
File[] children = file.listFiles();
for (File child : children) {
//递归调用
deleteFile(child);
}
//在删除当前文件家
System.out.println("删除了文件:" + file.getPath());
file.delete();
}
} else {
throw new RuntimeException("删除的文件或文件夹不存在");
}
}
}
IO流(读写数据)
用于读写数据的(可以读写文件,或网络中的数据...)