Java随记之集合

Collection接口,两个重要的子接口,List和Set。Map接口。
List接口的重要实现类:ArrayList

特点:底层使用变成数组实现,Object型的数组elementData。创建ArrayList对象时这个数组的长度为0.第一次调用add方法添加的时候,初始化数组长度为10.当集合中当前元素数超过了数组长度时,扩容。按照原来长度1.5倍扩容。由于ArrayList使用数组实现,所以遍历效率高,频繁增删效率低。ArrayList线程不安全。

重要实现类:Vector

特点:除了线程安全,其他和ArrayList一样。

重要实现类:LinkedList

特点:底层使用双向链表实现。遍历的效率低,频繁增删的效率高。拥有一套独有的对首尾元素进行单独操作的方法。LinkedList保存元素的结构是一个静态内部类叫Node。这个Node有item属性保存元素,有perv和next属性构成链表结构。
Set接口

特点:无序(新增顺序不是存储顺序),且不允许重复。

独有方法:无。

重要实现类:

HashSet:使用hashCode()方法和equals()方法配合去重。当一个元素存入HashSet集合时,首先调用该元素的hashCode()方法,得到hash码,利用hash码计算该元素在数组中存放的索引。如果该索引位置无元素则直接存放在该位置。如果该下标处有元素,则调用新元素的equals方法和原元素进行比较,如果equals返回false,则判定两个元素不相同,则将新元素链接到原元素的next属性上。如果equals方法返回true,则判定两个元素相同,不执行新增动作。

LinkedHashSet:底层实现和HashSet一模一样。除了本身有两个属性,first和last。由于这两个属性的存在,LinkedHashSet除了数组还维护一个链表结构。遍历时遍历的是这个链表,而链表可以记录元素新增顺序。

TreeSet:底层使用红黑树保存数据。元素在向红黑树新增的时候和原元素进行比较大小,如果相等,则判定元素相同,不执行新增操作。如果不相等则向红黑树中新增。使用红黑树可以在元素存入的时候即按照升序或者降序排列,同时去重。
Map接口

特点:键值对集合,元素是键值对组成的。键不允许重复,值允许重复。如果使用相同的键向map中存值,新的值替换原来的值。
重要实现类:

HashMap:底层使用哈希表存储数据。

去重方式:HashMap的键去重方式和HashSet一致。当元素存入HashMap时,首先使用键的hashCode方法判定该元素在底层数组中索引,如果该索引除无元素,则将键值对存放在数组该下标的位置。如果该位置有元素,则用新元素的键和原元素的键进行比较,使用equals方法。如果equals方法返回true则代表新元素的键和原元素的键重复。新元素的值替换原元素的值。如果equals方法返回false,则将新元素链接到原元素的next属性上。

底层保存数据的基本原来:HashMap使用一个哈希表保存数据。这个哈希表是一个数组,数组的元素是链表。当一个键值对放入HashMap集合中,首先使用键和集合中其他元素的键进行等值比较。如果新的元素键没有和原来的元素键重复,那么新的键值对则保存在集合中。如果新元素的键和原来元素的键重复,新的值会替换原来的值。从jdk1.8开始,如果底层数组的链表元素超过8个,并且当前数组的长度超过64位,则将链表结构变成红黑树。如果链表元素超过8个但是数组的长度低于64位,则不会变树而是数组扩容。HashMap底层维护一个叫负载因子的属性,这个负载因子默认值是0.75.负责因子的作用是控制数组扩容。HashMap在第一次调用put方法存储数据时,数组初始化长度为16.当集合中元素数超过负载因此乘以数组长度时,则数组扩容,2倍扩容。

LinkedHashMap:底层保存元素方式和HashMap一样。对于LinkedHashMap的键集维护一个first属性和一个last属性。保证键集的遍历顺序和新增顺序一致。

TreeMap:集合的键集去重方式和TreeSet一致,使用红黑树保存元素。需要存放在TreeMap中的键值对的键可以进行排序。

泛型

概念:在定义的时候不确定类型以便可以适应更多类型的变量。在创建对象时确定类型,方便方法的调用和传参。
泛型使用的场景:

1、泛型在类声明时的使用

public class Data<T>{// 在类声明时,为类定义一个泛型的空间,这个泛型可以在类中作为属性的类型,作为方法的参数或者是方法的返回类型
    
    private String one;
    private T two;// 泛型的属性
    
    public void methodOne(T t,String s){// 泛型作为方法的参数类型
        
    }
    
    public T methodOne(T t){// 泛型作为方法的返回值类型
        
    }
}

// 在创建类的对象时确定泛型的具体类型
public class Test{
    public static void main(String[] args){
        Data<String> data = new Data<>();// 声明对象时给出泛型的具体类型,要求引用和对象的泛型必须一致,泛型没有多态。由于要求左右两端泛型一致,所以声明引用时给出的泛型必须是创建对象时的对象泛型,所以右侧可以省略泛型。
       data.methodOne("ok","hello");// 一旦确定了泛型的具体类型,类体中声明的所有泛型位置的元素类型都是确定泛型类型,这里需要在methodOne方法的第一个参数位置传递Strnig。
    }
}

2、在继承状态下使用泛型

class Father<T>{// 定义父类时给出泛型空间
    private T one;
    
    public T getOne(){
        return one;
    }
    
    public void setOne(T one){
        this.one = one;
    }
}

class Son extends Father<String>{// 第一种方式,在子类继承父类时给出泛型的具体类型,子类没有泛型空间子类中所有继承自父类的属性和方法中有泛型的位置都是这个继承时确定的具体类型。
    
    public String getOne(){
        return super.getOne();
	}
    
    public void setOne(String one){
        super.setOne(one);
    }
    
}

class Son<T> extends Father<T>{// 子类继承自父类,子类也有泛型空间,但是子类的泛型和父类的泛型必须保持一致。由于子类也是泛型的类型,所以具体类型要等到创建子类对象才能确定。
    public T getOne(){
        return one;
    }
    
    public void setOne(T one){
        this.one = one;
    }
}

public class Test{
    public static void main(String[] args){
        Son<String> son = new Son<>();
        son.setOne("tom");// 子类创建对象确定了泛型的具体类型,方法的参数和返回值就是具体类型了。
        String s = son.getOne();
    }
}

3、在接口中声明泛型

public interface Animal<T>{// 在声明接口是给出泛型空间
    public void methodOne(T t);// 泛型作为参数类型
    
    public T methodTwo();// 泛型作为返回类型
}

class Dog implements Animal<String>{// 实现类在实现接口时确定泛型的具体类型
    public void methodOne(String t){
        System.out.println(t);
    }
    
    public String methodTwo(){
        return "";
    }
}

class Cat<T> implements Animal<T>{// 接口的实现类没有确定泛型的具体类型,实现类也有反省空间。需要创建实现类对象时确定泛型的具体类型。
    public void methodOne(T t){
        System.out.println(t);
    }
    
    public T methodTwo(){
        return t;
    }
}

class Test{
    public static void main(String[] args){
        Dog dog = new Dog();// 实现来创建对象时是不用指定泛型的具体了类型。
        Cat<String> cat = new Cat<>();// 创建实现类对象时确定泛型的具体类型。
    }
}

IO流

1、File类:代指硬盘上的文件或者文件夹的类。

2、File类创建对象:

File file = new File("路径");// File类创建对象必须给出一个文件或文件夹的硬盘路径,这个路径可以是绝对路径,也可以是相对路径。

3、File类的常用方法

 		file.length();// 当前文件的所占内存空间,文件的大小,单位是字节
        file.getName();// 返回当前文件的文件名,不带路径名。
        file.getParent();// 返回当前文件所在文件夹的路径名,没有文件名
        file.getAbsolutePath();// 返回当前文件的完整路径名和文件名,返回类型为String
        file.getAbsoluteFile();// 返回使用当前文件完整路径名和文件名创建的File对象。
        file.isFile();// 如果当前File对象是文件则返回true,否则返回false。
        file.isDirectory();// 如果当前File对象是文件夹则返回true,否则返回false。
        file.exists();// 如果使用file当前路径可以找到硬盘上的文件则返回true,否则返回false。
        file.delete();// 删除当前File对象代指硬盘文件或文件夹。
        file.createNewFile();// 创建当前File对象路径代指的硬盘文件。
        file.mkdir();// 创建当前File对象路径代指的文件夹。

4、流的特点:

流有两端,一端一定是程序,另一端可以是硬盘文件(File对象),也可以其他的外界信息。流有流转的内容,字节流和字符流。字节流是基本流,任何一种流其实都是字节流。字符流是在字节流的基础上构建了一些符合传递字符的特点的流。

5、流的分类:

5.1:按方向分:输入流和输出流

5.2:按流的内容分:字节流和字符流

5.3:按流的角色分:节点流和处理流

6、常用的流对象

1、字节节点流:FileInputStream和FileOutputStream。这两个流可以和File类构建流对象。读取和写出字节内容。

2、字符处理流:BufferedReader和BufferedWriter。这两个流是带缓冲区的字符处理流。需要使用FileReader和FileWriter这两个字符节点流构建对象。

3、转换流:InputStreamReader和OutputStreamWriter。这两个流是字符处理流。作用是将字节节点流转换成字符处理流。

4、对象流:ObjectInputStream和ObjectOutputStream。这两个流是字节处理流。需要字节节点流构建对象。可以将java程序中的对象写入硬盘文件,也可以将硬盘文件中对象读取到java程序中。需要对象流处理的对象必须要求它的类实现Serializable接口。Serializable接口是一个标签接口,接口中没有任何方法和属性。

反射就不写了,动态获取运行时类对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值