Come on guys, let’s meet.

Secular wanderer: a programmer who focuses on technical research

In the previous few articles, we introduced them separately

  • ArrayList
  • LinkedList
  • HashMap

LinkedHashMap descends from HashMap, which we won’t cover here

  • TreeMap

According to the previous structure diagram, we still lack Set to be introduced.

To be honest, I’m not going to go into the details of sets here, just call their API methods. So let’s go through this really quickly.

Set

A Set is a Set that contains no repeating elements and is implemented based on a Map

That’s why we don’t talk about Set, right

basis

In the Set Set, its source code implementation directly depends on Map to achieve, the Set added elements, through the Map’s Key to store, the Value part is through the Object Object to fill, the implementation method is as follows:

Has HashSet, for example

private static final Object PRESENT = new Object();

public HashSet(a) {
    map = new HashMap<>();
}

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}
Copy the code

You can see how the code above works

The classification is as follows

Set a subset implementation
HashSet HashMap
TreeSet TreeMap
LinkedHashSet LinkedHashMap

According to the above simple analysis, we can also summarize some characteristics of Set:

  • Set is implemented based on a Map. Elements are stored in the Key of the Map and the Value part is filled with Object objects
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}
Copy the code
  • The Set element is non-repeatable, unique, and unordered

  • The container data can be sorted in TreeSet by setting up a collator

public TreeSet(Comparator<? super E> comparator) {
    this(new TreeMap<>(comparator));
}
Copy the code
  • When setting the initial size of a HashSet, you are setting the HashMap, so it is recommended to set the number to the NTH power of 2
public HashSet(int initialCapacity) {
    map = new HashMap<>(initialCapacity);
}
Copy the code

Here we introduce a method called combinatorial method

combination

In Java, there are two ways to build on a base class and then do something innovative in that class:

  • Inheriting base class (parent class) : A method that overrides its parent in a subclass.
  • Composite base classes: that is, subclasses can reuse base classes by calling methods of the base class. In Set, this is how the base classes are combined

By comparing the above two methods, the advantages of combining basic classes are as follows:

  • Inheritance means that the parent class is of the same type, and Set and Map are intended to express both types, so inheritance is not appropriate, and Java syntax limits prevent subclasses from inheriting only one parent class, which is difficult to extend later.
  • The combination is more flexible, can arbitrarily combine the existing base class, and can be extended and arranged on the basis of the method of the base class, and the method name can be arbitrarily named, do not need to be consistent with the method name of the base class.

IO

This article will mainly introduce IO in Java. IO is input/output; IO operations in Java are essentially operations on disk files

This is disk IO, of course, there is another called network IO

It can be said that the program we develop, the system bottleneck mainly appears in these two IO, we are in the development of these two in the code level to optimize

From the overall division, can be divided into three categories

  • BIO
  • NIO
  • AIO

BIO, the type of IO we’ll look at next, is called blocking IO.

NIO, called New IO, is a non-blocking IO.

NIO is a little more efficient than BIO

We’ll start with the basics, and we’ll cover the use of NIO in the Netty series

AIO will not be introduced for the time being, and will be introduced to the network later

So we need to know what is a file?

file

A file can be thought of as a collection of related records or data put together. Like the pictures and videos in our computer are files, which need to be opened by corresponding software. If we open them with plain text, it will be a pile of gibberish. In fact, we also know that this is binary data.

That is to say, binary data is sorted by certain encoding rules, and then opened by the corresponding software, we can clearly view the current file

So where is this data stored? Corresponding to the hardware can be stored in the U disk, hard disk and other hardware systems.

I’m sure you’ve all seen it

How do I read files from disk?

We can think of it this way:

The disk reads and writes tracks through a magnetic head connected to a drive arm. The drive arm locates the head to the track in a process called addressing

Source: Chapter 6 storage technology in Understanding Computer Systems

But it’s important to note that hardware has read units when it reads data

Now for an analogy:

So now we have 1 gigabyte of data, and we’re looking for 1 byte of data, how does this wall read?

We should understand that it is definitely not 1 byte 1 byte search, the operating system is already page read unit (4K size).

Therefore, even if we want to read 1 byte of data, the disk will read a page (4K), and then read the data from disk into memory, but there is a problem when reading into memory:

It’s 4K when you read from disk, but it’s not necessarily 4K when you read from memory, it depends on the hardware, it depends on the operating system, it could be 4K or a multiple of 4K,

But one thing is for sure: data is read on a per-page basis

We refer to the above statement through specific documents:

Therefore, there is a concept in hardware devices: disk prefetch

That is, no matter how many bytes of data are required, an integer multiple of the page will be read in each time

Here also implies a famous principle: the principle of locality

Access to program data is clustered, meaning that all data is clustered together. So disk prefetch can read a whole block of data, so the next time you need to find similar data, you don’t have to go to disk to look it up (analogous to caching)

The locality principle comes in two different forms:

  • Spatial locality

In a program with good spatial locality, if a memory location is referenced once, the program is likely to reference a nearby memory location in the near future

  • Time locality

In a program with good time locality, a memory location referenced once is likely to be referenced many times in the near future

Chapter 6.6.2 in Understanding Computer Systems in Depth

File

So let’s take a look at how files should be manipulated. In Java, we have a class for handling files: File

Let’s look at the constructor:

There are more constructors using the second and third methods than the others. Let’s take the second constructor as an example:

File file = new File("D:\\Working Directory\\project\\study\\study-java\\src\\main\\java\\zopx\\top\\study\\jav\\_file\\FileDemo.java");
// Check whether the current file exists: true
System.out.println(file.exists());
Copy the code

As such, there are many other API methods for this class. Here are a few commonly used methods. The rest of the methods can be found in the File API documentation:

File file = new File("D:\\Working Directory\\project\\study\\study-java\\src\\main\\java\\zopx\\top\\study\\jav\\_file\\FileDemo.java");
System.out.printf("Currently file :%s \n", file.isFile());
System.out.printf("Current folder :%s \n", file.isDirectory());
System.out.printf("Current file name :%s \n", file.getName());
System.out.printf("Absolute path of current file :%s \n", file.getAbsolutePath());
System.out.printf("File size :%s \n", file.length());

File newFile = new File("newCreateFile.txt");
newFile.createNewFile();
System.out.printf("NewFile exists: %s \n", newFile.exists());

newFile.delete();
System.out.printf("NewFile exists: %s \n", newFile.exists());
Copy the code

You’d better try it yourself

One more common method, let’s do a small example:

List all files under the specified folder, if folders, then continue traversing

Here we can use a method called listFiles(), which retrieves all files in the specified folder and objects of type File[].

The corresponding method is list(), which is similar to listFiles(), but yields an array of string objects

private static void _demo2(a) {
    File file = new File("D:\\Working Directory\\project");
    printFile(file, "| -");
}

private static void printFile(File file, String level) {
    File[] files = file.listFiles();
    for (File f : files) {
        if (f.isDirectory()) {
            System.out.println(level + f.getName());
            printFile(f, level + "-");
        } else{ System.out.println(level + f.getName()); }}}// The output is a lot, I will not give the effect of the picture, you try it yourself
Copy the code

The parameter FileFilter is used to filter files

Here also need to clarify a point: if you test, there is an exception but the program itself is not wrong, then we need to think about whether the specified directory is protected by the system directory

The document

That’s it for File, and see the official documentation for more of its API methods:

The File API methods

That’s about documentation, let’s talk about another one

IO stream

Above we can get the current file, but if we want to read the contents of the file from our code, what should we do?

That’s what we’re going to talk about next: flow

It can be understood as follows:

Sending data back from one file to another. There is a flow problem here, where we need to specify a reference to the flow.

The reference point, of course, needs no explanation: it’s the program we write

  • We read the data from the source file programmatically, and here is an input stream
  • The data is then programmatically written to the object file, here is the output stream

Take a look at the picture below to get a clear picture of the next flow

classification

The following classes are base classes

Classification by flow direction

  • The input stream
    • InputStream
    • Reader
  • The output stream
    • OutputStream
    • Writer

According to the content processing

  • Byte stream: 8-bit universal byte stream
    • InputStream
    • OutputStream
  • Characters of the flow: 16-bit Unicode character stream
    • Reader
    • Writer

Now that we understand its classification, how does a stream actually operate on a file

Byte stream

FileInputStream

As we said, InputStream is an interface class, and naturally we need to know about subclasses

FileInputStream is one of our most commonly used implementation classes for reading raw byte streams of files, all images, files, etc. It’s officially recommended, but it’s not limited to byte files. If you want to read text files, you can use it

Here’s how:

FileInputStream fileInputStream = new FileInputStream("D:\\Working Directory\\project\\study\\study-java\\src\\main\\java\\zopx\\top\\study\\jav\\_file\\FileInputStreamDemo.java");

// Get the file size in the stream
System.out.println(fileInputStream.available());
// Read the contents of the stream
System.out.println((char)fileInputStream.read());
Copy the code

There are officially three initialization methods:

  • The way a File object is passed
new FileInputStream(new File("File Name"));
Copy the code
  • The way the file name is passed in

The way above

  • FileDescriptor

Rarely used, this is a file descriptor class

The ones above are read one by one, so let’s see how to simplify the read operation

FileInputStream fileInputStream = new FileInputStream("D:\\Working Directory\\project\\study\\study-java\\src\\main\\java\\zopx\\top\\study\\jav\\_file\\FileInputStreamDemo.java");

// Read mode
byte[] buffer = new byte[1024];
int len  = 0;
// Read the contents into the byte array and store them temporarily
while((len = fileInputStream.read(buffer)) ! = -1) {
    // Output to the console
    System.out.println(new String(buffer, 0, len));
}
fileInputStream.close();
Copy the code

In the stream, if we get to the last position of the file, we get -1 when we read through the read() method, so we can tell if the file has been read by checking len is -1

When using streams to manipulate files, it is best to close the stream at the end:

  • The stream operation is a very resource-intensive process, and if we only start the stream instead of closing it, the resource consumption can be very high

FileOutputStream

To be honest, when we’re doing a file operation, we can’t say output to the console, it doesn’t make any sense

To put it simply, what if we need to export content to a file?

This is where we use our output stream: FileOutputStream

Let’s do the following:

FileOutputStream fileOutputStream = new FileOutputStream("D:\\Working Directory\\project\\study\\study-java\\src\\main\\java\\zopx\\top\\study\\jav\\_file\\_FileInputStreamDemo.java".true);

// Output to a file
fileOutputStream.write(65);
fileOutputStream.write(66);
fileOutputStream.write(67);

// Close the stream: same as FileInputStream
fileOutputStream.close();
Copy the code

FileOutputStream is constructed in the same way as FileInputStream except that FileOutputStream takes a Boolean append

  • By default: appEnd =false, the output via FileOutputStream overwrites the contents of the file,
  • If appEnd =true, the content is appended to the end of the file

Point: We must get to the bottom of a problem

  • Is the flow direction problem: remember the flow direction diagram above, always use the program as a reference

Example: Copy a file

Now that FileInputStream and FileOutputStream are both out of the question, let’s do a quick example: copy a file

You have three seconds

private static void copyFile(a) {
    FileInputStream fis = null;
    FileOutputStream fos = null;

    try {
        File sourceFile = new File("D:\\Working Directory\\project\\study\\study-java\\src\\main\\java\\zopx\\top\\study\\jav\\_file\\FileInputStreamDemo.java");
        File targetFile = new File("D:\\Working Directory\\project\\study\\study-java\\src\\main\\java\\zopx\\top\\study\\jav\\_file\\_FileInputStreamDemo.java");

        fis = new FileInputStream(sourceFile);
        fos = new FileOutputStream(targetFile);

        byte[] buffer = new byte[1024];
        int len = 0;
        while((len = fis.read(buffer)) ! = -1) {
            fos.write(buffer, 0, len); }}catch (IOException e ) {
        e.printStackTrace();
    } finally {
        if (null! = fis) {try {
                fis.close();
            } catch(IOException e) { e.printStackTrace(); }}if (null! = fos) {try {
                fos.close();
            } catch(IOException e) { e.printStackTrace(); }}}}Copy the code

Sometimes we forget to close the stream, so we can also use the following approach

private static void copyFile2(a) {
        File sourceFile = new File("D:\\Working Directory\\project\\study\\study-java\\src\\main\\java\\zopx\\top\\study\\jav\\_file\\FileInputStreamDemo.java");
        File targetFile = new File("D:\\Working Directory\\project\\study\\study-java\\src\\main\\java\\zopx\\top\\study\\jav\\_file\\_FileInputStreamDemo.java");

    try (FileInputStream fis = newFileInputStream(sourceFile); FileOutputStream fos =new FileOutputStream(targetFile);) {
        byte[] buffer = new byte[1024];
        int len = 0;
        while((len = fis.read(buffer)) ! = -1) {
            fos.write(buffer, 0, len); }}catch(IOException e) { e.printStackTrace(); }}Copy the code

This approach will help us close the stream and reduce our one step

Here is the byte stream mode, let’s look at the character stream mode

Characters of the flow

As mentioned above, we recommend using character streams if we want to process text files and so on

I want to add here, do you think characters are physical concepts or logical concepts?

Let’s be clear: characters are logical concepts, and in the computer world, there is nothing that represents characters. Right

FileReader

FileReader is a subclass of Reader, so let’s see how it works

File file = new File("D:\\Working Directory\\project\\study\\study-java\\src\\main\\java\\zopx\\top\\study\\jav\\_file\\ReadDemo.java");

FileReader fileReader = new FileReader(file);
System.out.println((char)fileReader.read());
Copy the code

It’s basically the same as FileInputStream, but I won’t cover it here

FileWriter

FileWriter is the implementation of Writer. The operations are as follows:

FileWriter fileWriter = new FileWriter("D:\\Working Directory\\project\\study\\study-java\\src\\main\\java\\zopx\\top\\study\\jav\\_file\\_ReadDemo.java");
fileWriter.write("This is what I wrote through FileWriter.");
fileWriter.append("Append to content");
fileWriter.flush();
Copy the code

The FileOutputStream constructor is the same as the FileOutputStream constructor, but differs from it in API methods:

  • Write () : provides a way to write strings directly
  • Append () : provides methods to append

One of the biggest differences:

  • Although we are writing the content to the specified file using the write() method, we open the file and find that there is no content, because FileWriter will temporarily store the content in stream memory, and we need to flush the content from memory to the file manually: that is, call flush()

  • Call the close() method, which does two things:

    • Refresh the content to the file

    • Close the stream

However, in general, we recommend calling Flush () manually

Well, with that in mind, let’s familiarize ourselves with both by copying files above:

Example: Copy a file

Also, consider the time for 3 seconds:

private static void copyFile(a) {
    File sourceFile = new File("D:\\Working Directory\\project\\study\\study-java\\src\\main\\java\\zopx\\top\\study\\jav\\_file\\ReadDemo.java");
    File targetFile = new File("D:\\Working Directory\\project\\study\\study-java\\src\\main\\java\\zopx\\top\\study\\jav\\_file\\_AA.txt");
    try(FileReader fr = new FileReader(sourceFile); FileWriter fw = new FileWriter(targetFile)) {
        char[] buffer = new char[1024];
        int len = 0;
        while((len = fr.read(buffer)) ! = -1) {
            fw.write(buffer, 0, len); }}catch(IOException e) { e.printStackTrace(); }}Copy the code

The document

Their official documentation is given below, and more methods are recommended to see the API:

FileInputStream

FileOutputStream

FileReader

FileWriter