由于這是一項艱巨而簡單的任務,所以我很樂意交付。
WTF是番石榴嗎?
這是一組非常簡單的基本類,您最終還是要自己編寫。 僅由Google來思考Apache的共同點。 只是為了讓您的生活更輕松一點。
Wiktor Gworek在Javarsowia 2010上做了一個早期(v04) 演講 ,另外一個是波蘭語演講 。
在撰寫本文時,最新版本是v07,已經過Mavenized,可以在公共Maven repo上找到 。
這是一些有趣的事情的快速回顧。 不過,不要期望任何花哨的東西,Guava非常基礎。
@VisibleForTesting
一個簡單的注釋,告訴您為什么放寬了特定的屬性訪問限制。
在測試中使用的一個常見技巧是放寬對特定屬性的默認訪問限制,以便您可以在單元測試中使用它,該單元測試位于相同的程序包中(盡管在不同的目錄中)。 無論是好是壞,請記住給開發人員一個提示。
考慮:
public class User {private Long id;private String firstName;private String lastName;String login;
為什么登錄程序包有作用域?
public class User {private Long id;private String firstName;private String lastName;@VisibleForTesting String login;
啊,那是為什么。
前提條件
Guava有一些防御性編程的先決條件(按合同設計),但不如Apache Commons / Spring框架具備的條件好。 有趣的一件事是Guava解決方案返回了對象,因此可以進行內聯。 考慮:
使用手寫的前提條件:
public User(Long id, String firstName, String lastName, String login) {validateParameters(id, firstName, lastName, login);this.id = id;this.firstName = firstName;this.lastName = lastName;this.login = login.toLowerCase();}private void validateParameters(Long id, String firstName, String lastName, String login) {if(id == null ) {throw new IllegalArgumentException('id cannot be null');}if(firstName == null || firstName.length() == 0) {throw new IllegalArgumentException('firstName cannot be empty');}if(lastName == null || lastName.length() == 0) {throw new IllegalArgumentException('lastName cannot be empty');}if(login == null || login.length() == 0) {throw new IllegalArgumentException('login cannot be empty');}}
使用番石榴先決條件:
public void fullyImplementedGuavaConstructorWouldBe(Long id, String firstName, String lastName, String login) {this.id = checkNotNull(id);this.firstName = checkNotNull(firstName);this.lastName = checkNotNull(lastName);this.login = checkNotNull(login);checkArgument(firstName.length() > 0);checkArgument(lastName.length() > 0);checkArgument(login.length() > 0);}
(感謝Yom注意到checkNotNull必須在checkArgument之前進行,盡管這有點不直觀)
使用spring或apache commons前提(兩個庫的用法看起來完全一樣):
public void springConstructorWouldBe(Long id, String firstName, String lastName, String login) {notNull(id); hasText(firstName); hasText(lastName); hasText(login);this.id = id;this.firstName = firstName;this.lastName = lastName;this.login = login;}
CharMatcher
對于討厭regexp或只想要簡單美觀的對象樣式模式匹配解決方案的人。
例子:
和/或易用性
String input = 'This invoice has an id of 192/10/10';CharMatcher charMatcher = CharMatcher.DIGIT.or(CharMatcher.is('/'));String output = charMatcher.retainFrom(input);
輸出是:192/10/10
否定:
String input = 'DO NOT scream at me!';CharMatcher charMatcher = CharMatcher.JAVA_LOWER_CASE.or(CharMatcher.WHITESPACE).negate();String output = charMatcher.retainFrom(input);
輸出是:DONOT!
范圍:
String input = 'DO NOT scream at me!';CharMatcher charMatcher = CharMatcher.inRange('m', 's').or(CharMatcher.is('a').or(CharMatcher.WHITESPACE));String output = charMatcher.retainFrom(input);
輸出是:sram am
細木工/分離器
顧名思義,它是正確完成字符串連接/拆分的方法,盡管我發現調用有點兒反轉了……哦,那是java。
String[] fantasyGenres = {'Space Opera', 'Horror', 'Magic realism', 'Religion'};String joined = Joiner.on(', ').join(fantasyGenres);
輸出:太空歌劇,恐怖片,魔幻現實主義,宗教
您可以跳過空值:
String[] fantasyGenres = {'Space Opera', null, 'Horror', 'Magic realism', null, 'Religion'};String joined = Joiner.on(', ').skipNulls().join(fantasyGenres);
輸出:太空歌劇,恐怖片,魔幻現實主義,宗教
您可以填寫空值:
String[] fantasyGenres = {'Space Opera', null, 'Horror', 'Magic realism', null, 'Religion'};String joined = Joiner.on(', ').useForNull('NULL!!!').join(fantasyGenres);
輸出:太空歌劇,NULL !!!,恐怖,魔術現實主義,NULL !!!,宗教
您可以加入地圖
Map<Integer, String> map = newHashMap();map.put(1, 'Space Opera');map.put(2, 'Horror');map.put(3, 'Magic realism');String joined = Joiner.on(', ').withKeyValueSeparator(' -> ').join(map);
輸出:1? 太空歌劇2 恐怖3? 魔幻現實主義
Split返回Iterable而不是JDK數組:
String input = 'Some very stupid data with ids of invoces like 121432, 3436534 and 8989898 inside';Iterable<String> splitted = Splitter.on(' ').split(input);
盡管您不能為每個“列”指定不同的長度,但Split會進行固定長度的拆分,這使得在解析某些導出效果不佳的Excel時,它的使用受到了限制。
String input ='A 1 1 1 1\n' +'B 1 2 2 2\n' +'C 1 2 3 3\n' +'D 1 2 5 3\n' +'E 3 2 5 4\n' +'F 3 3 7 5\n' +'G 3 3 7 5\n' +'H 3 3 9 7';Iterable<String> splitted = Splitter.fixedLength(3).trimResults().split(input);
您可以在拆分時使用CharMatcher
String input = 'Some very stupid data with ids of invoces like 123231/fv/10/2010, 123231/fv/10/2010 and 123231/fv/10/2010';Iterable<String> splitted = Splitter.on(CharMatcher.DIGIT.negate()).trimResults().omitEmptyStrings().split(input);
謂詞/函數
謂詞本身并不多,它只是一個帶有返回true的方法的接口,但是如果將謂詞與函數和Collections2(一個簡化了集合處理的番石榴類)結合使用,則可以在工具箱中找到一個不錯的工具。
但是,讓我們從基本謂詞使用開始。 假設我們要查找是否有用數字登錄的用戶。 接種將是(返回布爾值):
Predicates.in(users).apply(shouldNotHaveDigitsInLoginPredicate);
謂詞看起來像那樣
public class ShouldNotHaveDigitsInLoginPredicate implements Predicate<User> {@Overridepublic boolean apply(User user) {checkNotNull(user);return CharMatcher.DIGIT.retainFrom(user.login).length() == 0;}
}
現在讓我們添加一個函數,該函數會將用戶轉換為他的全名:
public class FullNameFunction implements Function<User, String> {@Overridepublic String apply(User user) {checkNotNull(user);return user.getFirstName() + ' ' + user.getLastName();}
}
您可以使用靜態方法轉換調用它:
List<User> users = newArrayList(new User(1L, 'sylwek', 'stall', 'rambo'),new User(2L, 'arnold', 'schwartz', 'commando'));List<String> fullNames = transform(users, new FullNameFunction());
現在,讓謂詞與函數結合使用,以打印登錄名不包含數字的用戶名:
List<User> users = newArrayList(new User(1L, 'sylwek', 'stall', 'rambo'), new User(2L, 'arnold', 'schwartz', 'commando'), new User(3L, 'hans', 'kloss', 'jw23'));Collection<User> usersWithoutDigitsInLogin = filter(users, new ShouldNotHaveDigitsInLoginPredicate());
String names = Joiner.on('\n').join( transform(usersWithoutDigitsInLogin, new FullNameFunction()) );
我們沒有得到的是: 折疊(減少)和元組 。 哦,好吧,如果您想使用Java中的函數 ,您可能還是會轉向Java Functional Library ,對吧?
案例格式
是否曾經想過用一個襯里將這些丑陋的PHP Pear名稱轉換為漂亮的java / cpp樣式? 沒有? 好吧,無論如何,您可以:
String pearPhpName = 'Really_Fucked_Up_PHP_PearConvention_That_Looks_UGLY_because_of_no_NAMESPACES';
String javaAndCPPName = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL , pearPhpName);
輸出:ReallyFuckedUpPhpPearconventionThatLooksUglyBecauseOfNoNamespaces
但是,由于Oracle接管了Sun,您實際上可能希望將其轉換為sql風格,對嗎?
String sqlName = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, javaAndCPPName);
輸出:real_fucked_up_php_pearconvention_that_looks_ugly_because_of_no_namespaces
館藏
番石榴具有Google集合庫1.0的超集,這確實是將這種依賴關系包含在pom中的一個很好的理由。 我什至不會嘗試描述所有功能,而只是指出一些不錯的東西:
- 您幾乎擁有所有東西的不可變版本
- 您可以在常見類型(如列表,集合,地圖,對象數組)上獲得一些不錯的靜態和靜態類型化方法,這些方法包括:
- 基于返回類型創建的簡單方法:例如newArrayList
- 轉換(應用返回不可變版本的函數的方式)
- 分區(分頁)
- 逆轉
現在還有更多有趣的收藏。
多圖
Mutlimap基本上是一個映射,單個鍵可以具有許多值。 是否曾經在您的代碼中創建Map <T1,Set <T2 >>? 您不再需要了。
Multimap<Integer, String> multimap = HashMultimap.create();multimap.put(1, 'a');multimap.put(2, 'b');multimap.put(3, 'c');multimap.put(1, 'a2');
當然也有不可變的實現:ImmutableListMultimap,ImmutableSetMultomap等。
您可以在線(最多5個元素)或使用構建器構造不可變項:
Multimap<Integer, String> multimap = ImmutableSetMultimap.of(1, 'a', 2, 'b', 3, 'c', 1, 'a2');
Multimap<Integer, String> multimap = new ImmutableSetMultimap.Builder<Integer, String>().put(1, 'a').put(2, 'b').put(3, 'c').put(1, 'a2').build();
雙圖
BiMap是僅具有唯一值的地圖。 考慮一下:
@Test(expected = IllegalArgumentException.class)
public void biMapShouldOnlyHaveUniqueValues() {BiMap<Integer, String> biMap = HashBiMap.create();biMap.put(1, 'a');biMap.put(2, 'b');biMap.put(3, 'a'); //argh! an exception
}
這使您可以反轉地圖,因此值成為關鍵,反之亦然:
BiMap<Integer, String> biMap = HashBiMap.create();
biMap.put(1, 'a');
biMap.put(2, 'b');
biMap.put(3, 'c');BiMap<String, Integer> invertedMap = biMap.inverse();
不知道我實際上想用它做什么。
約束條件
這使您可以在集合上添加約束檢查,以便僅添加通過約束的值。
想象一下,我們想要一個在他們的登錄名中帶有首字母“ r”的用戶集合。
Constraint<User> loginMustStartWithR = new Constraint<User>() {@Overridepublic User checkElement(User user) {checkNotNull(user);if(!user.login.startsWith('r')) {throw new IllegalArgumentException('GTFO, you are not Rrrrrrrrr');}return user;}
};
現在進行測試:
@Test(expected = IllegalArgumentException.class)
public void shouldConstraintCollection() {//givenCollection<User> users = newArrayList(new User(1L, 'john', 'rambo', 'rambo'));Collection<User> usersThatStartWithR = constrainedCollection(users, loginMustStartWithR);//whenusersThatStartWithR.add(new User(2L, 'arnold', 'schwarz', 'commando'));
}
您還可以立即獲得notNull約束:
//notice it's not an IllegalArgumentException :(
@Test(expected = NullPointerException.class)
public void notNullConstraintShouldWork() {//givenCollection<Integer> users = newArrayList(1);Collection<Integer> notNullCollection = constrainedCollection(users, notNull());//whennotNullCollection.add(null);
}
需要記住的事情:約束條件不是檢查集合中已經存在的數據。
桌子
正如預期的那樣,表是具有列,行和值的集合。 我猜沒有更多的Map <T1,Map <T2,T3 >>。 用法很簡單,您可以轉置:
Table<Integer, String, String> table = HashBasedTable.create();
table.put(1, 'a', '1a');
table.put(1, 'b', '1b');
table.put(2, 'a', '2a');
table.put(2, 'b', '2b');Table transponedTable = Tables.transpose(table);
就是這樣,伙計們。 我沒有介紹util.concurrent,原語,io和net軟件包,但您可能已經知道會發生什么。
祝您編程愉快,別忘了分享!
參考: Solid Craft博客上來自JCG合作伙伴 Jakub Nabrdalik的Google Guava v07示例 。
翻譯自: https://www.javacodegeeks.com/2012/10/google-guava-v07-examples.html