Java
day16_2023.8.28
Set集合
Set集合中的常用方法基本上和Collection中差不多
Set集合的特点
元素不可以重复,无序(有些子类是有序的),只能存放一个null(也有不能存放null的子类)
Set集合常用子类
HashSet : 底层结构是哈希表(数组+链表)
TreeSet:底层是红黑树,有序(自然排序)
LinkedHashSet:底层是哈希表 + 链表
HashSet解析
注释说明
1,HashSet实现了Set接口,不保证元素的顺序,允许1一个null元素,非同步的,初始容量影响性能
2,底层实际上是一个HashMap对象
构造方法
HashSet()
构造一个新的空集合; 背景HashMap实例具有默认初始容量(16)和负载因子(0.75)。
HashSet(Collection<? extends E> c)
构造一个包含指定集合中的元素的新集合。
HashSet(int initialCapacity)
构造一个新的空集合; 背景HashMap实例具有指定的初始容量和默认负载因子(0.75)。
HashSet(int initialCapacity, float loadFactor)
构造一个新的空集合; 背景HashMap实例具有指定的初始容量和指定的负载因子。
从源码能看到,HashSet在创建的时候,都是通过HashMap来实现的
HashSet中是如何处理创建的HashMap对象的键值的?
通过查看HashSet中的add方法发现,其实add()方法,就是HashMap的put方法
set中通过add方法加入的元素,都成为了对应的map对象的键
map对象中的值都是一个常量PRESENT,这个常量其实是一个空对象(new Object)
所以,其实HashSet中的数据,就是一个HashMap的键,添加、删除、获取等方法,都是通过HashMap来实现的
public class SetDemo01 {
public static void main(String[] args) {
//创建一个hashSet
HashSet<String> set = new HashSet<>();
//存放元素,无序存放,不能重复
set.add("hello");
set.add("hello");
set.add("JAVA");
set.add("WORLD");
set.add(null);
set.add(null);
System.out.println(set);
//遍历
for (String s : set) {
System.out.println(s);
}
System.out.println("----------------");
//iterator遍历
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
HashSet的存放原理
先获取元素的hashCode()值,然后根据hashCode值,判断元素在集合中的位置,每次存入元素都会比较hashCode值,
如果hashCode值相同,再去比较元素的值或者调用equals()方法判断是否相同,如果都相同,则将相同的元素替换,如果不同,以链表形式继续往后存放。
如果hashCode值不同,那么就存放在数组的不同位置。
将来,如果存放对象到set中,需要判断对象是否相同,那么要重写对象的equals()和hashCode方法
public class SetDemo01 {
public static void main(String[] args) {
//创建一个hashSet
/* HashSet<String> set = new HashSet<>();
//存放元素,无序存放,不能重复
set.add("hello");
set.add("hello");
set.add("JAVA");
set.add("WORLD");
set.add(null);
set.add(null);
System.out.println(set);
//遍历
for (String s : set) {
System.out.println(s);
}
System.out.println("----------------");
//iterator遍历
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
*/
HashSet<User> userSet = new HashSet<>();
userSet.add(new User(1001,"jack",20));
userSet.add(new User(1002,"tom",20));
userSet.add(new User(1003,"lucy",22));
userSet.add(new User(1001,"jack",20));
userSet.add(new User(1004,"tony",22));
System.out.println(userSet);
}
}
public class User {
private int id;
private String name;
private int age;
public User(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public User() {
}
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 int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
if (id != user.id) return false;
if (age != user.age) return false;
return name != null ? name.equals(user.name) : user.name == null;
}
@Override
public int hashCode() {
int result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + age;
return result;
}
}
TreeSet
注释说明
实现了NavigableSet接口,有序的(自然排序),线程不安全,底层实际是一个TreeMap
构造方法
TreeSet()
构造一个新的,空的树组,根据其元素的自然排序进行排序。
TreeSet(Collection<? extends E> c)
构造一个包含指定集合中的元素的新树集,根据其元素的 自然排序进行排序 。
TreeSet(Comparator<? super E> comparator)
构造一个新的,空的树集,根据指定的比较器进行排序。
TreeSet(SortedSet s)
构造一个包含相同元素的新树,并使用与指定排序集相同的顺序。
当我们在构造无参的TreeSet的时候,其实是在构造一个TreeMap对象
所以,treeSet中的元素的存放,就是按照TreeMap的键来实现存放的,treeMap的值也是做了默认的空对象常量的实现
将来如果需要存入对象到TreeSet的话,
1,这个对象要实现Comparable接口,从写compareTo()方法,实现对象的比较功能
2,在构造TreeSet对象的时候,传入Comparator对象
public class TreeSetDemo {
public static void main(String[] args) {
/* TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(4);
treeSet.add(1);
treeSet.add(3);
treeSet.add(5);
treeSet.add(3);
// treeSet.add(null); //不能存放null
System.out.println(treeSet);
System.out.println( treeSet.first());
System.out.println( treeSet.last());
//遍历
for (Integer integer : treeSet) {
System.out.println(integer);
}
Iterator<Integer> iterator = treeSet.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
*/
TreeSet<User> userSet = new TreeSet<>();
userSet.add(new User(1002,"jack",21));
userSet.add(new User(1001,"TOM",20));
userSet.add(new User(1004,"TONY",22));
userSet.add(new User(1003,"LUCY",19));
System.out.println(userSet);
}
}
public class User implements Comparable<User> {
private int id;
private String name;
private int age;
public User(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public User() {
}
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 int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
if (id != user.id) return false;
if (age != user.age) return false;
return name != null ? name.equals(user.name) : user.name == null;
}
@Override
public int hashCode() {
int result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + age;
return result;
}
@Override
public int compareTo(User o) {
if (o.id > this.id){
return 1;
}
if (o.id < this.id){
return -1;
}
if (o.age > this.age){
return -1;
}
if (o.age < this.age){
return 1;
}
if (o.name.compareTo(this.name) > 0){
return -1;
}
if (o.name.compareTo(this.name) > 0){
return 1;
}
return 0;
}
}
练习 :创建TreeSet的时候,传入Comparator
TreeSet<User> userSet = new TreeSet<>(new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
if (o1.getId() > o2.getId()) {
return 1;
}
if (o1.getId() < o2.getId()) {
return -1;
}
if (o1.getAge() > o2.getAge()) {
return -1;
}
if (o1.getAge() < o2.getAge()) {
return 1;
}
if (o1.getName().compareTo(o2.getName()) > 0) {
return -1;
}
if (o1.getName().compareTo(o2.getName()) > 0) {
return 1;
}
return 0;
}
});
userSet.add(new User(1002,"jack",21));
userSet.add(new User(1001,"TOM",20));
userSet.add(new User(1004,"TONY",22));
userSet.add(new User(1003,"LUCY",19));
System.out.println(userSet);
LinkedHashSet
注释说明
底层是哈希表+双向链表,就是hashMap+双向链表(底层可以看做是LinkedHashMap)
有序的,存取有序,允许存null,不能重复,线程不安全的
通过查看原码,发现LinkedHashSet中,只有几个构造方法,没有其他的普通方法,方法调用来自父类
构造方法中,都是通过super在访问父类,所以,创建LinkedHashSet,其实就是在通过HashSet创建HashMap对象
public class LinkedHashSetDemo {
public static void main(String[] args) {
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("hello");
linkedHashSet.add("world");
linkedHashSet.add("java");
linkedHashSet.add("hello");
linkedHashSet.add("oracle");
linkedHashSet.add("abc");
System.out.println(linkedHashSet);
//遍历
for (String s : linkedHashSet) {
System.out.println(s);
}
Iterator<String> iterator = linkedHashSet.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
Set集合总结
HashSet : 无序、允许null,不能重复,底层是HashMap(数组+链表+红黑树),线程不安全
TreeSet :有序(自然排序),不允许null,不能重复,底层是TreeMap(红黑树),线程不安全
LinkedHashSet: 有序(存取有序),允许null,不能重复,底层是HashMap+双向链表,线程不安全
IO流
在程序中,对象、集合这些数据存储是暂时的,
一旦程序结束,数据就会丢失
为了能永久的保存程序创建的数据,需要将这些数据存放到磁盘中,以后就可以持久性的使用数据了
在Java中,通过I/O流技术可以将数据保存到本地磁盘的文件中或者二进制文件中,可以达到永久存储数据的要求
在Java中,所有的数据都可以使用流来完成读写
流就是一组有序的数据序列,将数据从一个地方带到另一个地方
根据流向的不同,可以分输入流(Input Stream) 和输出流(Output Stream)
输入流: 就将数据从输入设备(键盘、文件)中内容读取到内存中
输出流 :将数据写入到输出设备(显示器、文件、磁盘)
I/O流的分类
按照流向 主要分为 : 输入流和输出流
按照流数据单位的不同分为 : 字节流和字符流
按照功能可以分为:节点流和处理流
Java中的系统流
Java的System类,封装了程序运行时的3个系统流,分别是 :in 、 out 、err
System.in : 标准输入流,默认设备是 键盘 ,是InputStream类的一个对象
System.out: 标准输出流,默认设备是控制台
System.err : 标准错误流,默认设备是控制台
System.out和System.err 是 PrintStream的对象
public class DemoIO {
public static void main(String[] args) {
byte[] bytes = new byte[1024];
System.out.println("请输入内容:");
try {
System.in.read(bytes); //通过read()方法也能实现文件的读取
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("输入的内容是:");
for (int i = 0; i < bytes.length; i++) {
System.out.print((char) bytes[i]);
}
}
}
File类
文件其实就是用来存放数据的,将来文件既可以作为输入设备,也可以作为输出设备,
文件和目录路径名的抽象表示。
构造方法
File(File parent, String child)
从父抽象路径名和子路径名字符串创建新的 File实例。
File(String pathname)
通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
File(String parent, String child)
从父路径名字符串和子路径名字符串创建新的 File实例。
File(URI uri)
通过将给定的 file: URI转换为抽象路径名来创建新的 File实例。
普通方法
boolean delete()
删除由此抽象路径名表示的文件或目录。
boolean exists()
测试此抽象路径名表示的文件或目录是否存在。
String getAbsolutePath()
返回此抽象路径名的绝对路径名字符串。
String getName()
返回由此抽象路径名表示的文件或目录的名称。
String getPath()
将此抽象路径名转换为路径名字符串。
boolean isDirectory()
测试此抽象路径名表示的文件是否为目录。
boolean isFile()
测试此抽象路径名表示的文件是否为普通文件。
long length()
返回由此抽象路径名表示的文件的长度。
boolean mkdir()
创建由此抽象路径名命名的目录。
boolean createNewFile()
当且仅当具有该名称的文件尚不存在时,原子地创建一个由该抽象路径名命名的新的空文件。
Java中,路径地址的写法 C:\xxx\xxx ,写在字符串中的\有转义符的意思,写地址需要写两个\
或者写成 C:/XX/XX
public class FileDemo {
public static void main(String[] args) throws IOException {
//指定文件的路径,创建文件对象
File file = new File("D:\\hello.txt");
//判断是否存在
System.out.println(file.exists());
//创建文件
file.createNewFile();
//判断是否存在
System.out.println(file.exists());
//length()
System.out.println(file.length());
//getName()
System.out.println(file.getName());
//getPath()
System.out.println(file.getPath());
//delete()
System.out.println(file.delete()); //true
//判断是否存在
System.out.println(file.exists());//false
//判断是否是目录
System.out.println(file.isDirectory()); //false
}
}
IO流常用类
按照流向划分的类 :
输入流 : InputStream\Reader
输出流: OutputStream\Writer
按照处理单位划分的类 :
字节流:InputStream\OutputStream
字符流 :Reader\Writer
InputStream类
InputStream是一个抽象类,这个抽象类是表示输入字节流的所有类的超类。
常用子类
ByteArrayInputStream 将字节数组转为字节输入流,从中读取数据
FileInputStream 从文件中读取数据
ObjectInputStream 将对象反序列化
PipedInputStream 连接管道输出流PipedOutputStream
SequenceInputStream 将多个字节输入流串联成一个字节输入流
FileInputStream的使用
从文件系统中的文件获取输入字节
构造方法
FileInputStream(File file)
通过打开与实际文件的连接创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
FileInputStream(String name)
通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名name命名。
public class FileInputStreamDemo {
public static void main(String[] args) {
FileInputStream fis = null;
try {
//创建文件的输入流对象
//new FileInputStream(new File("D:\\hello.txt"));
fis = new FileInputStream("D:\\hello.txt");
System.out.println(fis.available()); //返回可读取文件字节数
//读取内容read()方法读完内容后,返回-1
//System.out.println(fis.read());
//System.out.println(fis.read());
int data;
//在循环体中调用read()方法,必须要给方法结果赋值,
// 不然每次调用read()方法,都是在读取一次内容,
// 读了之后,需要将结果返回出去
while ((data = fis.read()) != -1){
System.out.print((char) data);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (fis != null){ //防止空指针异常
fis.close(); //关闭资源
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
OutputStream类
这个抽象类是表示字节输出流的所有类的超类
ByteArrayOutputStream 向字节数组中写数据
FileOutputStream 向文件中写数据
ObjectOutputStream 将对象序列化
PipedOutputStream 连接PipedIutputStream
FileOutputStream 的使用
构造方法
FileOutputStream(File file)
创建文件输出流以写入由指定的 File对象表示的文件。
FileOutputStream(String name)
创建文件输出流以指定的名称写入文件。
public class FileOutPutDemo {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
//创建fileOutput对象
fos = new FileOutputStream("D:\\hello.txt");
//写内容
String s = "abcdefg,南京邮电大学";
//将字符串转为字节数组
byte[] bytes = s.getBytes();
//调用write方法,写内容
fos.write(bytes,0,bytes.length);
System.out.println("文件已更新!");
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (fos != null){
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Reader类
用于读取字符流的抽象类
常用子类
BufferedReader 为其他的字符输入流提供读缓冲区
CharArrayReader 将字符数组转为字符输入流,从中读取字符
InputStreamReader 将字节输入流转为字符输入流,可以指定字符编码
StringReader 将字符串转为字符输入流,从中读取字符
FileReader InputStreamReader的子类,一般用来读取文件相关字符流
FileReader类
构造方法
FileReader(File file)
创建一个新的 FileReader ,给出 File读取。
FileReader(String fileName)
创建一个新的 FileReader ,给定要读取的文件的名称。
public class FileReaderDemo {
public static void main(String[] args) {
FileReader fileReader = null;
try {
//先创建FileReader对象
fileReader = new FileReader("D:\\hello.txt");
//创建一个字符数组
char[] chars = new char[1024];
StringBuffer sbf = new StringBuffer();
//读取内容到字符数组
int length;
while ( (length = fileReader.read(chars)) != -1){
sbf.append(chars);
}
System.out.println(sbf.toString());
//System.out.println(fileReader.read());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (fileReader!= null){
fileReader.close();}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
使用fileReader配合BufferedReader完成内容读取
public class FileReaderDemo02 {
public static void main(String[] args) {
//使用fileReader配合BufferedReader完成内容读取
FileReader fileReader = null;
BufferedReader bufferedReader = null;
try {
//先创建FileReader对象
fileReader = new FileReader("D:\\hello.txt");
//创建BufferedReader对象
bufferedReader = new BufferedReader(fileReader);
//通过BufferedReader读取内容
String s;
while ((s = bufferedReader.readLine()) != null){
System.out.println(s);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (bufferedReader != null){
bufferedReader.close();
}
if (fileReader != null){
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Writer类
用于写入字符流的抽象类
常用子类
BufferedWriter 为其他输出流提供写缓冲区
CharArrayWriter 向内存缓冲区的字符数组写数据
OutputStreamWriter 将字节输入流,转为字符输出流,可以指定编码格式
FileWriter OutputStreamWriter的子类,用来向文件中写数据
FileWriter的使用
构造方法
FileWriter(File file)
给一个File对象构造一个FileWriter对象。
FileWriter(String fileName)
构造一个给定文件名的FileWriter对象。
public class FileWriteDemo {
public static void main(String[] args) {
try {
//创建fileWriter对象
FileWriter fileWriter = new FileWriter("D:\\hello.txt");
//创建BufferedWriter对象
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
//写内容
//fileWriter.write("你好");
//fileWriter.write("我是南京邮电大学的学生");
//fileWriter.write("我今年20岁");
//fileWriter.flush();
bufferedWriter.write("你好");
bufferedWriter.write("我是南京邮电大学的学生");
bufferedWriter.newLine();
bufferedWriter.write("我今年20岁");
bufferedWriter.flush(); //刷新缓冲区
//再读取内容
FileReader fileReader = new FileReader("D:\\hello.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);
String s ;
while ((s = bufferedReader.readLine())!= null){
System.out.println(s);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//释放资源
}
}
}