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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

转变馆藏

發(fā)布時(shí)間:2023/12/3 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 转变馆藏 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

您是否曾經(jīng)想替換過HashSet或HashMap使用的equals和hashCode方法? 或者有一個(gè)List的一些元素類型偽裝成的List相關(guān)類型的?

轉(zhuǎn)換集合使這成為可能,并且本文將展示如何實(shí)現(xiàn)。

總覽

轉(zhuǎn)換集合是LibFX 0.3.0的一項(xiàng)功能,該功能將在今天每天發(fā)布。 這篇文章將介紹總體思路,涵蓋技術(shù)細(xì)節(jié),并提供一些可能會(huì)派上用場(chǎng)的用例。

正在進(jìn)行的示例是LibFX中包含的功能演示的稍微改編的變體。 請(qǐng)記住,這只是演示該概念的一個(gè)示例。

轉(zhuǎn)變館藏

轉(zhuǎn)換集合是另一個(gè)集合的視圖(例如,列表中的列表,地圖上的地圖等),其中似乎包含不同類型的元素(例如,整數(shù)而不是字符串)。

通過應(yīng)用轉(zhuǎn)換從內(nèi)部元素創(chuàng)建視圖元素。 這是按需發(fā)生的,因此轉(zhuǎn)換集合本身是無狀態(tài)的。 作為一個(gè)適當(dāng)?shù)囊晥D,內(nèi)部集合以及轉(zhuǎn)換視圖的所有更改都反映在另一個(gè)視圖中(例如Map及其entrySet )。

命名法

轉(zhuǎn)換的集合也可以視為裝飾器。 我將裝飾后的集合稱為內(nèi)部集合,并將其泛型稱為內(nèi)部類型。 轉(zhuǎn)換集合及其通用類型分別稱為外部集合和外部類型。

讓我們來看一個(gè)例子。 假設(shè)我們有一組字符串,但是我們知道這些字符串只包含自然數(shù)。 我們可以使用一個(gè)轉(zhuǎn)換集來獲取一個(gè)看起來像是整數(shù)集的視圖。

(類似// "[0, 1] ~ [0, 1]"的System.out.println(innerSet + " ~ " + transformingSet);是System.out.println(innerSet + " ~ " + transformingSet);的控制臺(tái)輸出。)

Set<String> innerSet = new HashSet<>(); Set<Integer> transformingSet = new TransformingSet<>(innerSet,/* skipping some details */); // both sets are initially empty: "[] ~ []"// now let's add some elements to the inner set innerSet.add("0"); innerSet.add("1"); innerSet.add("2"); // these elements can be found in the view: "[0, 1, 2] ~ [0, 1, 2]"// modifying the view reflects on the inner set transformingSet.remove(1); // again, the mutation is visible in both sets: "[0, 2] ~ [0, 2]"

看看轉(zhuǎn)換有多愉快?

發(fā)布時(shí)間由Rooners玩具攝影下的CC-BY-NC-ND 2.0 。

細(xì)節(jié)

像往常一樣,魔鬼在細(xì)節(jié)中,所以讓我們討論這個(gè)抽象的重要部分。

轉(zhuǎn)寄

轉(zhuǎn)換集合是另一個(gè)集合的視圖。 這意味著它們本身不保存任何元素,而是將所有調(diào)用轉(zhuǎn)發(fā)給內(nèi)部/裝飾的集合。

他們通過將調(diào)用參數(shù)從外部類型轉(zhuǎn)換為內(nèi)部類型并使用這些參數(shù)調(diào)用內(nèi)部集合來實(shí)現(xiàn)此目的。 然后,將返回值從內(nèi)部類型轉(zhuǎn)換為外部類型。 對(duì)于以集合為參數(shù)的調(diào)用,這變得有些復(fù)雜,但是方法基本上是相同的。

所有轉(zhuǎn)換集合的實(shí)現(xiàn)方式都是將方法的每次調(diào)用轉(zhuǎn)發(fā)到內(nèi)部集合上的相同方法 (包括default方法 )。 這意味著內(nèi)部集合對(duì)線程安全性,原子性等的任何保證也將由轉(zhuǎn)換集合維護(hù)。

轉(zhuǎn)型

轉(zhuǎn)換是通過在構(gòu)造過程中指定的一對(duì)函數(shù)來計(jì)算的。 一個(gè)用于將外部元素轉(zhuǎn)換為內(nèi)部元素,另一個(gè)用于另一個(gè)方向。 (對(duì)于映射,存在兩對(duì)這樣的對(duì):一對(duì)用于鍵,一對(duì)用于值。)

轉(zhuǎn)換函數(shù)關(guān)于equals必須彼此相反,即, outer.equals(toOuter(toInner(outer))和inner.equals(toInner(toOuter(inner))對(duì)于所有外部元素和內(nèi)部元素必須為true。并非如此,這些集合的行為可能無法預(yù)測(cè)。

對(duì)于身份而言,情況并非如此,即, outer == toOuter(toInner(outer))可能為false。 詳細(xì)信息取決于所應(yīng)用的轉(zhuǎn)換,并且通常未指定-它可能永遠(yuǎn)不會(huì),有時(shí)或永遠(yuǎn)都是正確的。

讓我們看看轉(zhuǎn)換函數(shù)如何查找我們的字符串和整數(shù)集:

private Integer stringToInteger(String string) {return Integer.parseInt(string); }private String integerToString(Integer integer) {return integer.toString(); }

這就是我們使用它們創(chuàng)建轉(zhuǎn)換集的方式:

Set<Integer> transformingSet = new TransformingSet<>(innerSet,this::stringToInteger, this::integerToString,/* still skipping some details */);

直截了當(dāng)吧?

是的,但是即使這個(gè)簡(jiǎn)單的示例也包含陷阱。 注意前導(dǎo)零的字符串如何映射到相同的整數(shù)。 這可以用于創(chuàng)建不良行為:

innerSet.add("010"); innerSet.add("10"); // now the transforming sets contains the same entry twice: // "[010, 10] ~ [10, 10]"// sizes of different sets: System.out.println(innerSet.size()); // "2" System.out.println(transformingSet.size()); // "2" System.out.println(new HashSet<>(transformingSet).size()); // "1" !// removing is also problematic transformingSet.remove(10) // the call returns true // one of the elements could be removed: "[010] ~ [10]" transformingSet.remove(10) // the call returns false // indeed, nothing changed: "[010] ~ [10]"// now things are crazy - this returns false: transformingSet.contains(transformingSet.iterator().next()) // the transforming set does not contain its own elements ~> WAT?

因此,在使用轉(zhuǎn)換集合時(shí),仔細(xì)考慮轉(zhuǎn)換非常重要。 它們必須彼此相反!

但這僅限于實(shí)際發(fā)生的內(nèi)部和外部元素就足夠了。 在該示例中,問題僅在引入前導(dǎo)零的字符串時(shí)才開始。 如果這些被某些業(yè)務(wù)規(guī)則所禁止,并且已經(jīng)正確執(zhí)行,那么一切都會(huì)好起來的。

類型安全

以通常的靜態(tài),編譯時(shí)方式,對(duì)轉(zhuǎn)換集合進(jìn)行的所有操作都是類型安全的。 但是,由于收集接口中的許多方法都允許對(duì)象(例如Collection.contains(Object) )或未知通用類型的集合(例如Collection.addAll(Collection<?>) )作為參數(shù),因此這并不涵蓋所有可能發(fā)生在以下情況的情況運(yùn)行。

請(qǐng)注意,這些調(diào)用的參數(shù)必須從外部類型轉(zhuǎn)換為內(nèi)部類型,才能將調(diào)用轉(zhuǎn)發(fā)到內(nèi)部集合。 如果使用非外部類型的實(shí)例調(diào)用它們,則很可能無法將其傳遞給轉(zhuǎn)換函數(shù)。 在這種情況下,該方法可能會(huì)拋出ClassCastException 。 盡管這與方法的合同一致,但可能仍然是意外的。

為了減少這種風(fēng)險(xiǎn),轉(zhuǎn)換集合的構(gòu)造函數(shù)需要使用內(nèi)部和外部類型的令牌。 它們用于檢查元素是否為必需類型,如果不是,則可以毫無例外地優(yōu)雅地回答查詢。

我們終于可以確切地看到如何創(chuàng)建轉(zhuǎn)換集:

Set<Integer> transformingSet = new TransformingSet<>(innerSet,String.class, this::stringToInteger,Integer.class, this::integerToString);

構(gòu)造函數(shù)實(shí)際上接受Class<? super I> Class<? super I>所以這也將編譯:

Set<Integer> transformingSetWithoutTokens = new TransformingSet<>(innerSet,Object.class, this::stringToInteger,Object.class, this::integerToString);

但是由于所有內(nèi)容都是對(duì)象,所以針對(duì)令牌的類型檢查變得無用,并且調(diào)用轉(zhuǎn)換函數(shù)可能會(huì)導(dǎo)致異常:

Object o = new Object(); innerSet.contains(o); // false transformingSet.contains(o); // false transformingSetWithoutTokens.contains(o); // exception

用例

我想說,轉(zhuǎn)換集合是一種非常專業(yè)的工具,不太可能經(jīng)常使用,但在每個(gè)分類良好的工具箱中仍然占有一席之地。

重要的是要注意,如果性能至關(guān)重要,則可能會(huì)出現(xiàn)問題。 每次調(diào)用包含或返回元素的轉(zhuǎn)換集合,都會(huì)導(dǎo)致至少創(chuàng)建一個(gè)(通常是多個(gè))對(duì)象。 這些對(duì)垃圾收集器施加了壓力,并導(dǎo)致通往有效負(fù)載的方式的間接級(jí)別更高。 (與以往一樣,在討論性能時(shí):首先要介紹!)

那么轉(zhuǎn)換集合的用例是什么? 上面我們已經(jīng)看到了如何將集合的元素類型更改為另一種。 盡管這代表了總體思路,但我認(rèn)為這不是一個(gè)非常普遍的用例(盡管在某些邊緣情況下是有效的方法)。

在這里,我將展示兩個(gè)更狹窄的解決方案,您可能希望在某些時(shí)候使用它們。 但是我也希望這能使您了解如何使用轉(zhuǎn)換集合來解決棘手的情況。 也許您的問題的解決方案在于巧妙地應(yīng)用此概念。

用Equals和HashCode代替

我一直很喜歡.NET的哈希圖(他們稱其為字典)如何具有將EqualityComparer作為參數(shù)的構(gòu)造函數(shù) 。 通常將在鍵上調(diào)用的所有對(duì)equals和hashCode調(diào)用都委派給該實(shí)例。 因此有可能即時(shí)替換有問題的實(shí)現(xiàn)。

當(dāng)您處理無法完全控制的有問題的舊版代碼或庫代碼時(shí),這可以節(jié)省生命。 當(dāng)需要一些特殊的比較機(jī)制時(shí),它也很有用。

使用轉(zhuǎn)換集合,這很容易。 為了使它更加容易,LibFX已經(jīng)包含一個(gè)EqualityTransformingSet和EqualityTransformingMap 。 它們修飾另一個(gè)集合或映射實(shí)現(xiàn),并且在構(gòu)造過程中可以提供鍵和元素的equals和hashCode函數(shù)。

假設(shè)您想將字符串用作set元素,但為了進(jìn)行比較,您僅對(duì)它們的長(zhǎng)度感興趣。

Set<String> lengthSet = EqualityTransformingSet.withElementType(String.class).withInnerSet(new HashSet<Object>()).withEquals((a, b) -> a.length != b.length).withHash(String::length).build();lengthSet.add("a"); lengthSet.add("b"); System.out.println(lengthSet); // "[a]"

從集合中刪除可選性

也許您正在與一個(gè)在各處使用Optional的想法的人一起工作,然后瘋狂地使用它,現(xiàn)在您有了Set<Optional<String>> 。 如果無法修改代碼(或您的同事),則可以使用轉(zhuǎn)換集合來獲取一個(gè)對(duì)您隱藏Optional的視圖。

同樣,實(shí)現(xiàn)起來很簡(jiǎn)單,因此LibFX已經(jīng)以O(shè)ptionalTransforming[Collection|List|Set]的形式包含了它。

Set<Optional<String>> innerSet = new HashSet<>(); Set<String> transformingSet =new OptionalTransformingSet<String>(innerSet, String.class);innerSet.add(Optional.empty()); innerSet.add(Optional.of("A"));// "[Optional.empty, Optional[A]] ~ [null, A]"

請(qǐng)注意, null表示空的optional的方式。 這是默認(rèn)行為,但是您也可以將另一個(gè)字符串指定為空的Optionals的值:

Set<String> transformingSet =new OptionalTransformingSet<String>(innerSet, String.class, "DEFAULT");// ... code as above ... // "[Optional.empty, Optional[A]] ~ [DEFAULT, A]"

這樣可以避免使用Optional和null作為元素,但是現(xiàn)在您必須確保永遠(yuǎn)不會(huì)有包含DEFAULT的Optional。 (如果確實(shí)如此,則隱式轉(zhuǎn)換不是彼此相反的,我們已經(jīng)在上面看到了這些轉(zhuǎn)換會(huì)引起問題。)

有關(guān)此示例的更多詳細(xì)信息,請(qǐng)查看演示 。

反射

我們已經(jīng)介紹過,轉(zhuǎn)換集合是另一個(gè)集合的視圖。 使用類型標(biāo)記(以最大程度地減少ClassCastExceptions )和一對(duì)轉(zhuǎn)換函數(shù)(它們必須彼此相反),每個(gè)調(diào)用都將轉(zhuǎn)發(fā)到經(jīng)過修飾的集合。 轉(zhuǎn)換后的集合可以維護(hù)修飾后的集合所做的關(guān)于線程安全性,原子性的所有保證。

然后,我們看到了轉(zhuǎn)換集合的兩個(gè)特定用例:替換等于和哈希數(shù)據(jù)結(jié)構(gòu)使用的哈希碼,以及從Collection<Optional<E>>刪除可選性。

談?wù)凩ibFX

就像我說的那樣,轉(zhuǎn)換集合是我的開源項(xiàng)目LibFX的一部分。 如果您考慮使用它,我想指出一些事情:

  • 這篇文章介紹了這個(gè)想法和一些細(xì)節(jié),但是并不能代替文檔。 查閱Wiki,獲取最新描述和指向Javadoc的指針。
  • 我認(rèn)真對(duì)待測(cè)試。 多虧了Guava ,約6.500個(gè)單元測(cè)試涵蓋了轉(zhuǎn)換集合。
  • LibFX是根據(jù)GPL許可的。 如果那不適合您的許可模式,請(qǐng)隨時(shí)與我聯(lián)系。

翻譯自: https://www.javacodegeeks.com/2015/05/transforming-collections.html

總結(jié)

以上是生活随笔為你收集整理的转变馆藏的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

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