从上次集合讲起
TreeSet :Set接口有一个实现类:TreeSet类,该类底层使用红黑树算法(平衡二叉树)。
可以对集合中的数据按照一定的规则来排序。
public static void main(String[] args) {
// 元素可以自动排序
Set set=new TreeSet();
set.add(10);
set.add(5);
set.add(0);
System.out.println(set);
set=new TreeSet();
set.add("5");
set.add("10");
set.add("2");
set.add("5");
set.add("高数");
set.add("线代");
set.add("离散");
System.out.println(set);// 结果:10, 2, 5, 离散, 线代, 高数 。 为什么10是第一位呢?因为是字符串,会先比较10中的1.
注意:使用TreeSet,一定要保证该集合中的元素必须是同一种数据类型。
只有同一种数据类型才有可比性,若数据类型不同,就会报错。
把某一对象存储在TreeSet中,对象的类通过实现Comparable接口才会具有可比性,否则报错(类型转换错误)。
第一种方法继承Comparable接口:
Comparable 接口:
方法:
public int compareTo( Object o ){
编写比较规则
}
拿当前对象(this)和传入的o对象作比较。
返回: 当前对象 > o : return 正整数;
当前对象 == o : return 0;
当前对象 < o : return 负整数;
TreeSet会认为,如果compareTo方法返回的是0,则认为是同一个对象,若是同一个对象则不会添加到集合中。
class Student1 implements Comparable{
private String name;
private int age;
private int score;
public Student1(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
// 作比较
@Override
public int compareTo(Object obj) { // 父类要访问对象的成员变量,必须强转成子类
Student1 other=(Student1)obj;
if(this.score > other.score){
return 1;
}else if(this.score < other.score){
return -1;
}else{ // 如果成绩相同时则比较年龄
if(this.age > other.age){
return 1;
}else if(this.age < other.age){
return -1;
}else{
return this.name.compareTo(other.name); // 年龄和成绩都相同时,比较名字(String已实现compareTo方法)
}
}
}
@Override
public String toString() {
return "Studen1[name = "+name+",age = "+age+",score = "+score+"]";
}
}
^父类要访问对象的成员变量,必须强转成子类。
第二种方法继承Comparator接口:
//根据名字长度来排序,长的先排出来,短的后排出来
class Teacher {
private String name;
public Teacher(String name){
this.name=name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
'}';
}
}
public class StudentSortDemo2 {
public static void main(String[] args) {
Comparator com=new NameCom();// 不用Comparable接口的原因是,其采用的是自然排序(从低到高)。Comparator 更多是自定义排序(例如名字有长短之分)
Set set=new TreeSet(com);// com就相当于承载了你的自定义规则,并且给了TreeSet
/*Set set=new TreeSet(new Comparator() {--------------->匿名内部类的方法
@Override
public int compare(Object o1, Object o2) {
Teacher t1=(Teacher)o1;
Teacher t2=(Teacher)o2;
String name1=t1.getName();
String name2=t2.getName();
if(name1.length() > name2.length()){
return -1;
}else if(name1.length() < name2.length()){
return 1;
}
return 0;
}
});*/
set.add(new Teacher("will"));
set.add(new Teacher("ajksjdhbk"));
set.add(new Teacher("MA"));
System.out.println(set);
}
}
class NameCom implements Comparator{
@Override
public int compare(Object o1, Object o2) {
Teacher t1=(Teacher)o1;
Teacher t2=(Teacher)o2;
String name1=t1.getName();
String name2=t2.getName();
if(name1.length() > name2.length()){
return -1;
}else if(name1.length() < name2.length()){
return 1;
}
return 0;
}
}
^不用Comparable接口的原因是,其采用的是自然排序(从低到高)。Comparator 更多是自定义排序(例如名字有长短之分)。
TreeSet里面应用的排序规则( Comparable和Comparator接口 ):
Comparable:自然顺序排序(低–>高):内比较器
比较Set集合中是否是同一个元素的标准。
compareTo方法是否返回0。
Comparator:自定义排序 : 外比较器
比较Set集合中是否是同一个元素的标准。
compare方法是否返回0.
Map不是集合!
Map表示一种映射关系:
数学中的定义:有A和B两个集合,A集合中的一个元素,总能在B集合中找到唯一的一个映射值。(B集合中的一个元素,可以被A集合中的多个元素映射)。
Map:多个Entry的集合,一个Entry表示一个键值对,Map表示多个键值对,Map表示多组映射关系。
Map接口对应常用的实现类:
Hashtable类:在没有集合框架之前,就使用Hashtable来表示映射关系。现在建议使用HashMap类。通过源码可以看出,Hashtable中的每一个方法都是用synchronized修饰。
HashMap类:底层使用哈希表算法。底层没有用synchronized修饰。
HashMap相对于Hashtable来说,性能较高,但安全性较低。与ArrayList和Vector的区别相同。
LinkedHashMap类:保证Map中的key会记录添加顺序。
TreeMap类:保证Map中的key会按照指定的规则来排序。
通过观察Set和Map的体系,会发现很多实现类的名字相似(底层算法相同)。HashSet的底层就是使用HashMap。
在构建HashSet对象时,底层创建的是一个HashMap对象。
在把元素添加到set时,底层是存放在Map中的。
Map中的key其实就是Set的元素。
Map中所有的key其实就是Set集合。
集合工具类:
1:java.util.Arrays类:
Public static List asList(Object… params):把一个数组转换成对应的List集合。
数组转换为List最便捷的方式:List Arrays.asList( Object… params ):其底层还是ArrayList,不过是其内部类。
注意:返回的List的长度是固定的,不能改变。不过,一旦转换成功,该集合就会定长,所以对list进行增加删减操作就会报错,但替换操作不会。
2:java.util.Collections类:操作集合的工具类。
ArrayList,HashSet,HashMap都是线程不安全的类。
// 得到一个线程安全的Set
Set set=Collections.synchronizedSet(new HashSet<>());
synchronized (set){
// 对set对象的一些操作
}
但是我们平时不经常用,直接在Set方法上加入synchronized就行。
Collections的一些操作:
//返回空集:没有元素的集合
List emptyList= Collections.EMPTY_LIST; // 常量
System.out.println(emptyList);
emptyList =Collections.emptyList();//方法
System.out.println(emptyList);
list=Arrays.asList(1,2,3,4,5,6);
int index=Collections.binarySearch(list,5);//用二分法查找list中的元素,返回索引
System.out.println(index);
System.out.println(list);
Collections.reverse(list);//对list里的元素从后往前翻转
System.out.println(list);
Collections.shuffle(list);//对list里的元素进行随机翻转
System.out.println(list);
使用集合但不使用泛型所带来的问题:
1、编译器存在警告。
2、使用Tree类,如何保证只存储同一种数据类型(不是自己主动要求的那种类型)。
3、从集合中取出元素必须强转(或者使用Object)。
泛型:简概为广泛的通用类型,可以实现不同的调用者使用不同的数据类型。
若泛型不指明,就默认为Object类。
解决问题二:例如,想让TreeSet只存储String类型的元素:
Set<String> set = new TreeSet<String>();或Set<String> set = new TreeSet<>();
泛型通配符 :? ------------不知道传什么类型。
public static void main(String[] args) {
// 泛型通配符 :? ------------不知道传什么类型
List<Object> list1=null;
List<Number> list2=null;
List<Integer> list3=null;
//dowork(list1); 该操作会报错,因为此时在限定泛型上限只接受 Number和其子类
dowork(list2);
dowork(list3);
dowork1(list1);
dowork1(list2);
//dowork1(list3); 该操作会报错,因为此时在限定泛型下限限只接受 Number和其父类
}
static void dowork(List<? extends Number> list){ // 限定了泛型上限
}
static void dowork1(List<? super Number> list){ // 限定了泛型下限
}
文件
I/O的类库都在java.io包中。---------->使用I/O的类库必须使用import引入。
Java . io . File类:表示了文件和目录。
Windows系统:表示路径使用\ 。
但是在java中,一根 \ 表示转义符,我们必须写成: \。
路径分割使用 ; 。
Unix 系统:表示路径使用/ ,但是windows也可以用。
路径分割使用 : 。
注意:File表示一个文件/一个文件夹的路径,一旦File对象创建成功,就只能表示指定的路径。
public static void main(String[] args) throws IOException {
//创建File对象(使用前三个构造器)
File f1=new File("E:/hello/read.txt");
// 判断文件是文件夹(目录)还是文件
if(f1.isDirectory()){
System.out.println("是目录");
}
if(f1.isFile()){
System.out.println("是文件");
}
//boolean exists()测试此抽象路径名表示的文件或目录是否存在。
System.out.println(f1.exists());
// 创建文件夹
new File("E:/hello/read1.txt").createNewFile();//文件不存在时
new File("E:/hello/t").mkdir();// 目录存在,尾目录不存在
new File("E:/hello1/t1").mkdirs();//创建多级目录
File f1=new File("E:/hello/read.txt");
File f2=new File("E:/hello","read.txt");
// 路径名 文档名
//使用File表示文件夹对象
File dir=new File("E:/hello");
File f3=new File(dir,"read.txt");
//获取文件名称
String fileName=f1.getName();
System.out.println(fileName);
//获取文件父目录
String dir=f1.getParent();
System.out.println(dir);
//获取文件的路径(相对路径):一般在该项目中找,够用
System.out.println(f1.getPath());
// 获取文件的绝对路径:不在项目中找,就要用这个
System.out.println(f1.getAbsolutePath());
// 获取最后一次修改时间
System.out.println(f1.lastModified());
//修改文件名:boolean renameTo(File dest)重新命名此抽象路径名表示的文件。
f1.renameTo(new File("E:/hello/reads.txt"));
}
获取当前目录的子文件:
//获取当前目录的子文件
public class FileDemo2 {
public static void main(String[] args) {
File file=new File("E:\\BaiduNetdiskDownload");
File[] fs=file.listFiles();
for(File f : fs){
System.out.println(f);
}
System.out.println("--------------------------------------------------------");
//使用递归:获取该目录下的所有文件以及子目录的所有文件
//listAllFiles(file);
//列出有哪些盘符
File[] roots=file.listRoots();
for(File f : roots){
System.out.println(f);
}
}
public static void listAllFiles(File file){
System.out.println(file);
if(file.isDirectory()) {
File[] fs = file.listFiles();
for( File files : fs){
listAllFiles(files);
}
}
}
}