《Google Guava 官方教程》阅读笔记
一。使用和避免null
创建:
public void test1(){
String a = "5";
String b = null;
// 创建空的Optional对象,注意o5不是null
Optional<Object> o5 = Optional.absent();
System.out.println(o5.isPresent());
// 创建Optional,o2报NullPointerException异常,不接受null对象
Optional<String> o1 = Optional.of(a);
Optional<String> o2 = Optional.of(b);
// 创建Optional,允许null对象
Optional<String> o3 = Optional.fromNullable(a);
Optional<String> o4 = Optional.fromNullable(b);
}
2.常用方法
@org.junit.Test
public void test1() {
String a = "5";
Optional<String> o1 = Optional.of(a);
System.out.println(o1.isPresent());
System.out.println(o1.get());
System.out.println(o1.orNull());
System.out.println(o1.toJavaUtil());
}
输出:
true
5
5
Optional[5]
二。前置条件
静态导入,和普通的引包一样,语法是import static XXX.静态类名,使用静态导入后,再使用静态类下的方法就可以直接通过方法名调用了
Preconditions里面的方法:
1 .checkArgument(boolean) :
功能描述:检查boolean是否为真。 用作方法中检查参数
失败时抛出的异常类型: IllegalArgumentException
2.checkNotNull(T):
功能描述:检查value不为null, 直接返回value;
失败时抛出的异常类型:NullPointerException
3.checkState(boolean):
功能描述:检查对象的一些状态,不依赖方法参数。 例如, Iterator可以用来next是否在remove之前被调用。
失败时抛出的异常类型:IllegalStateException
4.checkElementIndex(int index, int size):
功能描述:检查index是否为在一个长度为size的list, string或array合法的范围。 index的范围区间是[0, size)(包含0不包含size)。无需直接传入list, string或array, 只需传入大小。返回index。
失败时抛出的异常类型:IndexOutOfBoundsException
5.checkPositionIndex(int index, int size):
功能描述:检查位置index是否为在一个长度为size的list, string或array合法的范围。 index的范围区间是[0, size)(包含0不包含size)。无需直接传入list, string或array, 只需传入大小。返回index。
失败时抛出的异常类型:IndexOutOfBoundsException
6.checkPositionIndexes(int start, int end, int size):
功能描述:检查[start, end)是一个长度为size的list, string或array合法的范围子集。伴随着错误信息。
失败时抛出的异常类型:IndexOutOfBoundsException
public class PreconditionsTest {
@Test
public void testPreconditions() throws Exception {
getPerson(8, "peida"); // 正确的情况
getPerson(-9, "peida"); // age不满足条件的情况
// getPerson(8, null);// name不满足条件的地方
}
/**
*
* @param age
* age应该满足age>0的条件
* @param neme
* name应该满足,name!=null的条件
* @throws Exception
* @return: void
*/
public static void getPerson(int age, String name) throws Exception {
// 对于age合法性的检查
Preconditions.checkArgument(age > 0, " age 应该满足 age > 0 的条件,age现在的数值为 ", age);
// 对于name的合法性检查
Preconditions.checkNotNull(name, "name 应该满足 name !=null 的条件,name现在的数值为", name);
}
}
方法声明(不包括额外参数) | 描述 | 检查失败时抛出的异常 |
checkArgument(boolean) | 检查 boolean 是否为 true,用来检查传递给方法的参数。 | IllegalArgumentException |
checkNotNull(T) | 检查 value 是否为 null,该方法直接返回 value,因此可以内嵌使用 checkNotNull。 | NullPointerException |
checkState(boolean) | 用来检查对象的某些状态。 | IllegalStateException |
checkElementIndex(int index, int size) | 检查 index 作为索引值对某个列表、字符串或数组是否有效。index>=0 && index<size * | IndexOutOfBoundsException |
checkPositionIndex(int index, int size) | 检查 index 作为位置值对某个列表、字符串或数组是否有效。index>=0 && index<=size * | IndexOutOfBoundsException |
checkPositionIndexes(int start, int end, int size) | 检查[start, end]表示的位置范围对某个列表、字符串或数组是否有效* | IndexOutOfBoundsException |
三。常见的object方法
equals:
当一个对象中的字段可以为 null 时,实现 Object.equals 方法会很痛苦,因为不得不分别对它们进行 null 检查。使用 Objects.equal 帮助你执行 null 敏感的 equals 判断,从而避免抛出 NullPointerException。例如:
Objects.equal("a", "a"); // returns true
Objects.equal(null, "a"); // returns false
Objects.equal("a", null); // returns false
Objects.equal(null, null); // returns true
注意:JDK7 引入的 Objects 类提供了一样的方法 Objects.equals。
hashCode
用对象的所有字段作散列[hash]运算应当更简单。Guava 的 Objects.hashCode(Object...)会对传入的字段序列计算出合理的、顺序敏感的散列值。你可以使用 Objects.hashCode(field1, field2, …, fieldn)来代替手动计算散列值。
注意:JDK7 引入的 Objects 类提供了一样的方法 Objects.hash(Object...)
toString
好的 toString 方法在调试时是无价之宝,但是编写 toString 方法有时候却很痛苦。使用 Objects.toStringHelper 可以轻松编写有用的 toString 方法。例如
// Returns "ClassName{x=1}"
Objects.toStringHelper(this).add("x", 1).toString();
// Returns "MyObject{x=1}"
Objects.toStringHelper("MyObject").add("x", 1).toString();
compare/compareTo
实现一个比较器[Comparator],或者直接实现 Comparable 接口有时也伤不起。考虑一下这种情况:
class Person implements Comparable<Person> {
private String lastName;
private String firstName;
private int zipCode;
public int compareTo(Person other) {
int cmp = lastName.compareTo(other.lastName);
if (cmp != 0) {
return cmp;
}
cmp = firstName.compareTo(other.firstName);
if (cmp != 0) {
return cmp;
}
return Integer.compare(zipCode, other.zipCode);
}
}
这部分代码太琐碎了,因此很容易搞乱,也很难调试。我们应该能把这种代码变得更优雅,为此,Guava 提供了 ComparisonChain。
ComparisonChain 执行一种懒比较:它执行比较操作直至发现非零的结果,在那之后的比较输入将被忽略。
public int compareTo(Foo that) {
return ComparisonChain.start()
.compare(this.aString, that.aString)
.compare(this.anInt, that.anInt)
.compare(this.anEnum, that.anEnum, Ordering.natural().nullsLast())
.result();
}
四。排序: Guava 强大的”流畅风格比较器”
创建排序器:常见的排序器可以由下面的静态方法创建
方法 | 描述 |
natural() | 对可排序类型做自然排序,如数字按大小,日期按先后排序 |
usingToString() | 按对象的字符串形式做字典排序[lexicographical ordering] |
from(Comparator) | 把给定的 Comparator 转化为排序器 |
@org.junit.Test
public void test1() {
User u1 = new User(1,1);
User u2 = new User(2,1);
Ordering<User> byLengthOrdering = new Ordering<User>() {
public int compare(User u1, User u2) {
return Ints.compare(u1.a, u2.a);
}
};
int compare = byLengthOrdering.compare(u1, u2);
System.out.println(compare);
}
链式调用方法:通过链式调用,可以由给定的排序器衍生出其它排序器
方法 | 描述 |
reverse() | 获取语义相反的排序器 |
nullsFirst() | 使用当前排序器,但额外把 null 值排到最前面。 |
nullsLast() | 使用当前排序器,但额外把 null 值排到最后面。 |
compound(Comparator) | 合成另一个比较器,以处理当前排序器中的相等情况。 |
lexicographical() | 基于处理类型 T 的排序器,返回该类型的可迭代对象 Iterable<T>的排序器。 |
onResultOf(Function) | 对集合中元素调用 Function,再按返回值用当前排序器排序。 |
Ordering<Foo> ordering = Ordering.natural().nullsFirst().onResultOf(new Function<Foo, String>() {
public String apply(Foo foo) {
return foo.sortedBy;
}
});
运用排序器:Guava 的排序器实现有若干操纵集合或元素值的方法
方法 | 描述 | 另请参见 |
greatestOf(Iterable iterable, int k) | 获取可迭代对象中最大的k个元素。 | leastOf |
isOrdered(Iterable) | 判断可迭代对象是否已按排序器排序:允许有排序值相等的元素。 | isStrictlyOrdered |
sortedCopy(Iterable) | 判断可迭代对象是否已严格按排序器排序:不允许排序值相等的元素。 | immutableSortedCopy |
min(E, E) | 返回两个参数中最小的那个。如果相等,则返回第一个参数。 | max(E, E) |
min(E, E, E, E...) | 返回多个参数中最小的那个。如果有超过一个参数都最小,则返回第一个最小的参数。 | max(E, E, E, E...) |
min(Iterable) | 返回迭代器中最小的元素。如果可迭代对象中没有元素,则抛出 NoSuchElementException。 | max(Iterable), min(Iterator), max(Iterator) |
Ordering<Foo> ordering = Ordering.natural().nullsFirst().onResultOf(sortKeyFunction)
不可变集合
public static final ImmutableSet<String> COLOR_NAMES = ImmutableSet.of(
"red",
"orange",
"yellow",
"green",
"blue",
"purple");
为什么要使用不可变集合
不可变对象有很多优点,包括:
- 当对象被不可信的库调用时,不可变形式是安全的;
- 不可变对象被多个线程调用时,不存在竞态条件问题
- 不可变集合不需要考虑变化,因此可以节省时间和空间。所有不可变的集合都比它们的可变形式有更好的内存利用率(分析和测试细节);
- 不可变对象因为有固定不变,可以作为常量来安全使用。
创建对象的不可变拷贝是一项很好的防御性编程技巧。Guava 为所有 JDK 标准集合类型和 Guava 新集合类型都提供了简单易用的不可变版本。
JDK 也提供了 Collections.unmodifiableXXX 方法把集合包装为不可变形式,但我们认为不够好:
- 笨重而且累赘:不能舒适地用在所有想做防御性拷贝的场景;
- 不安全:要保证没人通过原集合的引用进行修改,返回的集合才是事实上不可变的;
- 低效:包装过的集合仍然保有可变集合的开销,比如并发修改的检查、散列表的额外空间,等等。
不可变集合可以用如下多种方式创建:
- copyOf 方法,如 ImmutableSet.copyOf(set);
- of 方法,如 ImmutableSet.of(“a”, “b”, “c”)或 ImmutableMap.of(“a”, 1, “b”, 2);
- Builder 工具,如
public static final ImmutableSet<Color> GOOGLE_COLORS =
ImmutableSet.<Color>builder()
.addAll(WEBSAFE_COLORS)
.add(new Color(0, 191, 255))
.build();
@org.junit.Test
public void test1() {
List<Integer> integers = Arrays.asList(1, 2, 3);
ImmutableList<Integer> integers1 = ImmutableList.copyOf(integers);
System.out.println(integers1);
}
细节:关联可变集合和不可变集合
可变集合接口 | 属于JDK还是Guava | 不可变版本 |
Collection | JDK | ImmutableCollection |
List | JDK | ImmutableList |
Set | JDK | ImmutableSet |
SortedSet/NavigableSet | JDK | ImmutableSortedSet |
Map | JDK | ImmutableMap |
SortedMap | JDK | ImmutableSortedMap |
Multiset | Guava | ImmutableMultiset |
SortedMultiset | Guava | ImmutableSortedMultiset |
Multimap | Guava | ImmutableMultimap |
ListMultimap | Guava | ImmutableListMultimap |
SetMultimap | Guava | ImmutableSetMultimap |
BiMap | Guava | ImmutableBiMap |
ClassToInstanceMap | Guava | ImmutableClassToInstanceMap |
Table | Guava | ImmutableTable |
Guava 提供了一个新集合类型 Multiset,它可以多次添加相等的元素。维基百科从数学角度这样定义 Multiset:”集合[set]概念的延伸,它的元素可以重复出现…与集合[set]相同而与元组[tuple]相反的是,Multiset 元素的顺序是无关紧要的:Multiset {a, a, b}和{a, b, a}是相等的”。——译者注:这里所说的集合[set]是数学上的概念,Multiset继承自 JDK 中的 Collection 接口,而不是 Set 接口,所以包含重复元素并没有违反原有的接口契约。
@org.junit.Test
public void test1() {
String strWorld="a|b|c|d|e|a|a";
String[] words=strWorld.split("\\|");
List<String> wordList=new ArrayList<String>();
for (String word : words) {
wordList.add(word);
}
System.out.println(wordList.toString());
Multiset<String> wordsMultiset = HashMultiset.create();
wordsMultiset.addAll(wordList);
System.out.println(wordsMultiset.toString());
for(String key:wordsMultiset.elementSet()){
System.out.println(key+" count:"+wordsMultiset.count(key));
}
}
输出:
[a, b, c, d, e, a, a]
[a x 3, b, c, d, e]
a count:3
b count:1
c count:1
d count:1
e count:1
Multiset主要方法
Multiset接口定义的接口主要有:
add(E element) :向其中添加单个元素
add(E element,int occurrences) : 向其中添加指定个数的元素
count(Object element) : 返回给定参数元素的个数
remove(E element) : 移除一个元素,其count值 会响应减少
remove(E element,int occurrences): 移除相应个数的元素
elementSet() : 将不同的元素放入一个Set中
entrySet(): 类似与Map.entrySet 返回Set<Multiset.Entry>。包含的Entry支持使用getElement()和getCount()
setCount(E element ,int count): 设定某一个元素的重复次数
setCount(E element,int oldCount,int newCount): 将符合原有重复个数的元素修改为新的重复次数
retainAll(Collection c) : 保留出现在给定集合参数的所有的元素
removeAll(Collectionc) : 去除出现给给定集合参数的所有的元素
一篇详细的guava教程:http://www.bjpowernode.com/tutorial_guava/743.html
一篇很好的学习笔记:https://www.cnblogs.com/peida/tag/Guava%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix