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):
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:
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
outerVar
kurang 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
outerVar
ora 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 saben loop
Diwiwiti karo Java 1.5, pangembang Java menehi desain
for each loop
sing diterangake ing situs Oracle ing Pandhuan sing diarani "
The For-Each Loop " utawa kanggo versi
1.5.0 . Umumé, bakal katon kaya iki:
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" :
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(); i.hasNext(); ) {
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
ArrayList
carane 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.this
iterator 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
forEachRemaining
sing 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
ArrayList
lan
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.asList
ngasilake 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,
ListIterator
iku diijini kanggo nindakake iki amarga
List
akses dening indeks dipun ginakaken.
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();
while (iterator.hasNext()) {
iterator.next();
iterator.remove();
}
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();
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 loop
mung "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
forEachRemaining
bisa 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
GO TO FULL VERSION