JavaRush /Java blogi /Random-UZ /Fayllar bilan bayt-bayt ishlash
Joysi
Daraja

Fayllar bilan bayt-bayt ishlash

Guruhda nashr etilgan
uchun maxsus

Qani boshladik

18-darajada bayt-bayt faylni o'qishning birinchi vazifalari boshlandi: faylni o'qing, keyin minimal/maksimal baytlarni toping yoki uni tartiblangan shaklda chiqaring va hokazo.
Fayllar bilan bayt-bayt ishlash - 1
Bu yerdagi odamlar juda aqlli. Ular kollektsiyalar va saralash va kiritish haqida bilishadi. To'plamlar kuchli mexanizmdir. Va JavaRushdan oldin ko'pchilik ulardan umuman foydalanmagan. Ularni o‘rganib, noto‘g‘ri joylarga urishga harakat qilish, albatta, tahsinga sazovor. Shunday qilib. Keling, vazifalarda bo'lmagan muammoni olaylik (uni hal qilishda hech qanday buzuqlik bo'lmasligi uchun), lekin juda o'xshashlari bor:
  • Konsoldan fayl nomini kiriting
  • Fayldagi barcha baytlarni o'qing.
  • Takrorlashlarga e'tibor bermay, ularni bayt-kod bo'yicha kamayish tartibida tartiblang.
  • Displey
  • I/U oqimini yoping
Kirish faylining baytlari misoli 44 83 44 Chiqish misoli 83 44 Biz qo'shimcha ravishda o'zgaruvchilar kiritdik startTimeva finishTimedasturning bajarilish vaqtini qayd qildik. Hisoblash uchun men i3-3GHz/8Gb RAM/HDD WD Blue-1Tb/Win7-64/jdk-8u73-windows-x64 dan foydalandim (1-2-variantlardagi dasturlarning misollari info.javarush forumidan olingan, ular biroz faqat o'sish tartibida saralash uchun o'zgartirildi, ya'ni ular REAL!!)

Keling, buni to'g'ridan-to'g'ri hal qilaylik:

// Вариант 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.");
    }
}
Hamma narsani ajoyib tarzda hal qiladi! Sinov (agar bo'lgan bo'lsa, u portlash bilan o'tgan bo'lardi). Ammo hayotda "Onam ramkani yuvdi" satrini o'z ichiga olgan bir nechta fayllar mavjud. Keling, dasturimizni 46 MB hajmli fayl bilan ta'minlaylik (bugungi standartlarga ko'ra, bu unchalik ko'p emas). Bu nima, dastur 220 soniya ishlaydi. Kechqurun 1 Gb faylni oziqlantirishga urinish (MPEG4 filmining o'lchami eng yaxshi sifatga ega emas) muvaffaqiyatsiz tugadi. Men hali ertalab dasturni o'qiyotgan edim - va men allaqachon ishga borishim kerak edi. Muammo nimada? ArrayList<Integer>Ehtimol , ichkarida 1 milliard element mavjud bo'lgan foydalanishda . Uning har bir elementi kamida 16 baytni oladi (Sarlavha: 8 bayt + Field int: 4 bayt + Ko'plik uchun hizalama 8: 4 bayt). Hammasi bo'lib, biz ixtiyoriy ravishda 16 Gb ma'lumotni RAM hajmi 8. Biz yaxshiroq qilamiz. Keling, to'plamlarga chuqurroq kiraylik. Va shoshiling, biz kerakli narsani topdik.

TreeSet bilan tanishing

Bu juda ko'p:
  • ikkita bir xil elementni saqlashga ruxsat bermaydi (ya'ni biz milliard o'rniga barcha 255 elementni xotirada saqlaymiz!)
  • uning elementlarini manipulyatsiya qilganda, u avtomatik ravishda tartibga soladi (o'zini saralaydi - mana bu mukammallik balandligi!)
Biz olamiz:
// Вариант 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.");
    }
}
Chiqish: 46 MB fayl, 176 soniya. 1 Gb fayl - 3 soat 5 daqiqa. Taraqqiyot aniq. Biz natijalarni "kutishimiz" mumkin edi va 46 MB fayl sezilarli darajada tezroq qayta ishlanadi. Davom etishga ruxsat. Keling, kollektsiyalardan voz kechishga harakat qilaylik (bu ba'zilar uchun juda og'riqli bo'ladi). Biz oddiy massivlardan foydalanamiz (bu juda ibtidoiy). Bir muhim narsani ta'kidlaymiz . 256 uzunlikdagi massivga duch kelgan baytlar sonini kiritish mumkin. Shunday qilib, biz shunchaki o'qilgan baytga mos keladigan massiv elementini bittaga oshiramiz.

Massiv - bayt-bayt

// Вариант 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.");
    }
}
Chiqish: 46 MB fayl, 158 soniya. 1 Gb fayl - 2 soat 55 daqiqa. Yana yaxshilanish, lekin kichik. Va biz hamma narsani oddiy asboblar bilan qildik. Tirnoqlarni haydash uchun mikroskop ishlatilmadi . Endi lirik chekinish. Keling, kompyuterning tuzilishini eslaylik. RAM xotirasi (DRAM) , odatda dastur bajariladi va o'zgaruvchilar saqlanadi, kirish tezligi yuqori, lekin hajmi kichik. Odatda fayllar saqlanadigan qattiq / flesh-diskdagi (HDD yoki Flash drayvlar) xotira, aksincha, kirish tezligi past, lekin katta hajmga ega. Shunday qilib, biz 1 Gb faylni bayt-bayt o'qiganimizda (ya'ni biz HDD-ga milliard marta kiramiz), biz past tezlikda ishlaydigan qurilma bilan ishlash uchun ko'p vaqt sarflaymiz (biz KamAZ yuk mashinasining tanasidan qum donini don bilan o'tkazamiz. qum qutisiga). Keling, uni yanada yaxshilashga harakat qilaylik.

Keling, birdaniga BUTUN KAMAZni qum bilan to'kib tashlaymiz!

// Вариант 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.");
    }
}
kichik, lekin yana muhim bir chekinish . Eslatma:
  1. arrBytes indeksi 0..255 ichida aniqlanadi,
  2. fileImage bayt massivi bo'lib, uning elementlari -128..127 qiymatiga ega
arrBytes[fileImage[i] & 0b11111111]++; Shuning uchun, baytlarni hisoblash uchun biz shunchaki belgi bitini qayta o'rnatadigan va bizga 0..255 oralig'idagi qiymatni qaytaradigan konstruktsiyadan foydalanamiz Va shunday qilib, natijalar: 46MB fayl 0,13 soniya (bir soniyadan kam). 1 Gb fayl - 9 soniya. Biz uddaladik! Biz nihoyatda zo'rmiz! 3 soatdan 9 soniyagacha tezlashdi. Bo‘ldi, o‘rindiqqa o‘tirib, choy ichishing mumkin. Va endi yana bir tajriba - keling, 32 Gb faylni sinab ko'raylik (masalan, HD film). Natijada, biz Windows-da ishlamay qolgan dastur bilan ishlaydigan HDD-dan qarsillab ovoz chiqaramiz. KamAZ jasadni qum bilan tashladi va qum qutisini sindirdi! Nima qilamiz? Yana bir faktni eslaylik. Operatsion tizimdagi fayllar odatda 2-64 Kb (fayl tizimining turiga, sozlamalariga va boshqalarga qarab) bo'limlarda (klasterlarda) saqlanadi. Biz qismlarga bo'lib o'qiymiz, masalan, 64 000 bayt. Keling, KamAZni ekskavator bilan juda katta qismlarga tushirishga harakat qilaylik:

Buferdan foydalanish

// Вариант 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.");
    }
}
Natijada, biz oldik: 46 MB fayl 0,08 soniya (bir soniyadan kam). 1 Gb fayl - 0,9 soniya (bir soniyadan kam). 32 Gb fayl - 31 soniya. E'tibor bering, 1 Gb fayl uchun biz ishlashni bir necha soatdan soniyalarning kasrlarigacha oshirdik !!! Ushbu oddiy fakt bilan biz tajribani yakunlaymiz va dastlabki kodni yaxshilaymiz. Biz ko'p jihatdan muvaffaqiyatga erishdik - xotira iste'moli va ish vaqtining yangi ko'rsatkichlaridan mamnunmiz. Bundan tashqari, bu holda, biz standart kutubxonadan keraksiz to'plamlarni tortib olmaymiz. P.S. Kimdir misolni o'ta noto'g'ri deb aytadi va hokazo. Ammo shunga o'xshash juda ko'p vazifalar mavjud - cheklangan miqdordagi holatlarga ega bo'lgan katta hajmdagi elementlarni tahlil qilish. Masalan, tasvirlar (RGB - odatda 24 baytda saqlanadi, bizning holatlarimizda long[] arrRGB = new long[256*256*256] xotirada atigi 64MB joy egallaydi), musiqa (amplituda odatda 16 yoki 24 bitda raqamlanadi. ) yoki diskret ko'rsatkichlar datchiklari va boshqalar.
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION