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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

转变馆藏

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

您是否曾經想替換過HashSet或HashMap使用的equals和hashCode方法? 或者有一個List的一些元素類型偽裝成的List相關類型的?

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

總覽

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

正在進行的示例是LibFX中包含的功能演示的稍微改編的變體。 請記住,這只是演示該概念的一個示例。

轉變館藏

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

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

命名法

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

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

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

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]"

看看轉換有多愉快?

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

細節(jié)

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

轉寄

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

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

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

轉型

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

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

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

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

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

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

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

直截了當吧?

是的,但是即使這個簡單的示例也包含陷阱。 注意前導零的字符串如何映射到相同的整數(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?

因此,在使用轉換集合時,仔細考慮轉換非常重要。 它們必須彼此相反!

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

類型安全

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

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

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

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

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

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

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

但是由于所有內容都是對象,所以針對令牌的類型檢查變得無用,并且調用轉換函數(shù)可能會導致異常:

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

用例

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

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

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

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

用Equals和HashCode代替

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

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

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

假設您想將字符串用作set元素,但為了進行比較,您僅對它們的長度感興趣。

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]"

從集合中刪除可選性

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

同樣,實現(xiàn)起來很簡單,因此LibFX已經以OptionalTransforming[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]"

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

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

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

有關此示例的更多詳細信息,請查看演示 。

反射

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

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

談談LibFX

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

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

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

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。