JavaRush /Blog Jawa /Random-JV /Kanggo lan Kanggo-Saben Loop: crita babagan carane aku di...
Viacheslav
tingkat

Kanggo lan Kanggo-Saben Loop: crita babagan carane aku diulang, diulang, nanging ora diulang

Diterbitake ing grup

Pambuka

Loop minangka salah sawijining struktur dhasar basa pamrograman. Contone, ing situs web Oracle ana bagean " Pelajaran: Dasar-Dasar Basa ", ing ngendi loop duwe pelajaran sing kapisah " The for Statement ". Ayo refresh dhasar: Daur ulang kasusun saka telung ekspresi (pernyataan): initialization (initialization), kondisi (terminasi) lan increment (increment):
Kanggo lan Kanggo Saben Loop: crita babagan carane aku ngulang, ngulang, nanging ora ngulang - 1
Sing nggumunake, kabeh iku opsional, tegese kita bisa, yen pengin, nulis:
for (;;){
}
Bener, ing kasus iki, kita bakal entuk loop tanpa wates, amarga Kita ora nemtokake syarat kanggo metu saka loop (terminasi). Ekspresi inisialisasi dieksekusi mung sapisan, sadurunge kabeh loop dieksekusi. Iku tansah worth ngelingi sing siklus wis orane katrangan dhewe. Iki tegese initialization , mandap , increment lan awak daur ulang ndeleng variabel sing padha. Orane katrangan tansah gampang kanggo nemtokake nggunakake kurung kriting. Kabeh ing njero kurung ora katon ing njaba kurung, nanging kabeh sing njaba kurung katon ing njero kurung. Initialization mung ekspresi. Contone, tinimbang miwiti variabel, sampeyan umume bisa nelpon cara sing ora bakal ngasilake apa-apa. Utawa mung skip, ninggalake spasi kosong sadurunge titik koma pisanan. Ekspresi ing ngisor iki nemtokake kondisi mandap . Anggere bener , loop dieksekusi. Lan yen palsu , pengulangan anyar ora bakal diwiwiti. Yen sampeyan ndeleng gambar ing ngisor iki, kita entuk kesalahan sajrone kompilasi lan IDE bakal sambat: ekspresi kita ing daur ulang ora bisa digayuh. Awit kita ora bakal duwe pengulangan siji ing daur ulang, kita bakal langsung metu, amarga palsu:
Kanggo lan Kanggo-Saben Loop: crita babagan carane aku diulang, diulang, nanging ora diulang - 2
Iku worth tetep mripat ing expression ing statement mandap : iku langsung nemtokake apa aplikasi bakal puteran telas. Increment minangka ekspresi sing paling gampang. Dieksekusi sawise saben pengulangan sing sukses saka daur ulang. Lan ekspresi iki uga bisa dilewati. Tuladhane:
int outerVar = 0;
for (;outerVar < 10;) {
	outerVar += 2;
	System.out.println("Value = " + outerVar);
}
Nalika sampeyan bisa ndeleng saka conto, saben pengulangan saka daur ulang kita bakal nambah ing increments 2, nanging mung anggere Nilai outerVarkurang saka 10. Kajaba iku, wiwit expression ing statement nambah bener mung expression, iku. bisa ngemot apa wae. Mulane, ora ana sing nglarang nggunakake decrement tinimbang nambah, i.e. nyuda regane. Sampeyan kudu tansah ngawasi nulis saka increment. +=nindakake nambah pisanan lan banjur assignment, nanging yen ing conto ing ndhuwur kita nulis ngelawan, kita bakal njaluk daur ulang tanpa wates, amarga variabel outerVarora bakal nampa nilai diganti: ing kasus iki bakal =+diwilang sawise assignment. Miturut cara, iku padha karo nambah tampilan ++. Contone, kita duwe loop:
String[] names = {"John","Sara","Jack"};
for (int i = 0; i < names.length; ++i) {
	System.out.println(names[i]);
}
Siklus bisa lan ora ana masalah. Nanging banjur wong refactoring teka. Dheweke ora ngerti kenaikan lan mung nindakake iki:
String[] names = {"John","Sara","Jack"};
for (int i = 0; i < names.length;) {
	System.out.println(names[++i]);
}
Yen tandha increment katon ing ngarepe nilai, iki tegese bakal nambah dhisik banjur bali menyang panggonan sing dituduhake. Ing conto iki, kita bakal langsung miwiti ngekstrak unsur ing indeks 1 saka larik, ngliwati sing pisanan. Banjur ing indeks 3 kita bakal nabrak karo kesalahan " java.lang.ArrayIndexOutOfBoundsException ". Kaya sing wis sampeyan duga, iki bisa ditindakake sadurunge mung amarga kenaikan kasebut diarani sawise pengulangan wis rampung. Nalika nransfer ekspresi iki menyang pengulangan, kabeh rusak. Dadi metu, malah ing daur ulang prasaja sampeyan bisa nggawe kekacoan) Yen sampeyan duwe Uploaded, Mungkin ana sawetara cara sing luwih gampang kanggo nampilake kabeh unsur?
Kanggo lan Kanggo Saben Loop: crita babagan carane aku ngulang, ngulang, nanging ora ngulang - 3

Kanggo saben loop

Diwiwiti karo Java 1.5, pangembang Java menehi desain for each loopsing diterangake ing situs Oracle ing Pandhuan sing diarani " The For-Each Loop " utawa kanggo versi 1.5.0 . Umumé, bakal katon kaya iki:
Kanggo lan Kanggo Saben Loop: crita babagan carane aku ngulang, ngulang, nanging ora ngulang - 4
Sampeyan bisa maca katrangan babagan konstruksi iki ing Spesifikasi Basa Jawa (JLS) kanggo mesthekake yen ora sihir. construction iki diterangake ing bab " 14.14.2. Ing meningkat kanggo statement ". Nalika sampeyan bisa ndeleng, kanggo saben daur ulang bisa digunakake karo susunan lan sing ngleksanakake java.lang.Iterable antarmuka . Sing, yen pengin tenan, sampeyan bisa ngleksanakake antarmuka java.lang.Iterable lan kanggo saben daur ulang bisa digunakake karo kelas. Sampeyan bakal langsung ngomong, "Oke, iku obyek sing bisa diulang, nanging array dudu obyek. Lan sampeyan bakal salah, amarga ... Ing Jawa, array minangka obyek sing digawe kanthi dinamis. Spesifikasi basa ngandhani iki: " Ing basa pemrograman Java, arrays are objects ." Umumé, array minangka sihir JVM, amarga ... carane Uploaded wis kabentuk internal ora dingerteni lan dumunung nang endi wae nang Java Virtual Machine. Sapa wae sing kasengsem bisa maca jawaban ing stackoverflow: " Carane kelas array bisa digunakake ing Jawa? " Pranyata yen kita ora nggunakake array, banjur kita kudu nggunakake soko sing ngleksanakake Iterable . Tuladhane:
List<String> names = Arrays.asList("John", "Sara", "Jack");
for (String name : names) {
	System.out.println("Name = " + name);
}
Kene sampeyan mung bisa ngelingi yen kita nggunakake koleksi ( java.util.Collection ), thanks kanggo iki kita njaluk persis Iterable . Yen obyek wis kelas sing ngleksanakake Iterable, iku kapekso kanggo nyedhiyani, nalika cara iterator disebut, Iterator sing bakal iterate liwat isi obyek sing. Kode ing ndhuwur, contone, bakal duwe bytecode kaya iki (ing IntelliJ Idea sampeyan bisa nindakake "View" -> "Show bytecode" :
Kanggo lan Kanggo Saben Loop: crita babagan carane aku ngulang, ngulang, nanging ora ngulang - 5
Nalika sampeyan bisa ndeleng, iterator bener digunakake. Yen ora kanggo saben loop , kita kudu nulis kaya:
List<String> names = Arrays.asList("John", "Sara", "Jack");
for (Iterator i = names.iterator(); /* continue if */ i.hasNext(); /* skip increment */) {
	String name = (String) i.next();
	System.out.println("Name = " + name);
}

Iterator

Kaya sing wis dingerteni ing ndhuwur, antarmuka Iterable ujar manawa kanggo sawetara obyek, sampeyan bisa entuk iterator sing bisa digunakake kanggo ngulang konten kasebut. Maneh, iki bisa diarani Prinsip Tanggung Jawab Tunggal saka SOLID . Struktur data dhewe ngirim ora drive traversal, nanging bisa nyedhiyani siji sing ngirim. Implementasi dhasar saka Iterator iku biasane diumumake minangka kelas njero sing nduweni akses menyang isi kelas njaba lan nyedhiyakake unsur sing dikarepake sing ana ing kelas njaba. Punika conto saka kelas ArrayListcarane iterator ngasilake unsur:
public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
}
Kaya sing kita deleng, kanthi bantuan ArrayList.thisiterator ngakses kelas njaba lan variabel kasebut elementData, banjur ngasilake unsur saka ing kono. Dadi, entuk iterator gampang banget:
List<String> names = Arrays.asList("John", "Sara", "Jack");
Iterator<String> iterator = names.iterator();
Karya teka mudhun kanggo kasunyatan sing kita bisa mriksa apa ana unsur luwih ( cara hasNext ), njaluk unsur sabanjuré ( cara sabanjuré ) lan cara mbusak , kang mbusak unsur pungkasan ditampa liwat sabanjuré . Cara mbusak iku opsional lan ora dijamin bakal dileksanakake. Nyatane, nalika Jawa berkembang, antarmuka uga berkembang. Mulane, ing Jawa 8 uga ana cara forEachRemainingsing ngidini sampeyan nindakake sawetara tumindak ing unsur sing isih ora dibukak dening iterator. Apa sing menarik babagan iterator lan koleksi? Contone, ana kelas AbstractList. Iki minangka kelas abstrak sing dadi induk ArrayListlan LinkedList. Lan menarik kanggo kita amarga lapangan kaya modCount . Saben ngganti isi dhaftar diganti. Dadi apa sing penting kanggo kita? Lan kasunyatan manawa iterator nggawe manawa sajrone operasi, koleksi sing diulang ora owah. Nalika sampeyan ngerti, implementasine iterator kanggo dhaptar dumunung ing panggonan sing padha karo modcount , yaiku, ing kelas AbstractList. Ayo katon ing conto prasaja:
List<String> names = Arrays.asList("John", "Sara", "Jack");
names = new ArrayList(names);
Iterator<String> iterator = names.iterator();
names.add("modcount++");
System.out.println(iterator.next());
Iki minangka bab pisanan sing menarik, sanajan ora ana ing topik. Bener Arrays.asListngasilake khusus dhewe ArrayList( java.util.Arrays.ArrayList ). Ora ngleksanakake cara nambah, saengga ora bisa diowahi. Iki ditulis babagan ing JavaDoc: fixed-size . Nanging nyatane, iku luwih saka ukuran tetep . Iku uga immutable , sing, unchangeable; mbusak uga ora bisa digunakake. Kita uga bakal entuk kesalahan, amarga ... Sawise nggawe iterator, kita ngelingi modcount ing . Banjur kita ngganti negara koleksi "eksternal" (i.e., ora liwat iterator) lan kaleksanan cara iterator. Mulane, kita entuk kesalahan: java.util.ConcurrentModificationException . Kanggo ngindhari iki, owah-owahan sajrone iterasi kudu ditindakake liwat iterator dhewe, lan ora liwat akses menyang koleksi:
List<String> names = Arrays.asList("John", "Sara", "Jack");
names = new ArrayList(names);
Iterator<String> iterator = names.iterator();
iterator.next();
iterator.remove();
System.out.println(iterator.next());
Kaya sing sampeyan ngerteni, yen iterator.remove()sampeyan ora nindakake sadurunge iterator.next(), mula amarga. iterator ora nuduhake unsur sembarang, banjur kita bakal njaluk kesalahan. Ing conto, iterator bakal pindhah menyang unsur John , mbusak, lan banjur njaluk unsur Sara . Lan ing kene kabeh bakal apik, nanging nasib ala, maneh ana "nuansa") java.util.ConcurrentModificationException mung bakal kedadeyan nalika hasNext()bali bener . Sing, yen sampeyan mbusak unsur pungkasan liwat koleksi dhewe, iterator ora bakal tiba. Kanggo luwih rinci, luwih becik nonton laporan babagan teka-teki Jawa saka " #ITsubbotnik Section JAVA: Java puzzles ". Kita miwiti obrolan sing rinci kanthi alesan sing prasaja yen nuansa sing padha ditrapake nalika for each loop... iterator favorit kita digunakake ing hood. Lan kabeh nuansa iki uga ditrapake ing kana. Ing bab mung, kita ora bakal duwe akses kanggo iterator, lan kita ora bakal bisa kanggo aman mbusak unsur. Miturut cara, sampeyan ngerti, negara eling ing wayahe iterator digawe. Lan pambusakan aman mung bisa digunakake ing ngendi diarani. Sing, pilihan iki ora bakal bisa:
Iterator<String> iterator1 = names.iterator();
Iterator<String> iterator2 = names.iterator();
iterator1.next();
iterator1.remove();
System.out.println(iterator2.next());
Amarga kanggo iterator2 pambusakan liwat iterator1 "eksternal", yaiku, dileksanakake nang endi wae ing njaba lan dheweke ora ngerti apa-apa. Ing topik iterator, aku uga pengin nyathet iki. A iterator khusus lan lengkap digawe khusus kanggo implementasi antarmuka List. Banjur dijenengi ListIterator. Iki ngidini sampeyan ora mung maju, nanging uga mundur, lan uga ngidini sampeyan ngerteni indeks saka unsur sadurunge lan sabanjure. Kajaba iku, ngidini sampeyan ngganti unsur saiki utawa nglebokake sing anyar ing posisi antarane posisi iterator saiki lan sabanjure. Minangka sampeyan guessed, ListIteratoriku diijini kanggo nindakake iki amarga Listakses dening indeks dipun ginakaken.
Kanggo lan Kanggo-Saben Loop: crita babagan carane aku diulang, diulang, nanging ora diulang - 6

Jawa 8 lan Iterasi

Rilis Java 8 wis nggawe urip luwih gampang kanggo akeh. Kita uga ora nglirwakake iterasi babagan isi obyek. Kanggo ngerti cara kerjane, sampeyan kudu ngomong sawetara tembung babagan iki. Java 8 ngenalaken kelas java.util.function.Consumer . Iki contone:
Consumer consumer = new Consumer() {
	@Override
	public void accept(Object o) {
		System.out.println(o);
	}
};
Konsumen minangka antarmuka fungsional, tegese ing antarmuka mung ana 1 metode abstrak sing ora ditindakake sing mbutuhake implementasi wajib ing kelas kasebut sing nemtokake implementasi antarmuka iki. Iki ngidini sampeyan nggunakake barang gaib kaya lambda. Artikel iki dudu babagan, nanging kita kudu ngerti sebabe kita bisa nggunakake. Dadi, nggunakake lambdas, Konsumen ing ndhuwur bisa ditulis maneh kaya mangkene: Consumer consumer = (obj) -> System.out.println(obj); Iki tegese Jawa ndeleng manawa ana sing diarani obj bakal dikirim menyang input, banjur ekspresi sawise -> bakal dieksekusi kanggo obj iki. Kanggo pengulangan, saiki kita bisa nindakake iki:
List<String> names = Arrays.asList("John", "Sara", "Jack");
Consumer consumer = (obj) -> System.out.println(obj);
names.forEach(consumer);
Yen sampeyan pindhah menyang cara forEach, sampeyan bakal weruh sing kabeh iku edan prasaja. Ana sing favorit kita for-each loop:
default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
}
Sampeyan uga bisa mbusak unsur kanthi apik nggunakake iterator, contone:
List<String> names = Arrays.asList("John", "Sara", "Jack");
names = new ArrayList(names);
Predicate predicate = (obj) -> obj.equals("John");
names.removeIf(predicate);
Ing kasus iki, cara removeIf njupuk minangka input dudu Konsumen , nanging Predikat . Iki ngasilake boolean . Ing kasus iki, yen predikat ngandika " bener ", banjur unsur bakal dibusak. Iku menarik sing ora kabeh ketok ing kene uga)) Inggih, apa sing dikarepake? Wong kudu diwenehi papan kanggo nggawe teka-teki ing konferensi kasebut. Contone, ayo njupuk kode ing ngisor iki kanggo mbusak kabeh sing bisa ditindakake dening iterator sawise sawetara iterasi:
List<String> names = Arrays.asList("John", "Sara", "Jack");
names = new ArrayList(names);
Iterator<String> iterator = names.iterator();
iterator.next(); // Курсор на John
while (iterator.hasNext()) {
    iterator.next(); // Следующий элемент
    iterator.remove(); // Удалor его
}
System.out.println(names);
Oke, kabeh bisa digunakake ing kene. Nanging kita elinga yen Jawa 8 sawise kabeh. Mula, ayo nyoba nyederhanakake kode kasebut:
List<String> names = Arrays.asList("John", "Sara", "Jack");
names = new ArrayList(names);
Iterator<String> iterator = names.iterator();
iterator.next(); // Курсор на John
iterator.forEachRemaining(obj -> iterator.remove());
System.out.println(names);
Apa pancene wis dadi luwih ayu? Nanging, bakal ana java.lang.IllegalStateException . Lan alesane yaiku ... bug ing Jawa. Pranyata metu sing tetep, nanging ing JDK 9. Punika link kanggo tugas ing OpenJDK: Iterator.forEachRemaining vs. Iterator.remove . Alamiah, iki wis rembugan: Apa iterator.forEachRemaining ora mbusak unsur ing Consumer lambda? Ya, cara liya langsung liwat API Stream:
List<String> names = new ArrayList(Arrays.asList("John", "Sara", "Jack"));
Stream<String> stream = names.stream();
stream.forEach(obj -> System.out.println(obj));

kesimpulan

Kaya sing kita deleng saka kabeh materi ing ndhuwur, daur ulang for-each loopmung "gula sintaksis" ing ndhuwur iterator. Nanging, saiki digunakake ing akeh panggonan. Kajaba iku, produk apa wae kudu digunakake kanthi ati-ati. Contone, wong sing ora mbebayani forEachRemainingbisa ndhelikake kejutan sing ora nyenengake. Lan iki maneh mbuktekake manawa tes unit dibutuhake. Tes sing apik bisa ngenali kasus panggunaan kasebut ing kode sampeyan. Apa sampeyan bisa nonton / maca babagan topik: #Viacheslav
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION