日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

使用Java迭代器修改数据时要小心

發(fā)布時(shí)間:2023/12/3 java 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用Java迭代器修改数据时要小心 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

隨著本學(xué)期的結(jié)束,我想我會(huì)分享一個(gè)關(guān)于我對Java迭代器非常非常熟悉的小故事。

現(xiàn)實(shí)世界語境

就上下文而言,我教第二年的軟件組件課程,這是嘗試進(jìn)入該專業(yè)的學(xué)生的最后障礙。 當(dāng)然,這門課程對學(xué)生來說壓力很大,我經(jīng)常必須加倍努力,為他們提供一切成功的機(jī)會(huì)。

不幸的是,本學(xué)期我們被大流行所籠罩,不得不轉(zhuǎn)換為在線教學(xué)。 結(jié)果,我們不得不對教學(xué)做出一些快速?zèng)Q策,從而改變了學(xué)生的學(xué)習(xí)方式。 特別是,我們將所有的紙筆考試都轉(zhuǎn)換為在線測驗(yàn)。

對于某些學(xué)生來說,這是一個(gè)很大的祝福。 畢竟,這些測驗(yàn)并沒有比考試更困難,因此我們將其設(shè)為公開考試。 換句話說,我們使課程變得更容易讓他們通過。

當(dāng)然,學(xué)生遍布世界各地,他們無法獲得所需的幫助。 此外,學(xué)生沒有像考試那樣認(rèn)真地學(xué)習(xí)。 這種組合創(chuàng)造了一些非常糟糕的測驗(yàn)分?jǐn)?shù)。

到我們進(jìn)行第四個(gè)測驗(yàn)時(shí),學(xué)生們已經(jīng)非常沮喪。 實(shí)際上,我從幾位教師那里聽說他們的學(xué)生已經(jīng)厭倦了“技巧性問題”。 作為一名講師,聽到這有些令人沮喪,因?yàn)樗鼈兪欠浅5湫偷目荚噯栴}。 我們并沒有為他們增加困難,但這是我第一次聽到這些抱怨。

示例問題

然后,發(fā)生了一些奇怪的事情。 我們給了他們一個(gè)我真的不知道答案的問題,結(jié)果有點(diǎn)類似以下內(nèi)容:

以下代碼片段后面的Set <NaturalNumber> nums變量的值是什么?

Set<NaturalNumber> nums = new SomeSetImplementation<>(); nums.add( new NaturalNumber2( 1 )); nums.add( new NaturalNumber2( 5 )); nums.add( new NaturalNumber2( 6 )); for (NaturalNumber n : nums) { n.increment(); }

當(dāng)然,學(xué)生的選擇如下:

  • nums = {1,5,6,2,6,7}
  • nums = {2,6,7}
  • nums = {1,5,6}
  • 從提供的信息中無法分辨。

現(xiàn)在,就上下文而言,此示例中有一些內(nèi)部組件。

首先,NaturalNumber是一個(gè)可變的類,表示無界的非負(fù)整數(shù)。 換句話說,NaturalNumber的范圍可以從零到無窮大。 此外,可以使用一系列基本數(shù)學(xué)運(yùn)算來修改NaturalNumber,如下所示:

  • increment()加1 this
  • add(NaturalNumber n) :將n添加this

此外,這個(gè)問題讓使用的Set是類似于一個(gè)數(shù)學(xué)集合。 這里的想法是Set具有兩個(gè)主要屬性:

  • Set缺少重復(fù)項(xiàng)(即{1,2,1}不是合法集合)。
  • Set是無序的(即{1,2,3}和{3,2,1}是等效的)。
  • 作為參考,如果您有興趣詳細(xì)信息,請?jiān)谡n程網(wǎng)站上完整地記錄這兩個(gè)組件 。 所有組件均使用“按合同設(shè)計(jì)”編寫,因此每種方法都將包括一個(gè)適當(dāng)?shù)暮贤?#xff0c;其中前置條件用@requires表示,后置條件用@ensures表示。

    此外,我們使用@ restores,@ updates,@ clears和@replaces等參數(shù)模式標(biāo)記每個(gè)參數(shù)。 當(dāng)然,這超出了本文的范圍。

    解決問題

    現(xiàn)在,我重申一下,我一開始不確定確切的答案。 顯然,第一個(gè)答案(即{1、5、6、2、6、7})是錯(cuò)誤的,因?yàn)樵黾踊A(chǔ)值不會(huì)為Set添加新值-或我認(rèn)為。 使用相同的邏輯,我還假設(shè)第三組(即{1,5,6})顯然是不正確的,因?yàn)槲覀冿@然是在改變基礎(chǔ)值。

    在這一點(diǎn)上,我相當(dāng)有信心第二個(gè)答案(即{2,6,7})是正確的,我的學(xué)生中有87%也是正確的。 當(dāng)然,我有答案鍵,因此我不得不挑戰(zhàn)自己以理解為什么正確答案實(shí)際上是最終答案(即“無法從提供的信息中分辨出來。”)。

    現(xiàn)在,根據(jù)本文的標(biāo)題,您可能已經(jīng)遙遙領(lǐng)先于我。 沒關(guān)系! 但是,我沒有立即得出這個(gè)結(jié)論。 相反,我退后一步,決定實(shí)際繪制Set 。

    當(dāng)然,當(dāng)您嘗試這樣做時(shí)會(huì)遇到幾個(gè)主要問題。 首先,正如我之前提到的, Set沒有順序。 結(jié)果,我們?nèi)绾瓮茢嗟陂g哪個(gè)元素優(yōu)先? 我們會(huì)嘗試所有可能的配置嗎?

    這些是我還沒有準(zhǔn)備好應(yīng)對的問題。 幸運(yùn)的是,事實(shí)證明,按外觀順序進(jìn)行迭代可以節(jié)省很多時(shí)間。 看一看:

    { 1 , 5 , 6 } // Initial state { 2 , 5 , 6 } // After incrementing the first element { 2 , 6 , 6 } // After incrementing the second element

    哦哦! 我們打破了第一條規(guī)則: Set不能包含重復(fù)項(xiàng)。 因此,我們無法確定結(jié)果Set將是什么樣。 我的最終答案是D:“無法從提供的信息中分辨出來。”

    不幸的是,這種解釋并不令我滿意。 就像,我知道Set不能包含重復(fù)項(xiàng),但是打破該規(guī)則的實(shí)際后果是什么? 換句話說,如果情況如此糟糕,我們?yōu)槭裁催€要授予用戶訪問基礎(chǔ)數(shù)據(jù)的權(quán)限?

    我認(rèn)為,用戶僅應(yīng)在刪除數(shù)據(jù)后才能訪問數(shù)據(jù)。 總的來說,我認(rèn)為圖書館在這方面做得很好。 如果Set沒有實(shí)現(xiàn)Iterable ,那么我們將Iterable 。

    Java迭代器簡介

    這給我?guī)砹艘粋€(gè)甚至更奇怪的問題:Java迭代器。 為了使此代碼起作用, Set必須實(shí)現(xiàn)Iterable,這意味著為基礎(chǔ)體系結(jié)構(gòu)定義一個(gè)Iterator。

    現(xiàn)在,如果您曾經(jīng)編寫自己的迭代器,那么您就需要執(zhí)行以下操作:

    new Iterator<T>() { @Override public boolean hasNext() { ... } @Override public T next() { ... } @Override public void remove() { ... } }

    這里,基本思想是我們定義某種可以充當(dāng)惰性數(shù)據(jù)結(jié)構(gòu)的結(jié)構(gòu)。 如果您熟悉其他語言(例如Python)的生成器表達(dá)式 ,則有相同的想法:我們創(chuàng)建了一個(gè)對象,該對象可以從一系列項(xiàng)目中一次返回一個(gè)項(xiàng)目。

    在實(shí)踐中, Iterator工作方式是繼續(xù)通過next()方法提供項(xiàng),直到?jīng)]有返回值為止( 這可能永遠(yuǎn)不會(huì)發(fā)生 )。 在有界序列中,我們知道何時(shí)停止,因?yàn)閔asNext()方法將返回false 。 這些方法一起可以作為循環(huán)機(jī)制的核心:

    while (iter.hasNext()) { T item = next(); }

    通過使一個(gè)類實(shí)現(xiàn)Iterable ,我們可以利用一些Java語法糖,稱為for-each循環(huán):

    for (T item: collection) { ... }

    Java迭代器警告

    在上面定義的問題中,我們能夠遍歷Set因?yàn)樗鼘?shí)現(xiàn)了Iterable 。

    當(dāng)然,僅因?yàn)槲覀兡軌虮闅v數(shù)據(jù)結(jié)構(gòu)并不意味著我們不會(huì)遇到任何問題。 畢竟, Iterator類具有一些自己的規(guī)則。 也許最重要的規(guī)則可以在remove()方法的描述中找到:

    從基礎(chǔ)集合中移除此迭代器返回的最后一個(gè)元素(可選操作)。 每次調(diào)用next()只能調(diào)用一次此方法。 如果在迭代進(jìn)行過程中以其他方式(而不是通過調(diào)用此方法)修改了基礎(chǔ)集合,則未指定迭代器的行為。

    Java 8文檔 (捕獲于04/23/2020)

    記住我曾說過修改NaturalNumber是不好的,因?yàn)樗赡軐?dǎo)致重復(fù)。 好吧,基于此定義,修改Set可能會(huì)導(dǎo)致無法預(yù)測的行為。

    當(dāng)然,這對我提出了一個(gè)問題: 修改基礎(chǔ)集合意味著什么。 對于Java集合,for-each循環(huán)不允許從集合中添加或刪除項(xiàng)目。 在這些情況下,我們可以期望看到ConcurrentModificationException ( docs )。

    現(xiàn)在,該錯(cuò)誤并不普遍。 畢竟, Iterator如何知道集合是否已被修改? 事實(shí)證明,該行為是自定義地烘焙到每個(gè)集合的next()方法中的。 例如,使用List集合, 當(dāng)列表的大小更改時(shí),拋出 ConcurrentModificationException 。 換句話說,每次調(diào)用next()都會(huì)檢查數(shù)據(jù)結(jié)構(gòu)的完整性。

    由于集合利用泛型類型,因此不可能考慮可能出現(xiàn)的所有不同類型的情況。 結(jié)果, next()無法檢測是否有任何數(shù)據(jù)在沒有跟蹤狀態(tài)的情況下發(fā)生了變異。 例如,檢查列表中是否有任何值更改可能需要存儲(chǔ)先前狀態(tài)的副本并定期檢查該先前狀態(tài)。 那不便宜!

    更糟糕的是,我們還沒有真正討論修改基礎(chǔ)數(shù)據(jù)對實(shí)際迭代過程可能產(chǎn)生的影響。 例如,如果next()以某種方式依賴于基礎(chǔ)數(shù)據(jù),則對其進(jìn)行更改顯然會(huì)更改接下來要執(zhí)行的操作。

    想象一下,我們有一個(gè)用于列表的Iterator ,其項(xiàng)必須實(shí)現(xiàn)Comparable 。 然后,我們以始終返回已排序順序的下一個(gè)值的方式制作此Iterator 。 如果然后要修改基礎(chǔ)值,則可以創(chuàng)建一個(gè)永遠(yuǎn)不會(huì)遍歷整個(gè)列表的循環(huán):

    [ 1 , 2 , 3 ] // next() returns 1 which we scale by 5 [ 5 , 2 , 3 ] // hasNext() claims there are no other values

    現(xiàn)在,這并不理想。 通常,您希望for-each循環(huán)實(shí)際上遍歷整個(gè)數(shù)據(jù)結(jié)構(gòu),而這根本沒有做到這一點(diǎn)。

    再談集合問題

    在這一點(diǎn)上,我們有機(jī)會(huì)從兩個(gè)不同的角度來討論Set問題:

  • 如果我們通過生成重復(fù)項(xiàng)來使Set無效,會(huì)發(fā)生什么情況?
  • 如果我們通過修改基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)使for-each循環(huán)無效,會(huì)發(fā)生什么?
  • 現(xiàn)在,我想借此機(jī)會(huì)談?wù)剤?zhí)行問題代碼片段時(shí)實(shí)際可能發(fā)生的情況:

    Set<NaturalNumber> nums = new SomeSetImplementation<>(); nums.add( new NaturalNumber2( 1 )); nums.add( new NaturalNumber2( 5 )); nums.add( new NaturalNumber2( 6 )); for (NaturalNumber n : nums) { n.increment(); }

    假設(shè)Set的Iterator沒有花哨的修改檢測,則大多數(shù)人期望的結(jié)果可能是相同的Set :{2,6,7}。

    另一個(gè)可能的結(jié)果是我們得到一個(gè)Set ,其中僅某些值遞增。 就像我之前說過的那樣, next()方法可能取決于基礎(chǔ)數(shù)據(jù)來決定接下來要做什么。

    在這種情況下,我們可能會(huì)得到增量輸出的任何組合:

    • {2,5,6}
    • {1,6,6}
    • {1,5,7}
    • {2,6,6}
    • {2,5,7}
    • {1,6,7}

    無論哪種情況,我們都不是完全安全的。 當(dāng)然, Set看起來一樣,但是真的一樣嗎?

    讓我們想象一下,該Set是使用哈希表實(shí)現(xiàn)的。 這提供了能夠快速檢查重復(fù)項(xiàng)的優(yōu)點(diǎn),但是需要更多的維護(hù)。 例如,如果要更改Set的值,則必須重新計(jì)算哈希并檢查沖突。

    當(dāng)我們直接修改NaturalNumber ,我們將跳過此維護(hù)階段。 結(jié)果,我們的哈希表仍將包含原始的三個(gè)哈希。 例如,當(dāng)有人檢查Set中是否包含兩個(gè)時(shí),該方法將錯(cuò)誤地返回false 。

    當(dāng)然,這是一個(gè)實(shí)現(xiàn)細(xì)節(jié)。 很可能根本沒有發(fā)現(xiàn)任何問題。 該程序繼續(xù)平穩(wěn)運(yùn)行,沒有人注意。 但是,與所有實(shí)現(xiàn)細(xì)節(jié)一樣,我們不能依賴于它們的假定行為。 換句話說,該程序仍然是不可預(yù)測的。

    除了未成年人, Set的Java實(shí)現(xiàn)實(shí)際上指出了這個(gè)確切的問題:

    注意:如果將可變對象用作集合元素,則必須格外小心。 如果對象的值更改為影響相等比較的方式,而該對象是集合中的元素,則不指定集合的??行為。 此禁止的一種特殊情況是,不允許集合將自身包含為元素。

    Java Set文檔 (查看04/24/2020)

    看起來很難組合一個(gè)不存在可變類型問題的Set實(shí)現(xiàn)。 我不知道那是關(guān)于可變類型的...

    什么是外賣?

    最后,我認(rèn)為Iterator文檔的編寫方式讓用戶玩的很好。 換句話說,當(dāng)它說:

    如果在迭代進(jìn)行過程中以其他方式(而不是通過調(diào)用此方法)修改了基礎(chǔ)集合,則未指定迭代器的行為。

    它的真正含義是“ 以任何方式” 。 當(dāng)然,我永遠(yuǎn)無法證實(shí)這些懷疑,所以我很想看看其他人怎么說。

    同時(shí),如果您喜歡這篇文章,那么如果您借此機(jī)會(huì)學(xué)習(xí)如何可以幫助該站點(diǎn)的發(fā)展 ,我將不勝感激。 在該文章中,您將了解我的郵件列表以及Patreon。

    否則,這是一些適合您的相關(guān)文章:

    • 余數(shù)運(yùn)算符在Java中用于Doubles
    • 復(fù)制可變數(shù)據(jù)類型時(shí)要小心

    否則,感謝您的堅(jiān)持。 希望我深夜的研究生學(xué)習(xí)對您有用!

    翻譯自: https://www.javacodegeeks.com/2020/04/be-careful-when-modifying-data-while-using-a-java-iterator.html

    總結(jié)

    以上是生活随笔為你收集整理的使用Java迭代器修改数据时要小心的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。