BufferedInputStream解读

  咦咦咦,各位小可爱,我是你们的好伙伴——bug菌,今天又来给大家普及Java之IO流啦,别躲起来啊,听我讲干货还不快点赞,赞多了我就有动力讲得更嗨啦!所以呀,养成先点赞后阅读的好习惯,别被干货淹没了哦~

在这里插入图片描述

前言

  在进行Java编程时,避免不了要进行一些文件操作。虽然Java提供了很多类和方法来进行文件操作,但是由于I/O操作是比较费时的,所以我们需要考虑如何优化文件操作的效率。本文介绍了如何使用Java中的BufferedInputStream类来提高I/O操作的效率。

摘要

  本文主要介绍了如何使用Java中的BufferedInputStream类来优化I/O操作。首先介绍了BufferedInputStream类的作用和原理,然后分别从文件读取和文件写入两个角度,介绍了如何使用BufferedInputStream类来提高文件操作的效率。最后通过测试用例来验证了BufferedInputStream类的优化效果。

BufferedInputStream

简介

  BufferedInputStream是Java中的一个输入流,它实现了输入流的缓冲功能,使读取数据更加高效。当从一个InputStream中读取数据时,Java在内部会一次读取一个字节。这种方式读取数据的效率非常低,因为每次读取都需要和硬盘或网络进行IO操作,而这些操作非常耗时。因此,使用BufferedInputStream可以先将读取的数据缓存到内存中,在内存中慢慢读取,这样就可以减少IO操作的次数,提高读取数据的效率。在使用BufferedInputStream时,可以通过调用read()方法来读取数据,该方法会从缓冲区中读取数据,如果缓冲区中没有数据,则会调用底层流来读取数据。

作用和原理

  BufferedInputStream类是Java中的一个输入流缓冲器,它可以提高I/O操作的效率。BufferedInputStream类的原理是,当我们从文件中读取数据时,它会先将数据读入内存缓冲区中,然后再从缓冲区中逐个地读取数据。这样可以减少一些不必要的I/O操作,从而提高读取数据的效率。

优缺点

  BufferedInputStream是Java中的一个输入流包装类,它提供了输入流缓冲区的功能,可以提高读取数据的效率。

优点:

  1. 提供了输入流的缓冲功能,减少了底层输入流的读取次数。这种减少I/O操作的方式可以显著提高读取数据的性能,特别是在处理大量数据时。
  2. 具有预读的功能,可以一次性读取多个字节到缓冲区中,从而减少了对底层输入流的访问次数。
  3. 可以通过设置合适的缓冲区大小来优化读取性能,尤其是在连续读取大型数据块时。

缺点:

  1. 需要额外的内存空间来存储缓冲区,可能会占用较大的内存。
  2. 当读取数据量较小时,缓冲区的额外开销可能会引起性能下降。

应用场景

  1. 读取大文件或数据流时,使用BufferedInputStream可以提高读取性能。
  2. 在网络传输中,可以使用BufferedInputStream来缓冲输入流,减少网络I/O的次数,提高网络传输性能。
  3. 当需要连续读取大量数据块时,可以使用BufferedInputStream来将其存储在缓冲区中,减少对底层输入流的访问次数。

源码分析

  在BufferedInputStream的源码中,它维护了一个内部的字节数组缓冲区,默认缓冲区大小为8192字节(8KB)。当从BufferedInputStream中读取数据时,它会先检查缓冲区中是否还有可读取的数据,如果有,则直接从缓冲区中返回数据;如果没有,则从底层输入流中读取数据,并同时将读取的数据存入缓冲区中。在写入数据时也是类似的过程,先写入缓冲区,当缓冲区满或者需要刷新缓冲时,再将数据写入底层输出流。

  BufferedInputStream的源码比较复杂,涉及缓冲区的管理、数据读写等操作,通过内部的缓冲区来实现数据的快速读取和写入。在具体使用时,我们只需要关注使用API即可,无需深入理解其具体实现细节。

  由于BufferedInputStream主要的作用是在数据源和程序之间添加一个缓冲区,来提高IO操作的效率。读取数据时,BufferedInputStream先从缓冲区中读取,如果缓冲区中没有数据,则从数据源中读取一定量的数据放入缓冲区中,然后再从缓冲区中读取数据,这样就可以避免频繁地访问物理设备,提高了效率。

下面是BufferedInputStream的源码分析:

public class BufferedInputStream extends FilterInputStream {
    protected volatile byte buf[];
    //缓冲区大小
    protected int count;
    protected int pos;
    protected int markpos = -1;
    protected int marklimit;
    protected boolean mSkipLF = false;
    //默认缓冲区大小
    static int defaultBufferSize = 8192;
    //跳过缓冲区数据并重新读取数据
    private void fill() throws IOException {
        //pos记录缓存区的位置
        pos = 0;
        count = 0;
        int n = in.read(buf, pos, buf.length);
        if (n > 0) {//如果成功从输入流中读取数据
            count = n;
        }
    }
    //构造方法传入InputStream流对象
    public BufferedInputStream(InputStream in) {
        this(in, defaultBufferSize);
    }
    public BufferedInputStream(InputStream in, int size) {
        super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }
    //读取单个字节
    public synchronized int read() throws IOException {
        //如果当前数据已经读取完了,从输入流读取数据并缓存
        if (pos >= count) fill();
        //如果读完了还为空则返回-1
        return (pos < count) ? (buf[pos++] & 0xff) : -1;
    }
    //读取多个字节
    private int read1(byte[] b, int off, int len) throws IOException {
        int avail = count - pos;
        //如果缓存区正在被使用,则重新从流中读取数据到缓冲区中
        if (avail <= 0) {
            if (len >= buf.length && markpos < 0) {
                return in.read(b, off, len);
            }
            fill();
            avail = count - pos;
            if (avail <= 0) return -1;
        }
        int cnt = (avail < len) ? avail : len;
        System.arraycopy(buf, pos, b, off, cnt);
        pos += cnt;
        return cnt;
    }
    //读取多个字节,并返回读取的字节数
    public synchronized int read(byte b[], int off, int len)
        throws IOException {
        //校验参数范围
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }
        int n = 0;
        //连续读取
        for (;;) {
            int nread = read1(b, off + n, len - n);
            if (nread <= 0) return (n == 0) ? nread : n;
            n += nread;
            //读够了就退出循环
            if (n >= len) return n;
        }
    }
    //跳过指定字节数
    public synchronized long skip(long n) throws IOException {
        if (n <= 0) {
            return 0;
        }
        //现将缓存区的数据跳过
        long avail = count - pos;
        if (avail <= 0) {
            //如果缓存区没有数据,则直接跳过n个字节
            return in.skip(n);
        }
        long skipped = (avail < n) ? avail : n;
        pos += skipped;
        n -= skipped;
        //如果还需要跳过的字节数超过了缓存区大小,则直接调用输入流的skip方法跳过
        if (n > 0) {
            skipped += in.skip(n);
        }
        return skipped;
    }
    //返回当前可读取的字节数
    public synchronized int available() throws IOException {
        int n = count - pos;
        int avail = in.available();
        return (n > (Integer.MAX_VALUE - avail)) ? Integer.MAX_VALUE : n + avail;
    }
    //标记当前位置
    public synchronized void mark(int readlimit) {
        marklimit = readlimit;
        markpos = pos;
    }
    //重置到上一次标记的位置
    public synchronized void reset() throws IOException {
        if (markpos < 0) {
            throw new IOException("Resetting to invalid mark");
        }
        pos = markpos;
    }
    //判断是否支持mark和reset方法
    public boolean markSupported() {
        return true;
    }
    //关闭流
    public void close() throws IOException {
        byte[] buffer;
        synchronized (this) {
            buffer = buf;
            buf = null;
        }
        if (buffer != null) {
            //调用输入流的close方法关闭流
            in.close();
        }
    }
}

  从源码中可以看出,BufferedInputStream主要是通过缓冲区来提高了读取效率,其读取过程与InputStream的读取过程基本一致,只不过在数据从输入流中读取后,会先将其缓存到一个缓冲区中,然后再从缓冲区中读取数据。同时,BufferedInputStream还提供了markreset方法,可以对数据流进行标记和重置,方便对流的操作。

使用BufferedInputStream类读取文件

  在进行文件读取时,我们可以使用BufferedInputStream类来提高读取效率。下面是一个使用BufferedInputStream类读取文件的示例代码:

try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("./template/hello.txt"))) {
    byte[] buffer = new byte[1024];
    int length;
    while ((length = bis.read(buffer)) != -1) {
        // do something with the data
    }
} catch (IOException e) {
    // handle exception
}

  上面的代码中,我们首先创建了一个BufferedInputStream对象,并将其包装在一个try-with-resources语句中,这样可以自动关闭资源。然后我们创建一个字节数组作为缓冲区,读取文件时每次读取1024个字节,读取到文件末尾时返回-1。我们可以在while循环中对读取到的数据进行处理。

  使用BufferedInputStream类读取文件时,每次读取的数据会先被读入缓冲区中,当缓冲区的数据被读取完后,再从文件中读取新的数据。这样可以减少I/O操作的次数,提高读取数据的效率。

使用BufferedInputStream类写入文件

  在进行文件写入时,我们同样可以使用BufferedInputStream类来提高写入效率。下面是一个使用BufferedInputStream类写入文件的示例代码:

try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("./template/hello.txt"))) {
    byte[] data = "Hello, jym! ".getBytes();
    bos.write(data);
} catch (IOException e) {
    // handle exception
}

  上面的代码中,我们首先创建了一个BufferedOutputStream对象,并将其包装在一个try-with-resources语句中,这样可以自动关闭资源。然后我们将要写入的数据转换成字节数组,并通过BufferedOutputStream对象将数据写入到文件中。

  使用BufferedInputStream类写入文件时,每次写入的数据会先被写入到缓冲区中,当缓冲区的数据写满后,再将缓冲区中的数据一次性写入到文件中。这样可以减少I/O操作的次数,提高写入数据的效率。

测试用例

代码演示

  为了验证BufferedInputStream类的优化效果,我们可以编写一个测试用例。下面是一个使用BufferedInputStream类读取文件的测试用例:

package com.example.javase.io.bufferedInputStream;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * @author bug菌
 * @version 1.0
 * @date 2023/10/13 17:51
 */
public class BufferedInputStreamTest {

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("./template/hello.txt"))) {
            byte[] buffer = new byte[1024];
            int length;
            while ((length = bis.read(buffer)) != -1) {
                String content = new String(buffer, 0, length);
                System.out.println("读取内容为:" + content);
            }
        } catch (IOException e) {
            // handle exception
        }
        long end = System.currentTimeMillis();
        System.out.println("Time used: " + (end - start) + "ms");
    }
}

  上面的代码中,我们首先记录了开始时间,然后使用BufferedInputStream类读取文件,并在while循环中对读取到的数据进行处理。最后,在读取数据时,将字节数组转换为字符串,并使用 System.out.println() 方法打印出来;并记录了结束时间,并输出总共使用的时间。

  我们可以在不使用BufferedInputStream类的情况下编写一个相同的测试用例,然后比较两个测试用例的运行时间,从而验证BufferedInputStream类的优化效果。

测试代码解析

这段代码演示了如何使用BufferedInputStream读取文件内容。

  1. 首先导入需要的类,包括BufferedInputStreamFileInputStreamIOException等;

  2. 在main函数中,首先记录程序开始的时间;

  3. 使用try-with-resources语句创建一个BufferedInputStream对象,它包装了一个FileInputStream对象,用于读取指定路径下的hello.txt文件;

  4. 创建一个byte数组作为缓冲区,长度为1024;

  5. 使用while循环读取文件内容,每次读取的数据被存储在缓冲区中,长度由read()方法返回;

  6. 将从缓冲区中读取的字节转换为字符串,输出到控制台;

  7. catch IOException异常,处理异常;

  8. 记录程序结束的时间;

  9. 输出程序执行时间。

  需要注意的是,使用BufferedInputStream可以提高读取文件的效率,但在读取大文件时,可能会导致内存溢出,需要注意调整缓冲区的大小。

测试结果

如下是运行测试用例所得截图,仅供参考:

在这里插入图片描述

小结

  通过本文的介绍,我们了解了如何使用Java中的BufferedInputStream类来提高I/O操作的效率。我们可以使用BufferedInputStream类来读取文件和写入文件,达到减少I/O操作次数、提高效率的目的。同时我们还编写了一个测试用例来验证BufferedInputStream类的优化效果。

总结

  BufferedInputStream类是Java中的一个输入流缓冲器,它可以提高I/O操作的效率。我们可以使用BufferedInputStream类来读取文件和写入文件,达到减少I/O操作次数、提高效率的目的。通过编写测试用例,我们验证了BufferedInputStream类的优化效果。在进行文件操作时,我们可以尽可能使用BufferedInputStream类来提高效率。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/484014.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

实现文本溢出提示效果

实现效果 本文的需求是文本溢出时显示省略号&#xff0c;鼠标移入文本时提示完整的文字内容。 实现代码 <div class"container" onmouseover"handleMouseEnter(this)">鼠标移入显示全部内容</div><style> .container {width: 100px;o…

力扣每日一题 2024/3/23 统计桌面上的不同数字

题目描述 用例说明 思路讲解 给定整数n&#xff0c;找出循环十亿天后桌上的数字。可以先通过一天来找找规律。 第一天 n%i1 &#xff08;1<i<n&#xff09;只有n-1符合.加入桌面 第二天(n-1)%i1 &#xff08;1<i<n-1&#xff09;只有n-2符合 加入桌面 依次类推…

第十三届蓝桥杯JavaB组省赛真题 - 星期计算

解题思路&#xff1a; 方法一&#xff1a; 20的22次方是一个比较大的数&#xff0c;long和int都装不下这么大的数&#xff0c;因此需要使用下面的方法&#xff0c;如果 a, b, p 都是整数&#xff0c;且 p 是正数&#xff0c;那么&#xff1a;(a * b) % p (a % p * b % p) % …

【C语言】linux内核pci_set_drvdata函数

一、讲解 该函数pci_set_drvdata是Linux内核中用于PCI设备的一个辅助函数&#xff0c;其主要作用是设置给定PCI设备的驱动程序私有数据。这个函数的参数包括一个指向pci_dev结构体的指针pdev&#xff0c;该结构体描述了一个PCI设备&#xff0c;以及一个void *类型的指针data&a…

我父亲曾经告诉我:“等你到了35岁,你应该足够聪明地意识到...”。

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

iostream、fstream、sstream、string、vector、unordered_map、stack

iostream 用于输入输出操作&#xff0c;包含了处理标准输入输出流的功能&#xff08;例如&#xff0c;cin, cout, cerr等&#xff09;。 #include <iostream>int main() {int number;std::cout << "Enter a number: ";std::cin >> number;std::…

算法-最短路径

图的最短路径问题是一个经典的计算机科学和运筹学问题&#xff0c;旨在找到图中两个顶点之间的最短路径。这种问题在多种场景中都有应用&#xff0c;如网络路由、地图导航等。 解决图的最短路径问题有多种算法&#xff0c;其中最著名的包括&#xff1a; 1.迪杰斯特拉算法 (1).…

windows 系统下(nacos1.x) nacos-1.1.3 链接数据库 mysql8.0 出错分析

** windows 系统下&#xff08;nacos1.x&#xff09; nacos-1.1.3 链接数据库 mysql8.0 出错分析 ** 1、首先以下方法亲测无效&#xff1a; 1&#xff09;需要在数据库 URL 链接配置信息中 添加 allowPublicKeyRetrievaltrue 无效 db.url.0**&allowPublicKeyRetrievalt…

web前端之行为验证码、不同设备和屏幕尺寸呈现不同大小、元素宽度根据视口宽度进行调整、元素或图片裁剪、图片验证码

MENU 前言版本一(htmlJScss)版本二(htmlJScsscanvas) 前言 1、版本一的样式比较齐全&#xff1b; 2、版本二的JS逻辑和功能效果比较完善&#xff0c;且是别人的代码&#xff0c;后续会对样式进行完善。[Gitee | 哔哩哔哩]&#xff1b; 3、两个版本各有千秋&#xff0c;主要学习…

使用 ArcGIS Pro 和 Google Earth Engine 可视化地表温度

在这项研究中,利用 Landsat 热数据通过各种方法检查了 2013 年和 2023 年恰纳卡莱省的地表温度变化。使用了 NDVI、大气层顶部、亮度温度、植被比例和地表温度等公式。研究结果表明,从热图像中获得的数据,特别是地表温度(LST),是土地解释的重要资源。 研究区域:恰纳卡莱…

[Java、Android面试]_14_Retrofit的作用

本人今年参加了很多面试&#xff0c;也有幸拿到了一些大厂的offer&#xff0c;整理了众多面试资料&#xff0c;后续还会分享众多面试资料。 整理成了面试系列&#xff0c;由于时间有限&#xff0c;每天整理一点&#xff0c;后续会陆续分享出来&#xff0c;感兴趣的朋友可关注收…

2024 年广西职业院校技能大赛高职组《云计算应用》赛项赛题第 2 套

#需要资源或有问题的&#xff0c;可私博主&#xff01;&#xff01;&#xff01; #需要资源或有问题的&#xff0c;可私博主&#xff01;&#xff01;&#xff01; #需要资源或有问题的&#xff0c;可私博主&#xff01;&#xff01;&#xff01; 某企业根据自身业务需求&…

电脑数据守护神:自动备份数据的重要性与实用方案

一、数据安全的基石&#xff1a;自动备份数据的重要性 在数字化时代&#xff0c;电脑中的数据成为了我们生活和工作中不可或缺的一部分。无论是重要的工作文件、珍贵的个人照片&#xff0c;还是日常使用的应用程序&#xff0c;这些数据都承载着我们的记忆和劳动成果。然而&…

学习次模函数-第1章 引言

许多组合优化问题可以被转换为集合函数的最小化&#xff0c;集合函数是在给定基集合的子集的集合上定义的函数。同样地&#xff0c;它们可以被定义为超立方体的顶点上的函数&#xff0c;即&#xff0c;其中是基集合的基数-它们通常被称为伪布尔函数[27]。在这些集合函数中&…

taro框架之taro-ui中AtSwipeAction的使用

题记&#xff1a;所需效果&#xff1a;滑动删除 工作进程 官网文档代码 <AtSwipeAction options{[{text: 取消,style: {backgroundColor: #6190E8}},{text: 确认,style: {backgroundColor: #FF4949}} ]}><View classNamenormal>AtSwipeAction 一般使用场景</…

【Linux】进程地址空间详解

前言 在我们学习C语言或者C时肯定都听过老师讲过地址的概念而且老师肯定还会讲栈区、堆区等区域的概念&#xff0c;那么这个地址是指的物理内存地址吗&#xff1f;这里这些区域又是如何划分的呢&#xff1f; 我们在使用C语言的malloc或者C的new函数开辟空间时&#xff0c;开辟…

FreeRTOS(二)

第一部分 信号量 &#xff08;一&#xff09;信号量的本质 首先我们先来看队列的结构体&#xff0c;我们不难发现队列结构体中说到有个联合体在用于队列时&#xff0c;使用Queue&#xff0c;在用于信号量时&#xff0c;使用XSemaphore。后面又说到了一些对列的类型&#xff0…

简述C语言文件操作

&#x1f308; 个人主页&#xff1a;白子寰 &#x1f525; 分类专栏&#xff1a;魔法指针&#xff0c;进阶C&#xff0c;C语言&#xff0c;C语言题集&#xff0c;C语言实现游戏&#x1f448; 希望得到您的订阅和支持~ &#x1f4a1; 坚持创作博文(平均质量分79)&#xff0c;分享…

逻辑运算符、#define易错点

文章目录 逻辑运算符#define易错点 一、逻辑运算符 #include<stdio.h> #define PERIOD . int main() {char ch;int charcount0;while((chgetchar())!PERIOD){if(ch!"&&ch!\)charcount;}printf("There are %d non-quote characters.\n",charcount…

算法---动态规划练习-1(三步问题)

三步问题 1. 题目解析2. 讲解算法原理3. 编写代码 1. 题目解析 题目地址&#xff1a;三步问题 2. 讲解算法原理 1. 定义一个常量MOD为10^97&#xff0c;用于取模运算。 2. 创建一个长度为n3的数组dp&#xff0c;用于存储计算过程中的中间结果。数组的下标表示台阶的级数&…
最新文章