JavaRush /Java Blog /Random-TL /Byte-by-byte na gumagana sa mga file
Joysi
Antas

Byte-by-byte na gumagana sa mga file

Nai-publish sa grupo
Espesyal para sa

Magsimula na tayo

Sa antas 18, nagsimula ang mga unang gawain ng pagbabasa ng byte-by-byte na file: basahin ang file, pagkatapos ay hanapin ang minimum/maximum na byte o i-output ito sa isang ordered form, atbp.
Byte-by-byte na trabaho sa mga file - 1
Napakatalino ng mga tao dito. Alam nila ang tungkol sa mga koleksyon at maaari nilang ayusin at ipasok. Ang mga koleksyon ay isang makapangyarihang mekanismo. At marami ang hindi gumamit ng mga ito bago ang JavaRush. Siyempre, kapuri-puri na pag-aralan ang mga ito at subukang tamaan sila sa mga maling lugar. Kaya. Kumuha tayo ng isang problema na wala sa mga gawain (upang walang mga spoiler kapag nilutas ito), ngunit may mga katulad na katulad:
  • Ilagay ang pangalan ng file mula sa console
  • Basahin ang lahat ng byte mula sa isang file.
  • Hindi pinapansin ang mga pag-uulit, pag-uri-uriin ang mga ito ayon sa bytecode sa pababang pagkakasunud-sunod.
  • Pagpapakita
  • Isara ang I/O stream
Halimbawa ng mga byte ng isang input file 44 83 44 Halimbawa ng output 83 44 Dagdag pa rito, ipinakilala namin ang mga variable startTimeat finishTimeupang itala ang oras ng pagpapatupad ng programa. Para sa pagkalkula na ginamit ko ang i3-3GHz/8Gb RAM/HDD WD Blue-1Tb/Win7-64/jdk-8u73-windows-x64 (mga halimbawa ng mga programa sa mga opsyon 1-2 ay kinuha mula sa info.javarush forum, sila ay bahagyang binago lamang para sa pag-uuri sa pataas na pagkakasunud-sunod okay - ibig sabihin, sila ay TOTOO!!)

Lutasin natin ito nang direkta:

// Вариант 1. Загоняем в коллекцию и сортируем используя ее метод Collections.sort
public class Solution {
    public static void main(String[] args) throws Exception {
        FileInputStream inputStream = new FileInputStream(new BufferedReader(new InputStreamReader(System.in)).readLine());
        long startTime = System.currentTimeMillis();

        ArrayList<Integer> listData = new ArrayList<Integer>();
        while (inputStream.available() > 0) listData.add(inputStream.read());
        inputStream.close();
        ArrayList<Integer> result = new ArrayList<Integer>(new HashSet<Integer>(listData));
        Collections.sort(result);

        while (!result.isEmpty()) {
            System.out.print(result.get(result.size()-1) + " ");
            result.remove(result.get(result.size()-1));
        }

        long finishTime = System.currentTimeMillis();
        System.out.println("\nвремя работы=" + (finishTime-startTime) + "ms.");
    }
}
Mahusay ang lahat! Ang pagsubok (kung meron man, pasado na sana). Ngunit sa buhay, kakaunti ang mga file na naglalaman lamang ng linyang "Si Nanay ang naghugas ng frame." Bigyan natin ang ating programa ng 46MB na file (sa mga pamantayan ngayon, mukhang hindi ito gaanong). Ano ito, ang programa ay tumatakbo sa loob ng 220 segundo. Ang isang pagtatangka na magpakain ng 1Gb file sa gabi (ang laki ng MPEG4 na pelikula ay hindi sa pinakamahusay na kalidad) ay hindi nagtagumpay. Nagbabasa pa ako ng programa sa umaga - at kailangan ko nang pumasok sa trabaho. Ano ang problema? Marahil ay ginagamit ArrayList<Integer>na may 1 bilyong elemento sa loob. Ang bawat elemento nito ay tumatagal ng 16 bytes na minimum (Header: 8 bytes + Field int: 4 bytes + Alignment para sa multiplicity 8: 4 bytes). Sa kabuuan, boluntaryo kaming naglalagay ng 16 Gb ng data sa memorya na may laki ng RAM na 8. Gagawin namin ang mas mahusay. Sumisid tayo nang mas malalim sa mga koleksyon. At hurray, nakita namin ang kailangan namin.

Kilalanin ang TreeSet

Ito ay marami:
  • hindi pinapayagan ang pag-iimbak ng dalawang magkatulad na elemento (na nangangahulugang iimbak namin ang lahat ng 255 elemento sa memorya, sa halip na isang bilyon!)
  • kapag minamanipula ang mga elemento nito, awtomatiko itong nag-aayos (nag-aayos ng sarili - narito, ang taas ng pagiging perpekto!)
Nakukuha namin:
// Вариант 2. Загоняем в ТreeSet который сам сортирует (лютый win!)
public class Solution {
    public static void main(String[] args) throws Exception {
        FileInputStream inputStream = new FileInputStream(new BufferedReader(new InputStreamReader(System.in)).readLine());

        byte[] arrBytes = new byte[256];
        long startTime = System.currentTimeMillis();

        SortedSet<Integer> list = new TreeSet<Integer>();
        while(inputStream.available()>0) list.add(inputStream.read());
        inputStream.close();

        while (!list.isEmpty())        {
            System.out.print(list.last() + " ");
            list.remove(list.last());
        }

		long finishTime = System.currentTimeMillis();
        System.out.println("\nвремя работы=" + (finishTime-startTime) + "ms.");
    }
}
Ang output ay: 46MB file, 176 segundo. 1Gb file - 3 oras 5 minuto. Kitang-kita ang pag-unlad. Nagawa naming "maghintay" para sa mga resulta, at ang 46MB na file ay naproseso nang mas mabilis. Sige lang. Subukan nating talikuran ang mga koleksyon (ito ay magiging lubhang masakit para sa ilan). Gagamit kami ng mga simpleng arrays (napaka-primitive nito). Tandaan natin ang isang mahalagang bagay . Ang bilang ng mga byte na nakatagpo ay maaaring ilagay sa isang array na may haba na 256. Kaya't dadagdagan lang namin ng isa ang array element na tumutugma sa read byte.

Array - byte byte

// Вариант 3. Считываем массив поbyteно.
public class Solution {
    public static void main(String[] args) throws Exception {
        FileInputStream inputStream = new FileInputStream(new BufferedReader(new InputStreamReader(System.in)).readLine());

        long[] arrBytes = new long[256];
        long startTime = System.currentTimeMillis();

        while (inputStream.available() > 0) arrBytes[inputStream.read()]++;

		inputStream.close();
        // Выводим отсортированный по byte-codeу в обратном порядке
        for (long i = 255; i >= 0 ; i--)
            if (arrBytes[(int) i] > 0) System.out.print(i + " ");

			long finishTime = System.currentTimeMillis();
        System.out.println("\nвремя работы=" + (finishTime-startTime) + "ms.");
    }
}
Ang output ay: 46MB file, 158 segundo. 1Gb file - 2 oras 55 minuto. Muli isang pagpapabuti, ngunit maliit. At ginawa namin ang lahat gamit ang mga simpleng tool. Hindi gumamit ng mikroskopyo para sa pagmamaneho ng mga kuko . Ngayon isang lyrical digression. Tandaan natin ang istraktura ng isang computer. RAM memory (DRAM) , kung saan ang program ay karaniwang isinasagawa at ang mga variable ay nakaimbak, ay may mataas na bilis ng pag-access, ngunit maliit ang laki. Ang memorya sa isang hard/flash drive (HDD o Flash drive) kung saan ang mga file ay karaniwang naka-imbak, sa kabaligtaran, ay may mababang bilis ng pag-access ngunit isang malaking sukat. Kaya kapag nagbasa kami ng isang 1Gb file byte byte (iyon ay, na-access namin ang HDD ng isang bilyong beses), gumugugol kami ng maraming oras sa pagtatrabaho sa isang mababang bilis na aparato (naglilipat kami ng butil ng buhangin sa pamamagitan ng butil mula sa katawan ng isang trak ng KamAZ sa sandbox). Subukan nating pagbutihin pa ito.

Sabay-sabay nating itapon ang BUONG KAMAZ truck na may buhangin!

// Вариант 4. Считываем массив сразу целиком за раз в память.
public class Solution {
    public static void main(String[] args) throws Exception {
        FileInputStream inputStream = new FileInputStream(new BufferedReader(new InputStreamReader(System.in)).readLine());

        long[] arrBytes = new long[256];
        long startTime = System.currentTimeMillis();

        byte fileImage[]=new byte[inputStream.available()];
        long fileSize=fileImage.length;
        inputStream.read(fileImage);
        for (int i = 0; i = 0 ; i--)
            if (arrBytes[(int) i] > 0) System.out.print(i + " ");

		long finishTime = System.currentTimeMillis();
        System.out.println("\nвремя работы=" + (finishTime-startTime) + "ms.");
    }
}
isang maliit, ngunit muli mahalagang digression . Tandaan:
  1. Ang index ng arrBytes ay tinukoy sa loob ng 0..255,
  2. Ang fileImage ay isang byte array na ang mga elemento ay may value na -128..127
Samakatuwid, para magbilang ng mga byte, gagamit kami ng construction arrBytes[fileImage[i] & 0b11111111]++; na magre-reset lang ng sign bit at magbabalik sa amin ng value sa range na 0..255 At kaya, ang mga resulta: 46MB file na 0.13 segundo (mas mababa sa isang segundo). 1Gb file - 9 segundo. Nagawa natin! Kami ay hindi kapani-paniwalang cool! Binilisan mula 3 oras hanggang 9 na segundo. Iyon lang, maaari kang umupo sa iyong upuan at uminom ng tsaa. At ngayon isa pang eksperimento - subukan natin ang isang 32 Gb file (halimbawa, isang HD na pelikula). Bilang resulta, nakakakuha kami ng kaluskos na tunog mula sa gumaganang HDD kasama ang program na nag-crash sa Windows. Tinapon ni KamAZ ng buhangin ang katawan at nabasag ang sandbox! Anong gagawin natin? Tandaan natin ang isa pang katotohanan. Ang mga file sa OS ay karaniwang naka-imbak sa mga bahagi (mga kumpol) ng 2-64Kb (depende sa uri ng file system, mga setting, atbp.). Magbabasa tayo sa mga bahagi, halimbawa 64,000 bytes. Subukan nating i-unload ang KamAZ gamit ang isang excavator sa medyo malalaking bahagi:

Gamit ang buffer

// Вариант 5. Считываем массив кусками.
public class Solution {
    public static void main(String[] args) throws Exception {
        FileInputStream inputStream = new FileInputStream(new BufferedReader(new InputStreamReader(System.in)).readLine());

        long[] arrBytes = new long[256];
        long startTime = System.currentTimeMillis();

        int  bufferSize = 64000;
        byte buffer[]   = new byte[64000];

        while (inputStream.available() > 0) {
            if (inputStream.available() < 64000) bufferSize = inputStream.available();
            inputStream.read(buffer, 0, bufferSize );
            for (int i = 0; i = 0 ; i--)
            if (arrBytes[(int) i] > 0) System.out.print(i + " ");

		long finishTime = System.currentTimeMillis();
        System.out.println("\nвремя работы=" + (finishTime-startTime) + "ms.");
    }
}
Bilang resulta, nakakuha kami ng: 46MB file na 0.08 segundo (mas mababa sa isang segundo). 1Gb file - 0.9 segundo (mas mababa sa isang segundo). 32Gb file - 31 segundo. Tandaan na para sa isang 1 Gb na file ay napabuti namin ang pagganap mula sa ilang oras hanggang sa mga fraction ng segundo !!! Sa simpleng katotohanang ito, tatapusin namin ang eksperimento at pagbutihin ang paunang code. Nakagawa kami ng pag-unlad sa maraming paraan - nalulugod kami sa mga bagong tagapagpahiwatig ng pagkonsumo ng memorya at oras ng pagpapatakbo. Gayundin, sa kasong ito, hindi kami kumukuha ng mga walang kwentang koleksyon mula sa karaniwang aklatan. PS May magsasabi na ang halimbawa ay malayo, atbp. Ngunit mayroong maraming katulad na mga gawain - upang pag-aralan ang isang malaking dami ng mga elemento na may isang may hangganan na bilang ng mga estado. Halimbawa, ang mga imahe (RGB - karaniwang nakaimbak sa 24 bytes, sa aming kaso mahaba[] arrRGB = bagong haba[256*256*256] ay kukuha lamang ng 64MB sa memorya), musika (ang amplitude ay karaniwang na-digitize sa 16 o 24 bits ) o mga discrete indicator sensor, atbp.
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION