Guava:好用java类库学习

原创
小哥 3年前 (2022-11-11) 阅读数 45 #大杂烩

基础功能

google guava中定义的String操作

在google guava它为字符串操作提供了极大的便利。判断字符串是空字符串还是null、用指定的字符填充字符串,以及拆分合并字符串、字符串匹配判断等。

  1. 使用com.google.common.base.Strings类的isNullOrEmpty(input)该方法确定字符串是否为空。

1 //Strings.isNullOrEmpty(input) demo 2 String input = ""; 3 boolean isNullOrEmpty = Strings.isNullOrEmpty(input); 4 System.out.println("input " + (isNullOrEmpty?"is":"is not") + " null or empty.");

  1. 获取两个具有相同前缀或后缀的字符串。

1 //Strings.commonPrefix(a,b) demo 2 String a = "com.jd.coo.Hello"; 3 String b = "com.jd.coo.Hi"; 4 String ourCommonPrefix = Strings.commonPrefix(a,b); 5 System.out.println("a,b common prefix is " + ourCommonPrefix); 6 7 //Strings.commonSuffix(a,b) demo 8 String c = "com.google.Hello"; 9 String d = "com.jd.Hello"; 10 String ourSuffix = Strings.commonSuffix(c,d); 11 System.out.println("c,d common suffix is " + ourSuffix);

  1. Strings的padStart和padEnd方法填充字符串。

1 int minLength = 4; 2 String padEndResult = Strings.padEnd("123", minLength, 0); 3 System.out.println("padEndResult is " + padEndResult); 4 5 String padStartResult = Strings.padStart("1", 2, 0); 6 System.out.println("padStartResult is " + padStartResult);

  1. 使用Splitter类以拆分字符串。

Splitter类可以根据正则表达式轻松拆分字符串,从拆分结果中删除空字符串trim操作,也可以做二次拆分。

让我们从一个基本的拆分示例开始:

    Iterable splitResults = Splitter.onPattern("[,,]{1,}")
            .trimResults()
            .omitEmptyStrings()
            .split("hello,word,,世界,级别");

    for (String item : splitResults) {
        System.out.println(item);
    }

Splitter的onPattern该方法作为正则表达式传入,后跟trimResults()该方法表明结果有待完成。trim,omitEmptyStrings()指示忽略空字符串,split该方法执行拆分操作。

split返回的结果是Iterable,我们可以使用for循环语句逐个打印拆分字符串的结果。

Splitter还有一个更强大的功能,做二次拆分,这里的二次拆分意味着拆分两次,例如,我们可以a=b;c=d这样的字符串被拆分为一个字符串。Map<String,String>。

1 String toSplitString = "a=b;c=d,e=f"; 2 Map<String,String> kvs = Splitter.onPattern("[,;]{1,}").withKeyValueSeparator(=).split(toSplitString); 3 for (Map.Entry<String,String> entry : kvs.entrySet()) { 4 System.out.println(String.format("%s=%s", entry.getKey(),entry.getValue())); 5 }

首先使用第二个分割。onPattern做第一次劈叉,然后通过。withKeyValueSeperator()方法进行第二次拆分。

  1. 如果存在拆分字符串,则必须存在合并字符串,guava为我们提供Joiner类合并字符串

让我们从一个简单的例子开始:

1 String joinResult = Joiner.on(" ").join(new String[]{"hello","world"}); 2 System.out.println(joinResult);

在面部示例中,我们使用Joiner.on(" ").join(xx)合并字符串。它简单有效。

Splitter该方法可以将字符串拆分两次,对应于Joiner你也可以反转操作,威尔。Map<String,String>进行合并。让我们看看下面的示例:

1 Map<String,String> map = new HashMap<String,String>(); 2 map.put("a", "b"); 3 map.put("c", "d"); 4 String mapJoinResult = Joiner.on(",").withKeyValueSeparator("=").join(map); 5 System.out.println(mapJoinResult);

使用withKeyValueSeparator方法可以应用于map进行合并。合并的结果是: a=b,c=d

guava该库还可以对字符串进行大小写转换(CaseFormat枚举),可以对字符串进行模式匹配。使用起来很方便。

guava对象操作封装

在开发中,经常需要比较两个对象是否相等。此时,我们需要考虑要比较的两个对象是否null,然后拨打equals方法比较是否相等,google guava库的com.google.common.base.Objects类提供了一个静态方法。equals我们可以避免对它是否为空做出自己的判断。示例如下:

1 Object a = null; 2 Object b = new Object(); 3 boolean aEqualsB = Objects.equal(a, b);

Objects.equals实现非常完美,代码如下:

1 public static boolean equal(@Nullable Object a, @Nullable Object b) { 2 return a == b || (a != null && a.equals(b)); 3 }

首先判断a b是否是同一个对象,如果是同一对象,则直接返回相等,如果不是同一对象则判断a不为null并且a.equals(b). 这考虑了性能和null空指针的问题。

另外Objects类中还为我们提供方便的重写toString()该方法的机制,让我们举个例子来理解它:

1 import com.google.common.base.Objects; 2 3 public class ObjectsDemo { 4 public static void main(String [] args) { 5 Student jim = new Student(); 6 jim.setId(1); 7 jim.setName("Jim"); 8 jim.setAge(13); 9 System.out.println(jim.toString()); 10 } 11 12 public static class Student { 13 private int id; 14 private String name; 15 private int age; 16 17 public int getId() { 18 return id; 19 } 20 public void setId(int id) { 21 this.id = id; 22 } 23 24 public String getName() { 25 return name; 26 } 27 public void setName(String name) { 28 this.name = name; 29 } 30 31 public int getAge() { 32 return age; 33 } 34 public void setAge(int age) { 35 this.age = age; 36 } 37 38 public String toString() { 39 return Objects.toStringHelper(this.getClass()) 40 .add("id", id) 41 .add("name", name) 42 .add("age", age) 43 .omitNullValues().toString(); 44 } 45 } 46 }

我们定义了Student类,它有三个属性,即id,name,age,我们改写了toString()方法,我们在其中使用Objects.toStringHelper方法,首先指定toString上课,然后轮流上课。add属性名称和属性值,您可以使用。omitNullValues()方法指定忽略并最终调用空值。toString()方法,你可以得到一个好的格式。toString实现了。

上述代码输出的结果是:

Student{id=1, name=Jim, age=13}

这种方式写起来很简单,可读性也很好,所以可以使用。Guava吧。

guava的Preconditions使用

guava的base包装中提供Preconditions类用于方便地检查参数。主要提供以下方法:

  1. checkArgument 接受一个boolean参数类型和可选errorMsg参数,此方法用于确定参数是否满足某些条件以及满足哪些条件。google guava别在意,当你不符合条件时就扔。IllegalArgumentException异常
  2. checkState 和checkArgument参数和实现基本相同,从字面上我们也可以知道,该方法用于确定状态是否正确,如果状态不正确,将抛出。IllegalStateException异常
  3. checkNotNull方法用于确定参数是否null,如果为null则会抛出NullPointerException空指针异常
  4. checkElementIndex该方法用于确定用户的传入数组下标或list索引位置,是否合法,如果非法,将被抛出。IndexOutOfBoundsException
  5. checkPositionIndexes方法的作用和checkElementIndex该方法类似,只是该方法的索引范围为。0到size包括size,而上述方法不包括size。

让我们看一个具体的使用示例:

1 import com.google.common.base.Preconditions; 2 3 public class PreconditionsDemo { 4 public static void main(String[] args) { 5 PreconditionsDemo demo = new PreconditionsDemo(); 6 demo.doSomething("Jim", 19, "hello world, hello java"); 7 } 8 9 public void doSomething(String name, int age, String desc) { 10 Preconditions.checkNotNull(name, "name may not be null"); 11 Preconditions.checkArgument(age >= 18 && age < 99, "age must in range (18,99)"); 12 Preconditions.checkArgument(desc !=null && desc.length() < 10, "desc too long, max length is ", 10); 13 14 //do things 15 } 16 }

在上面的示例中doSomething()该方法被调用了三次。Preconditions方法检查参数。

看似Preconditions实现很简单,他的意义在于为我们提供同一的参数校验,并对不同的异常情况抛出合适类型的异常,并对异常信息做格式化。

使用google guava的Optional避免空指针错误的接口

null这将带来许多问题,首先null起初,有无数的程序被植入null的手里,null意思不清楚,检查一下。null在大多数情况下,我们必须这样做,而在很多情况下我们忘记了正确的做法null为了检查,当我们的产品真正投入使用时,出现了异常的空指针,这是一种令人恶心的情况。

鉴于此google的guava图书馆提供Optional接口来使null快速故障,如可能null在对象上制作一层封装Optional静态方法of如果传入的参数为null就抛出NullPointerException异常。

让我们看一个实际的例子:

1 import com.google.common.base.Optional; 2 3 public class OptionalDemo { 4 public static void main(String[] args) { 5 Optional possibleNull = Optional.of(null); 6 possibleNull.get(); 7 } 8 public static class Student { } 9 }

上面的程序,我们使用Optional.of(null)方法,这一次程序将首次抛出空指针异常,这可以帮助我们尽快找到问题。

让我们看看另一个例子,我们使用。Optional.absent要初始化的方法posibleNull实例,然后我们get这个对象,看看会发生什么。

1 public class OptionalDemo { 2 public static void main(String[] args) { 3 Optional possibleNull = Optional.absent(); 4 Student jim = possibleNull.get(); 5 } 6 public static class Student { } 7 }

运行上述程序并发现:Exception in thread "main" java.lang.IllegalStateException: Optional.get() cannot be called on an absent value。

这种使用也会有异常Optional这有什么意义?

使用Optional除了赋予null语义上,增加了可读性,最大的优点是它是一种愚蠢的保护类型。 Optional迫使你积极思考没有引用的问题 因为您必须显式地从。Optional获取参考。直接使用null虽然很容易忘记某些情况FindBugs可以帮助查找null相关问题,但我们仍然认为它没有准确定位问题的根源。

与输入参数一样,方法的返回值也可以是null。和其他人一样,你绝对有可能忘记别人是怎么写的。method(a,b)将返回null就像当你意识到method(a,b)还可能忘记输入参数。a可以为null。指定方法的返回类型。Optional,还可以迫使调用方考虑返回的引用丢失的情况。

google guava Throwables帮助您抛出异常并处理它们。

guava类库中的Throwables提供了一些用于异常处理的静态方法,这些方法在功能上分为两类,一类是帮助您抛出异常,另一类是用于帮助您处理异常。

也许你会想:你为什么要帮助我们处理异常情况?我们自己不例外吗?

假设下面的方法是我们要调用的方法。

1 public void doSomething() throws Throwable { 2 //ignore method body 3 } 4 5 public void doSomethingElse() throws Exception { 6 //ignore method body 7 }

这两种方法的签名是一个。throws出了Throwable另外一个throws出了Exception,它们没有定义将抛出什么异常,也就是说,它们可以抛出任何异常。如果我们想调用这样的方法,我们需要对它们的异常进行一些处理。我们需要判断需要抛出何种异常以及需要封装何种异常。RuntimeException。这些东西是Throwables班级帮助我们做事。

假设我们要实现doIt方法,它将被调用。doSomething方法,而doIt定义中只允许抛掷SQLException,我们可以这样做:

1 public void doIt() throws SQLException { 2 try { 3 doSomething(); 4 } catch (Throwable throwable) { 5 Throwables.propagateIfInstanceOf(throwable, SQLException.class); 6 Throwables.propagateIfPossible(throwable); 7 } 8 }

请注意doIt的catch块,下一行代码表示如果异常类型为SQLException,然后引发此异常

Throwables.propagateIfInstanceOf(throwable, SQLException.class);

第二行指示如果异常Error类型,然后抛出此类型,否则将抛出它。RuntimeException,我们知道RuntimeException中不需要throws在中声明。

Throwables.propagateIfPossible(throwable);

Throwables类还为我们提供一些方便的异常处理帮助方法:

  1. 我们可以通过Throwables.getRooCause(Throwable)获取根异常
  2. 可以使用getCausalChain方法获取异常列表。
  3. 可以通过getStackTraceAsString获取异常堆栈的字符串。

集合增强

guava不可变集合

不可变集合的含义

不可变对象具有许多优点,包括:

  • 当对象被不受信任的库调用时,不可变形的形式是安全的;
  • 当多个线程调用不可变对象时,不存在竞争条件问题。
  • 不变集不需要考虑更改,因此节省了时间和空间。所有不可变集合都比它们的变量形式具有更好的内存利用率(分析和测试细节);
  • 不可变对象可以安全地用作常量,因为它们是固定的。

创建对象的不可变副本是一种很好的防御编程技术。Guava为所有JDK标准集类型和Guava新的集合类型都提供了易于使用的不可变版本。
JDK也提供了Collections.unmodifiableXXX该方法将集合包装为不可变形,但我们认为它不够好:

  • 沉重而笨重:在所有想要制作防御性副本的场景中都不太舒服;
  • 不安全:为了确保没有人通过引用修改原始集合,返回的集合实际上是不可变的;
  • 效率低下:打包的集合仍然保留了变量集合的开销,例如并发修改检查、哈希列表的额外空间等等。

如果您不需要修改集合,或者不希望集合保持不变,最好将其复制到不可变的集合中。

重要提示: 所有Guava不接受不可变集合的实现null价值我们对Google对内部代码库进行了详细研究,发现只有5%该案例需要在集合中被允许。null元素,剩余95%所有场景都会遇到null该值很快失效。如果您需要在不可变集合中使用。null,请使用JDK中的Collections.unmodifiableXXX方法有关更多详细建议,请参阅 “使用和避免null”

如何使用guava不可变集合

1. 如何创建不可变集合。

第一种方法使用builder创建:

1 public class ImmutableDemo { 2 public static void main(String[] args) { 3 Set immutableNamedColors = ImmutableSet.builder() 4 .add("red", "green","black","white","grey") 5 .build(); 6 //immutableNamedColors.add("abc"); 7 for (String color : immutableNamedColors) { 8 System.out.println(color); 9 } 10 } 11 }

第二种方法使用of静态方法创建:

        ImmutableSet.of("red","green","black","white","grey");

第三种方法使用copyOf静态方法创建:

        ImmutableSet.copyOf(new String[]{"red","green","black","white","grey"});

2. 使用asList()获取不可变集list视图

asList方法是在ImmutableCollection中定义,而所有不可变集合都会从ImmutableCollection继承,所以所有不可变集合都会有asList()方法返回当前不可变集合。list视图,此视图也是不变的。

3. 使用不可变集合

使用不可变集合和普通集合一样,只是不能使用他们的add,remove以及其他方法来修改集合。

guava集合之Multiset

Multiset看起来是Set,但本质上它不是Set,它不继承Set接口,它继承Collection界面,您可以转到Multiset添加重复元素,Multiset将对添加的元素进行计数。

它本质上是一个Set添加元素计数器。

1 import com.google.common.base.Splitter; 2 import com.google.common.collect.HashMultiset; 3 import com.google.common.collect.Multiset; 4 5 public class MultisetDemo { 6 public static void main(String[] args) { 7 Multiset multiset = HashMultiset.create(); 8 String sentences = "this is a story, there is a good girl in the story."; 9 Iterable words = Splitter.onPattern("[^a-z]{1,}").omitEmptyStrings().trimResults().split(sentences); 10 for (String word : words) { 11 multiset.add(word); 12 } 13 14 for (Object element : multiset.elementSet()) { 15 System.out.println((String)element + ":" + multiset.count(element)); 16 } 17 } 18 }

在上面的示例中,我们将一段文本逐个拆分为单词,然后依次放置。multiset在中,请注意本文中有多个重复单词,然后我们传递for循环遍历multiset并输出它们的计数。输出如下:

story:2 is:2 girl:1 there:1 a:2 good:1 the:1 in:1 this:1

显然,计数不是问题,Multiset还提供了add和remove重载方法的,可以在add或这remove同时指定计数的值。

常用实现 Multiset 接口的类包括:

  • HashMultiset: 元素存储在 HashMap
  • LinkedHashMultiset: 元素存储在 LinkedHashMap也就是说,元素的排列顺序取决于它们首先放置的顺序。
  • TreeMultiset: 元素排序并存储在 TreeMap
  • EnumMultiset: 元素必须为 enum 类型
  • ImmutableMultiset: 不可修改 Mutiset

看到这里你可能已经找到了 Guava Collections 都是以 create 或是 of 这样的静态方法构造对象。这是因为这些集合类中的大多数都有多个参数的私有构造函数,并且由于参数的数量太多,客户端代码程序员不方便使用它们。通过这种方式,您可以返回原始类型的子类型对象。此外,这种方法对于创建范例对象更为简洁。

google guava的BiMap:双向Map

我们知道Map是键-值对映射,这是键到值的映射,并且BiMap首先,它也是一种Map让他与众不同的是,他同时提供了键到值的映射和值到键的映射,因此它是双向的。Map.

想象这样一个场景,例如,我们需要在一周的时间里用中文和英文表示地图。Monday对应的中文意思是周一,对应的英文意思是周一Monday。这是一个极好的用途。BiMap的场景。

1 mport com.google.common.collect.BiMap; 2 import com.google.common.collect.HashBiMap; 3 4 public class BiMapDemo { 5 public static void main(String[] args) { 6 BiMap<String,String> weekNameMap = HashBiMap.create(); 7 weekNameMap.put("星期一","Monday"); 8 weekNameMap.put("星期二","Tuesday"); 9 weekNameMap.put("星期三","Wednesday"); 10 weekNameMap.put("星期四","Thursday"); 11 weekNameMap.put("星期五","Friday"); 12 weekNameMap.put("星期六","Saturday"); 13 weekNameMap.put("星期日","Sunday"); 14 15 System.out.println("星期天的英文名称是" + weekNameMap.get("星期日")); 16 System.out.println("Sunday的中文是" + weekNameMap.inverse().get("Sunday")); 17 } 18 }

BiMap值键对的Map可以通过inverse()获得方法。

BiMap 常见的实现有:

  1. HashBiMap: key 集合与 value 集合都有 HashMap 实现
  2. EnumBiMap: key 与 value 都必须是 enum 类型
  3. ImmutableBiMap: 不可修改 BiMap

google guava的Multimaps:一键多值Map

有时我们需要这样的数据类型。Map<String,Collection>,guava中的Multimap就是要解决这样的问题。

Multimap的实现

Multimap提供了一个丰富的实现,因此您可以使用它来替换程序。Map<K, Collection>,具体实现如下:

实现

Key实现

Value实现

ArrayListMultimap

HashMap

ArrayList

HashMultimap

HashMap

HashSet

LinkedListMultimap

LinkedHashMap

LinkedList

LinkedHashMultimap

LinkedHashMap

LinkedHashSet

TreeMultimap

TreeMap

TreeSet

ImmutableListMultimap

ImmutableMap

ImmutableList

ImmutableSetMultimap

ImmutableMap

ImmutableSet

让我们举个例子来理解Multimap如何使用:

1 public class MutliMapTest {
2 public static void main(String... args) {
3 Multimap<String, String> myMultimap = ArrayListMultimap.create();
4
5 // 添加键值对 6 myMultimap.put("Fruits", "Bannana"); 7 //给Fruits元素添加另一个元素 8 myMultimap.put("Fruits", "Apple");
9 myMultimap.put("Fruits", "Pear");
10 myMultimap.put("Vegetables", "Carrot");
11
12 // 获得multimap的size 13 int size = myMultimap.size();
14 System.out.println(size); // 4
15
16 // 获得Fruits所有对应值 17 Collection fruits = myMultimap.get("Fruits");
18 System.out.println(fruits); // [Bannana, Apple, Pear]
19
20 Collection vegetables = myMultimap.get("Vegetables");
21 System.out.println(vegetables); // [Carrot]
22
23 //遍历Mutlimap
24 for(String value : myMultimap.values()) {
25 System.out.println(value);
26 }
27
28 // Removing a single value
29 myMultimap.remove("Fruits","Pear");
30 System.out.println(myMultimap.get("Fruits")); // [Bannana, Pear]
31
32 // Remove all values for a key
33 myMultimap.removeAll("Fruits");
34 System.out.println(myMultimap.get("Fruits")); // [] (Empty Collection!)
35 }
36 }

google guava集合之Table

在guava该库还提供了二维表格结构:Table。使用Table可以实现二维矩阵的数据结构,其可以是薄滑动矩阵。

让我们看一个使用示例:

1 public class TableDemo { 2 public static void main(String[] args) { 3 Table<Integer, Integer, String> table = HashBasedTable.create(); 4 for (int row = 0; row < 10; row++) { 5 for (int column = 0; column < 5; column++) { 6 table.put(row, column, "value of cell (" + row + "," + column + ")"); 7 } 8 } 9 for (int row=0;row<table.rowMap().size();row++) { 10 Map<Integer,String> rowData = table.row(row); 11 for (int column =0;column < rowData.size(); column ++) { 12 System.out.println("cell(" + row + "," + column + ") value is:" + rowData.get(column)); 13 } 14 } 15 } 16 }

在上面的示例中,我们传递HashBasedTable已创建行类型。Integer,列类型也是Integer,值为String的Table。然后我们使用put方法向Table添加一些值,然后显示这些值。

Guava设置:使用Iterators简化Iterator操作

Iterators是Guava中对Iterator迭代器操作的帮助类,它提供了许多有用的简化方法。Iterator的操作。

  1. 确定迭代器中的所有元素是否满足特定条件。 all 方法

1 List list = Lists.newArrayList("Apple","Pear","Peach","Banana"); 2 3 Predicate condition = new Predicate() { 4 @Override 5 public boolean apply(String input) { 6 return ((String)input).startsWith("P"); 7 } 8 }; 9 boolean allIsStartsWithP = Iterators.all(list.iterator(), condition); 10 System.out.println("all result == " + allIsStartsWithP);

all该方法的第一个参数是Iterator,第二个参数是Predicate这个方法的意义是我们不需要自己编写。while循环,他的内部实现帮助我们制作了一个循环,在循环体中抽象了条件判断。

  1. 通过any确定迭代器中是否存在满足条件的记录,any方法参数和all同样的方法,没有更多具体的例子。

  2. get方法获取迭代器中的第一个。x个元素

    String secondElement = Iterators.get(list.iterator(), 1);

  3. filter方法筛选符合条件的项目

1 Iterator startPElements = Iterators.filter(list.iterator(), new Predicate() { 2 @Override 3 public boolean apply(String input) { 4 return input.startsWith("P"); 5 } 6 });

filter该该方法的第一个参数是源迭代器,第二个参数是。Predicate实施,其apply该方法返回当前元素是否合格。

  1. find方法返回满足条件的第一个元素。

1 String length5Element = Iterators.find(list.iterator(), new Predicate() { 2 @Override 3 public boolean apply(String input) { 4 return input.length() == 5; 5 } 6 });

  1. transform方法转换迭代器元素。

1 Iterator countIterator = Iterators.transform(list.iterator(), new Function<String, Integer>() { 2 @Override 3 public Integer apply(String input) { 4 return input.length(); 5 } 6 });

在上面的示例中,我们将字符串转换为其长度,transform该方法输出另一个Iterator.

如果你不迈出一小步,你可以到达数千英里。

版权声明

所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除

热门