集合类容器和IO流

集合类容器和IO流

引言

本文是对集合类容器,IO流以及基本数据结构的一些个人总结,适合小白在进行基本的学习后所复习的总结文档,所需要理解的地方居多。本文为个人理解,如有需要订正之处,敬请指出,本人邮箱1209110434@qq.com,在之后也会更新并发编程,jvm,Spring系列等个人总结。最后,希望各位在Java之路越走越远。Java之路道阻且长,最后一句话:Java之路为者长存,行者长至–送给你也送给我

—-Yang11😊

常见的集合体系

数组与集合的区别

数组:只能存储指定类型的数据,且固定长度。可以存储基本数据类型与引用数据类型

集合:集合体系不固定长度,且只能存储引用数据类型的数据

collection下分为两大类:list&set

list常见实现类:Arraylist Linklist

set常见实现类:Hashset Linkhashset Treeset

map常见实现类:Hashmap Linkhashmap Treemap

基本数据结构

线性结构

线性结构

指数据元素存在一对一的线性关系,包括链式存储结构和顺序储存结构

**数组:**常见的线性结构,存储相同类型且空间连续的数据。常见有一维数组,特点是查询快,增删慢

**队列:**双向开口型 先进先出,后进后出(先addLast)

img

栈: 线性表,类似于手枪弹夹,单方向开口型

先进后出,后进先出,插入元素叫进栈(压栈),删除元素叫出栈(退栈)

img

**链表:**链表是由一串集合所组成的数据,每个节点都有存储后一个元素的地址且空间不需要连续,查询首位较快

img

非线性结构

非线性结构指一对多与多对多关系

**树:**之所以叫“树”,是因为这种数据结构看起来就像是一个倒挂的树,只不过根在上,叶在下。树形数据结构有以下这些特点:

  • 每个节点都只有有限个子节点或无子节点;
  • 没有父节点的节点称为根节点;
  • 每一个非根节点有且只有一个父节点;
  • 除了根节点外,每个子节点可以分为多个不相交的子树。
  • img

常见树有红黑树,二叉查找树,平衡二叉树

Collection

ArrayList

底层原理

基于数组,是一种随机访问模式。第一次创建时会创建一个默认为10的数组(size记录元素个数)每次扩容1.5倍

特点

有序,可重复,有索引,但是是一种线程不安全的集合

查询快,增删相对较慢,(原因:ArrayList在进行增删操作的时候会先将元素复制一份,如果复制的元素过多,就会造成性能不佳

同时是一种线程不安全的集合,如果遇到多线程场景,可以通过 Collections 的 synchronizedList 方法将其转换成线程安全的容器后再使用

遍历方式: 继承RandomAssess接口都可以使用for循环

       //遍历方式
        //1.for循环
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        //2.迭代器
        Iterator<Integer> it = list.iterator();
       while (it.hasNext()){
           System.out.println(it.next());
       }
       //3.foreach
        for (Integer integer : list) {
            System.out.println(integer);
            //增强for只是将集合中的数值复制一遍,在foreach中修改数值无效
        }
        //lamda表达式
//        list.forEach(new Consumer<Integer>() {
//            @Override
//            public void accept(Integer integer) {
//                System.out.println(integer);
//            }
//        });
// lamda表达式
        list.forEach((integer)->  System.out.println(integer));
    }

基本操作

@Test
public void test01() {
    List<Integer> list = new ArrayList<Integer>();
    //ArrayList的增删改查
    list.add(3);
    list.add(5);
    list.add(20);
    list.add(6);
    list.remove(0);
    list.set(1,8);
    System.out.println(list.get(1));

LinkList

底层原理

基于双向链表,

特点:有序,可重复,有索引,同时是一种线程不安全的集合

查询慢,首尾增删快,同时是一种线程不安全的集合

遍历方式:同Arraylist将Arraylist改成linklist即可

与ArrayList 的区别:

1.ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。

2.ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。

3.在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标。

4.LinkedList 比 ArrayList 更占内存,因为 LinkedList 的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。

5.ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;

Hashset

底层原理:

基于哈希表与链表类似与HashMap,存储数据时会在底层生成一个16长度的数组,通过哈希算法(对16取余)指定存储位置,同时判断是否为NULL,如果不为null调用重写过的equals方法判断存储值是否存在,存在则覆盖原有值。

特点:

无序,不可重复,无索引,同时是一种线程不安全的,集合检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变

遍历方式:

Iterator<String> iterator = hashSet.iterator();
while (iterator.hasNext()){
    System.out.println(iterator.next());
}
for (String s : hashSet) {
    System.out.println(s);
}
hashmaphashset
实现map接口实现set接口
存储键值对仅存储对象
用put向map中添加元素调用add方法向set中添加元素
HashMap使用键(Key)计算HashcodeHashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false
HashMap相对于HashSet较快,因为它是使用唯一的键获取对象HashSet较HashMap来说比较慢

LinkHashset

特点:有序,不重复,每个存储的元素多了一个双链表

TreeSet

特点:可排序,不重复基于红黑树

排序方式:基本数据类型(大小),String(首字母大小),自定义引用数据类型(自定义比较规则重写comparator(在实体POJO中重写comparator或在重写比较规则))

Map

HashMap

HashMap概述: HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变

特点:基于哈希表键值对集合,键无序,不重复,无索引且可以为NULL,相同值覆盖

HashMap 基于 Hash 的算法实现

A:当我们往Hashmap中put元素时,利用key的hashCode重新hash计算出当前对象的元素在数组中的下标

B:存储时,如果出现hash值相同的key,此时有两种情况。(1)如果key相同,则覆盖原始值;(2)如果key不同(出现冲突),则将当前的key-value放入链表中

C: 获取时,直接找到hash值对应的下标,在进一步判断key是否相同,从而找到对应值。Jdk 1.8中对HashMap的实现做了优化,当链表中的节点数据超过八个之后,该链表会转为红黑树来提高查询效率

hashmap扩容方式

①.在jdk1.8中,resize方法是在hashmap中的键值对大于阀值时或者初始化时,就调用resize方法进行扩容;

②.每次扩展的时候,都是扩展2倍;

③.扩展后Node对象的位置要么在原位置,要么移动到原偏移量两倍的位置。

HashMap解决哈希冲突

1.使用链地址法(使用散列表)来链接拥有相同hash值的数据使用

2.次扰动函数(hash函数)来降低哈希冲突的概率,使得数据分布更平均;

3.引入红黑树进一步降低遍历的时间复杂度,使得遍历更快;

hashmap的key

可以使用任何类作为 Map 的 key,然而在使用之前,需要考虑以下几点:

类需要重写了 equals() 方法,也应该重写 hashCode() 方法。类的所有实例需要遵循与 equals() 和 hashCode() 相关的规则

String、Integer等包装类的特性能够保证Hash值的不可更改性和计算准确性,能够有效的减少Hash碰撞的几率

String、Integer都是final类型,即不可变性,保证key的不可更改性,不会存在获取hash值不同的情况

内部已重写了equals()、hashCode()等方法,遵守了HashMap内部的规范,不容易出现Hash值计算错误的情况;

Linkhashmap

特点:基于哈希与链表,键值对集合,键有序,不重复,无索引且可以为NULL,相同值覆盖

treemap

特点

基于树型结构,键值对集合,键可排序,不重复,无索引且可以为NULL,相同值覆盖

TreeMap 要求存放的键值对映射的键必须实现 Comparable 接口从而根据键对元素进 行排 序。

基本操作

@Test
public void test2() {
    Map<String, Integer> map = new HashMap<>();
    map.put("y",22);
    map.put("z",22);
    map.put("w",23);
    //map.remove("z");
    //map.clear();
    System.out.println(map.isEmpty());
    System.out.println(map.containsKey("y"));
    System.out.println(map.containsValue(23));
    Set<String> strings = map.keySet();
    Set<Map.Entry<String, Integer>> entries = map.entrySet();

遍历方式

map.forEach((k,v)-> System.out.println(k+"====>"+v));
System.out.println(2<<3);
map.forEach(new BiConsumer<String, Integer>() {
                @Override
                public void accept(String s, Integer integer) {
                    System.out.println(map.get(s)); 
                    System.out.println(map.get(integer));
                }
            }
);

Stream流

Stream流不是一种数据结构,不保存数据,它只是在原数据集上定义了一组操作。这些操作是惰性的,即每当访问到流中的一个元素,才会在此元素上执行这一系列操作。Stream不保存数据,故每个Stream流只能使用一次。

流的生成方法

Collection接口的.stream()或parallelStream()方法

静态的Stream.of()、Stream.empty()方法

Arrays.stream(array, from, to)

静态的Stream.concat()方法将两个流连接起来

流的操作方法

filter(Predicate) 将结果为false的元素过滤掉

map(fun) 加工方法,可以用方法引元或者lambda表达式

limit(n) 保留前n个元素

skip(n) 跳过前n个元素

distinct() 剔除重复元素

sorted() 将Comparable元素的流排序

sorted(Comparator) 将流元素按Comparator排序

peek(fun) 流不变,但会把每个元素传入fun执行,可以用作调试

流的Terminal方法(终结操作)

max(Comparator),min(Comparator),count()

findFirst() 返回第一个元素

findAny() 返回任意元素

anyMatch(Predicate) 任意元素匹配时返回true

allMatch(Predicate) 所有元素匹配时返回true

noneMatch(Predicate) 没有元素匹配时返回true

reduce(fun) 从流中计算某个值,接受一个二元函数作为累积器,从前两个元素开始持续应用它,累积器的中间结果作为第一个参数,流元素作为第二个参数

收集操作

iterator()

forEach(fun)

forEachOrdered(fun)

可以应用在并行流上以保持元素顺序
toArray()

toArray(T[] :: new)

返回正确的元素类型

/**
目标:Stream流的常用API
    forEach : 逐一处理(遍历)
    count:统计个数
       -- long count();
    filter : 过滤元素
       -- Stream<T> filter(Predicate<? super T> predicate)
    limit : 取前几个元素
    skip : 跳过前几个
    map : 加工方法
    concat : 合并流。
    */
        
        list.stream().filter(s -> s.startsWith("张")).forEach(s -> System.out.println(s));

        long size = list.stream().filter(s -> s.length() == 3).count();
        System.out.println(size);

       // list.stream().filter(s -> s.startsWith("张")).limit(2).forEach(s -> System.out.println(s));
        list.stream().filter(s -> s.startsWith("张")).limit(2).forEach(System.out::println);

        list.stream().filter(s -> s.startsWith("张")).skip(2).forEach(System.out::println);

        // 给集合元素的前面都加上一个:黑马的:
        list.stream().map(s -> "李:" + s).forEach(a -> System.out.println(a));

        // 需求:把所有的名称 都加工成一个学生对象。
         list.stream().map(s -> new Student(s)).forEach(s -> System.out.println(s));
//        list.stream().map(Student::new).forEach(System.out::println); // 构造器引用  方法引用

        // 合并流。
        Stream<String> s1 = list.stream().filter(s -> s.startsWith("张"));
        Stream<String> s2 = Stream.of("java1", "java2");
        // public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
        Stream<String> s3 = Stream.concat(s1 , s2);
        s3.distinct().forEach(s -> System.out.println(s));
    }

线程安全类容器

Vector

就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用。在web应用中,特别是前台页面,往往效率(页面响应速度)是优先考虑的,Vector 中的方法由于加了 synchronized 修饰,因此 Vector 是线程安全容器,但性能上较ArrayList差

Hashtable

就比hashmap多了个线程安全。

ConcurrentHashMap

首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。

ConcurrentHashMap对整个桶数组进行了分割分段(Segment),然后在每一个分段上都用lock锁进行保护,相对于HashTable的synchronized锁的粒度更精细了一些,并发性能更好,而HashMap没有锁机制,不是线程安全的。

JDK1.8之后ConcurrentHashMap启用了一种全新的方式实现,利用CAS算法(在并发编程有提到)。

IO流

流的分类:
通过不同的方法,可以对于进行分类。
1.按照功能来划分:

输入流:只能从中读取数据,而不能向其写入数据。
输出流:只能向其写入数据,而不能从中读取数据。

2.按照处理单元来划分

字节流和字符流操作的方式基本上完全相同。操作的数据单元不同
字节流:操作的是8位的字节 InputStream/OutputStream 作为字节流的基类
字符流:操作的是16位的字符 Reader/Writer 作为字符流的基类

原始

字符流

FileReader

解决中文输出出现乱码的情况,方便读取文字

FileWriter

字节流

FileinputStream

调用read方法一次读取一个字节,如果没有可以读取的字节则返回-1

FileOutputStream

调用write方法,每次写入时都会清空数据,flush方法为刷新流,还可以继续写数据,close为关闭流

缓冲流

缓冲字节流
BufferedInputStream
BufferedOutputStream

缓冲字符流
BufferedReade
BufferedWriter

缓冲流的好处:

缓冲流内部包含一个缓冲区域,默认8kb,每一次程序调用read方法其实都是从缓冲区域当中读取内容,如果读取失败就说明缓冲区域当中没有内容,那么就从数据源当中读取内容,然后会尽可能读取更多的字节放入到缓冲区域当中,最后缓冲区域当中的内容,会全部返回给程序。从缓冲区读取数据会比直接从数据源读取数据的速度快,效率也更高,性能更好。

用法:将原始流包装给缓冲流即可

 is = new FileInputStream("E:\\JAVASEDEMO\\FirstPeriod\\src\\java_FirstPriodTest\\a.txt");
                BufferedInputStream bufferedInputStream = new BufferedInputStream(is);//转化为字符缓冲输入流
                byte[] bytes = new byte[1024];
                int len;
                FileReader fileReader = new FileReader("E:\\JAVASEDEMO\\FirstPeriod\\src\\java_FirstPriodTest\\a.txt");
                BufferedReader bufferedReader = new BufferedReader(fileReader);
while ((len = is.read(bytes))!=-1) {
                 System.out.println(new String(bytes,0,len));
             }

转换流

InputStreamReader:
OutputStreamWriter:

转换流主要解决读取字符流因为格式不同的乱码问题

//输入流读取
InputStreamReader inputStreamReader = new InputStreamReader(is, StandardCharsets.UTF_8);
//输出流
FileOutputStream fo = new FileOutputStream("E:\\JAVASEDEMO\\FirstPeriod\\src\\java_FirstPriodTest\\");
                OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fo,"GBK");
                BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
                bufferedWriter.write("jiayou");

思想:流之间转换作用都是高级流由低级流转换而来

打印流

是一种更方便更便利的存储数据的流

//分为字符打印流和字节打印流
PrintStream printStream = new PrintStream("E:\\\\JAVASEDEMO\\\\FirstPeriod\\\\src\\\\java_FirstPriodTest\\\\\"");
 printStream.print("Springboot");
//同理使用PrintWriter

对象序列化

序列化来源

在Java中,我们可以通过多种方式来创建对象,并且只要对象没有被回收我们都可以复用此对象。但是,我们创建出来的这些对象都存在于JVM中的堆(heap)内存中,只有JVM处于运行状态的时候,这些对象才可能存在。一旦JVM停止,这些对象也就随之消失;但是在真实的应用场景中,我们需要将这些对象持久化下来,并且在需要的时候将对象重新读取出来,Java的序列化可以帮助我们实现该功能。

序列化的两种方式:

间接继承Externalizable或继承Serializable接口未实现此接口的类将无法将其任何状态或者信息进行序列化或者反序列化。可序列化类的所有子类型都是可以序列化的。序列化接口没有方法或者字段,仅用于标识可序列化的语义

序列化的作用

对象序列化机制(object serialization)是java语言内建的一种对象持久化方式,通过对象序列化,可以将对象的状态信息保存未字节数组,并且可以在有需要的时候将这个字节数组通过反序列化的方式转换成对象,对象的序列化可以很容易的在JVM中的活动对象和字节数组(流)之间进行转换。在JAVA中,对象的序列化和反序列化被广泛的应用到RMI(远程方法调用)及网络传输中;

Externalizable接口与Serializable接口的区别

Externalizable继承了Serializable,该接口中定义了两个抽象方法:writeExternal()与readExternal()。当使用Externalizable接口来进行序列化与反序列化的时候需要开发人员重写writeExternal()与readExternal()方法。由于上面的代码中,并没有在这两个方法中定义序列化实现细节,所以输出的内容为空。还有一点值得注意:在使用Externalizable进行序列化的时候,在读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。所以,实现Externalizable接口的类必须要提供一个public的无参的构造器

commons-Io框架

Commons IO是针对开发IO流功能的工具类库

具体可在springboot位置简化IO操作

public class CommonsIODemo01 {
    public static void main(String[] args) throws Exception {

        // 1.完成文件复制!
//        IOUtils.copy(new FileInputStream("D:\\resources\\hushui.jpeg"),
//                new FileOutputStream("D:\\resources\\hushui2.jpeg"));


        // 2.完成文件复制到某个文件夹下!
//        FileUtils.copyFileToDirectory(new File("D:\\resources\\hushui.jpeg"), new File("D:/"));


          // 3.完成文件夹复制到某个文件夹下!
//          FileUtils.copyDirectoryToDirectory(new File("D:\\resources") , new File("D:\\new"));
//           FileUtils.deleteDirectory(new File("D:\\new"));

         // JDK1.7 自己也做了一些一行代码完成复制的操作:New IO的技术
         // Files.copy(Path.of("D:\\resources\\hushui.jpeg"), Path.of("D:\\resources\\hushui3.jpeg"));

        FileUtils.deleteDirectory(new File("D:\\new"));
    }
}

collections工具类

Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。

确保集合不能被修改:unmodifiableCollection(Collection c) 方法来创建一个只读集合,这样改变集合的任何操作都会抛出 Java. lang. UnsupportedOperationException 异常。

https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Collections.html#method-summary

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值