Guava-Save junk code, write elegant and efficient, and increase efficiency by N times

When looking at the code of a classmate recently, I found that the code uses a lot of the content of Google's open source Guava core library, which makes the code a lot simpler and clearer, so I learned to share Guava's most useful functions.

The Guava project is Google’s open source Java core library. It mainly contains some frequently used functions in Java development, such as  data verification  ,  immutable collections  , counting collections, collection enhancement operations, I/O, caching, strings Operation etc. And Guava is  widely used in Google's internal Java projects, and is also widely used by other companies. Even  the excellent class libraries in Guava are directly introduced in the new version of the JDK  , so the quality is beyond doubt.

The usage is directly dependent on the introduction of mavan.

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>30.0-jre</version>
</dependency>

Data validation

Data verification is very simple to say, one is  non-empty judgment  , the second is  the expected value judgment  . Non-empty judgment I think every Java developer is familiar with it, and often dealt with NullPointException at the beginning. The way we deal with it is naturally an if( xx == null) which can be easily solved. The expected value judgment is similar, just check whether the data value is the result you want.

Even with such a simple operation, do we often make mistakes? Moreover, the code written is always judging one line by line and it is so elegant no matter how you look at it. Fortunately, let's try Guava for the first time now.

Non-empty judgment

String param = "未读代码";
String name = Preconditions.checkNotNull(param);
System.out.println(name); // 未读代码
String param2 = null;
String name2 = Preconditions.checkNotNull(param2); // NullPointerException
System.out.println(name2);

After introducing Guava, you can directly use Preconditions.checkNotNull for non-null judgment. The advantage is that there are two, one is that the semantics are clear and the code is elegant; the other is that you can customize the error message, so if the parameter is empty, the error message is clear You can locate specific parameters directly.

String param2 = null;
String name2 = Preconditions.checkNotNull(param2,"param2 is null");
// java.lang.NullPointerException: param2 is null

Expected value judgment

Similar to the non-empty judgment, you can compare the current value and the expected value, and if they are not equal, you can customize the error message to throw.

String param = "www.wdbyte.com2";
String wdbyte = "www.wdbyte.com";
Preconditions.checkArgument(wdbyte.equals(param), "[%s] 404 NOT FOUND", param);
// java.lang.IllegalArgumentException: [www.wdbyte.com2] 404 NOT FOUND

Is it out of bounds

The Preconditions class can also be used to check whether the elements of arrays and collections are out of bounds.

// Guava 中快速创建ArrayList
List<String> list = Lists.newArrayList("a", "b", "c", "d");
// 开始校验
int index = Preconditions.checkElementIndex(5, list.size());
// java.lang.IndexOutOfBoundsException: index (5) must be less than size (4)

The way to quickly create a List in the code is also provided by Guava, and we will introduce the many poses created by the collection in Guava in detail later.

Immutable collection

Creating immutable collections is one of my personal favorite reasons for Guava, because  it is so practical to create a  collection that cannot be deleted, cannot be modified, or added . You don't have to worry about any problems with such a collection. In general, it has the following advantages:

  1. Thread-safe, because no element can be modified, it can be used in multiple threads at will and there is no concurrency problem.
  2. It can be provided to a third party without any worries, and cannot be modified anyway.
  3. Reduce the memory footprint, because it cannot be changed, the internal implementation can save the memory footprint to the greatest extent.
  4. Can be used as a constant collection.

Creation method

Having said so much, how do you use it? Quickly pick up the code.

// 创建方式1:of
ImmutableSet<String> immutableSet = ImmutableSet.of("a", "b", "c");
immutableSet.forEach(System.out::println);
// a
// b
// c

// 创建方式2:builder
ImmutableSet<String> immutableSet2 = ImmutableSet.<String>builder()
    .add("hello")
    .add(new String("未读代码"))
    .build();
immutableSet2.forEach(System.out::println);
// hello
// 未读代码

// 创建方式3:从其他集合中拷贝创建
ArrayList<String> arrayList = new ArrayList();
arrayList.add("www.wdbyte.com");
arrayList.add("https");
ImmutableSet<String> immutableSet3 = ImmutableSet.copyOf(arrayList);
immutableSet3.forEach(System.out::println);
// www.wdbyte.com
// https

The traversal results can be printed normally, but if additions, deletions and changes are made, UnsupportedOperationException will be reported directly.

In fact, an immutable collection is also provided in the JDK, which can be created as follows.

ArrayList<String> arrayList = new ArrayList();
arrayList.add("www.wdbyte.com");
arrayList.add("https");
// JDK Collections 创建不可变 List
List<String> list = Collections.unmodifiableList(arrayList);
list.forEach(System.out::println);// www.wdbyte.com https
list.add("未读代码"); // java.lang.UnsupportedOperationException

Precautions

  1. Immutable collections created with Guava reject null values, because in Google's internal survey, it is not necessary to put null values ​​in 95% of the cases.
  2. After the immutable collection provided by the JDK is successfully created, the added elements of the original collection will be reflected in the immutable collection, and Guava's immutable collection will not have this problem.
   List<String> arrayList = new ArrayList<>();
   arrayList.add("a");
   arrayList.add("b");
   List<String> jdkList = Collections.unmodifiableList(arrayList);
   ImmutableList<String> immutableList = ImmutableList.copyOf(arrayList);
   arrayList.add("ccc");
   jdkList.forEach(System.out::println);// result: a b ccc
   System.out.println("-------");
   immutableList.forEach(System.out::println);// result: a b
  1. If the elements of the immutable collection are reference objects, then the attributes of the reference objects can be changed.

Other immutable collections

Immutable collections In addition to the set shown above, there are many immutable collections. The following is the correspondence between immutable collections and other collections in Guava.

 

Collective operation factory

In fact, only one creation method will be introduced here, but why is it introduced separately? If you look at it, you'll  exclaim that it works  . Although JDK has provided a large number of collection-related operation methods, it is very convenient to use, but Guava still adds some very useful methods to ensure that you will love it the last time you use it.

Create a collection.

// 创建一个 ArrayList 集合
List<String> list1 = Lists.newArrayList();
// 创建一个 ArrayList 集合,同时塞入3个数据
List<String> list2 = Lists.newArrayList("a", "b", "c");
// 创建一个 ArrayList 集合,容量初始化为10
List<String> list3 = Lists.newArrayListWithCapacity(10);

LinkedList<String> linkedList1 = Lists.newLinkedList();
CopyOnWriteArrayList<String> cowArrayList = Lists.newCopyOnWriteArrayList();

HashMap<Object, Object> hashMap = Maps.newHashMap();
ConcurrentMap<Object, Object> concurrentMap = Maps.newConcurrentMap();
TreeMap<Comparable, Object> treeMap = Maps.newTreeMap();

HashSet<Object> hashSet = Sets.newHashSet();
HashSet<String> newHashSet = Sets.newHashSet("a", "a", "b", "c");

Guava adds a factory method creation method for each collection. The factory method creation methods of some collections have been shown above. Is it very easy to use? And you can throw in a few elements directly when you create it. This is awesome, you don't need to add one by one anymore.

Set intersection and union difference

Too simple, just look at the code and output results.

Set<String> newHashSet1 = Sets.newHashSet("a", "a", "b", "c");
Set<String> newHashSet2 = Sets.newHashSet("b", "b", "c", "d");

// 交集
SetView<String> intersectionSet = Sets.intersection(newHashSet1, newHashSet2);
System.out.println(intersectionSet); // [b, c]

// 并集
SetView<String> unionSet = Sets.union(newHashSet1, newHashSet2);
System.out.println(unionSet); // [a, b, c, d]

// newHashSet1 中存在,newHashSet2 中不存在
SetView<String> setView = Sets.difference(newHashSet1, newHashSet2);
System.out.println(setView); // [a]

Number of sets

This is really useful, because we often need to design a collection that can be counted, or a Map collection whose value is a List. If you don't understand it, look at the following code, whether you wrote it like this one day or night.

  1. Count the number of occurrences of the same element (I have written as short as possible in the code below). Native JDK writing:
   // Java 统计相同元素出现的次数。
   List<String> words = Lists.newArrayList("a", "b", "c", "d", "a", "c");
   Map<String, Integer> countMap = new HashMap<String, Integer>();
   for (String word : words) {
       Integer count = countMap.get(word);
       count = (count == null) ? 1 : ++count;
       countMap.put(word, count);
   }
   countMap.forEach((k, v) -> System.out.println(k + ":" + v));
   /**
    * result:
    * a:2
    * b:1
    * c:2
    * d:1
    */

Although the code has been optimized as much as possible, the amount of code is still quite large, so what is the difference in Guava? The HashMultiset class is mainly used in Guava. See below.

   ArrayList<String> arrayList = Lists.newArrayList("a", "b", "c", "d", "a", "c");
   HashMultiset<String> multiset = HashMultiset.create(arrayList);
   multiset.elementSet().forEach(s -> System.out.println(s + ":" + multiset.count(s)));
   /**
    * result:
    * a:2
    * b:1
    * c:2
    * d:1
    */

Yes, as long as you add the elements, you don't need to care whether they are repeated or not. Finally, you can use the count method to count the number of repeated elements. Looking comfortable and writing elegantly, HashMultiset is a Collection class implemented in Guava, which can easily count the number of elements.

  1. One-to-many, value is the Map collection of List. Assuming a scene where many animals need to be classified according to their types, I believe you will write similar codes in the end. Native JDK writing:
   HashMap<String, Set<String>> animalMap = new HashMap<>();
   HashSet<String> dogSet = new HashSet<>();
   dogSet.add("旺财");
   dogSet.add("大黄");
   animalMap.put("狗", dogSet);
   HashSet<String> catSet = new HashSet<>();
   catSet.add("加菲");
   catSet.add("汤姆");
   animalMap.put("猫", catSet);
   System.out.println(animalMap.get("猫")); // [加菲, 汤姆]

The last line of the query cat gets "Garfield" and "Tom" in the cat category. This code is simply too cumbersome, what about Guava?

   // use guava
   HashMultimap<String, String> multimap = HashMultimap.create();
   multimap.put("狗", "大黄");
   multimap.put("狗", "旺财");
   multimap.put("猫", "加菲");
   multimap.put("猫", "汤姆");
   System.out.println(multimap.get("猫")); // [加菲, 汤姆]

HashMultimap can throw in duplicate key values, and you can get all the value values ​​when you finally get it. You can see that the output result is the same as the JDK writing, but the code is very refreshing.

String manipulation

As the most commonly used data type in development, enhancements to string operations can make development more efficient.

Character splicing

In fact, JDK 8 has built-in string splicing method, but it is just simple splicing without additional operations, such as filtering out null elements, removing leading and trailing spaces, etc. Let's take a look at several ways of string splicing in JDK 8.

// JDK 方式一
ArrayList<String> list = Lists.newArrayList("a", "b", "c", null);
String join = String.join(",", list);
System.out.println(join); // a,b,c,null
// JDK 方式二
String result = list.stream().collect(Collectors.joining(","));
System.out.println(result); // a,b,c,null
// JDK 方式三
StringJoiner stringJoiner = new StringJoiner(",");
list.forEach(stringJoiner::add);
System.out.println(stringJoiner.toString()); // a,b,c,null

You can see that the null value is also spliced ​​into the string, which is sometimes not what we want, so what is the difference when using Guava?

ArrayList<String> list = Lists.newArrayList("a", "b", "c", null);
String join = Joiner.on(",").skipNulls().join(list);
System.out.println(join); // a,b,c

String join1 = Joiner.on(",").useForNull("空值").join("旺财", "汤姆", "杰瑞", null);
System.out.println(join1); // 旺财,汤姆,杰瑞,空值

You can see that using skipNulls() to skip null values, and useFornull(String) to customize the display text for null values.

String split

The JDK comes with its own string splitting, I think you must have used it, that is the split method of String, but this method has a problem, that is, if the last element is empty, it will be discarded. The strange thing is the first The element is empty but will not be discarded, which is very confusing. Let's demonstrate this problem through an example.

String str = ",a,,b,";
String[] splitArr = str.split(",");
Arrays.stream(splitArr).forEach(System.out::println);
System.out.println("------");
/**
 *
 * a
 * 
 * b
 * ------
 */

You can also test it yourself, the last element is not empty, it just disappears.

How does it work if you use Guava? Guava provides the Splitter class, and there are a series of operation modes to intuitively control the division logic.

String str = ",a ,,b ,";
Iterable<String> split = Splitter.on(",")
    .omitEmptyStrings() // 忽略空值
    .trimResults() // 过滤结果中的空白
    .split(str);
split.forEach(System.out::println);
/**
 * a
 * b
 */

Cache

In development, we may need to use a small-scale cache to improve access speed. At this time, introducing professional cache middleware may feel wasteful. It's okay now. Guava provides a simple cache class, and can automatically expire the added elements according to the estimated capacity, expiration time, etc. Even so, we have to estimate the memory space that may be occupied to prevent excessive memory usage.

Now let's take a look at how to use caching in Guava.

@Test
public void testCache() throws ExecutionException, InterruptedException {

    CacheLoader cacheLoader = new CacheLoader<String, Animal>() {
        // 如果找不到元素,会调用这里
        @Override
        public Animal load(String s) {
            return null;
        }
    };
    LoadingCache<String, Animal> loadingCache = CacheBuilder.newBuilder()
        .maximumSize(1000) // 容量
        .expireAfterWrite(3, TimeUnit.SECONDS) // 过期时间
        .removalListener(new MyRemovalListener()) // 失效监听器
        .build(cacheLoader); //
    loadingCache.put("狗", new Animal("旺财", 1));
    loadingCache.put("猫", new Animal("汤姆", 3));
    loadingCache.put("狼", new Animal("灰太狼", 4));

    loadingCache.invalidate("猫"); // 手动失效

    Animal animal = loadingCache.get("狼");
    System.out.println(animal);
    Thread.sleep(4 * 1000);
    // 狼已经自动过去,获取为 null 值报错
    System.out.println(loadingCache.get("狼"));
    /**
     * key=猫,value=Animal{name='汤姆', age=3},reason=EXPLICIT
     * Animal{name='灰太狼', age=4}
     * key=狗,value=Animal{name='旺财', age=1},reason=EXPIRED
     * key=狼,value=Animal{name='灰太狼', age=4},reason=EXPIRED
     *
     * com.google.common.cache.CacheLoader$InvalidCacheLoadException: CacheLoader returned null for key 狼.
     */
}

/**
 * 缓存移除监听器
 */
class MyRemovalListener implements RemovalListener<String, Animal> {

    @Override
    public void onRemoval(RemovalNotification<String, Animal> notification) {
        String reason = String.format("key=%s,value=%s,reason=%s", notification.getKey(), notification.getValue(), notification.getCause());
        System.out.println(reason);
    }
}

class Animal {
    private String name;
    private Integer age;

    @Override
    public String toString() {
        return "Animal{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
    }

    public Animal(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}

This example is mainly divided into CacheLoader, MyRemovalListener, LoadingCache.

The load method is rewritten in CacheLoader. This method will be called when the query cache misses a hit. I directly return null here. In fact, this will throw the CacheLoader returned null for key exception message when there is no hit.

MyRemovalListener is the monitor class when the cache element fails. The onRemoval method is automatically called when the element cache fails. It should be noted that this method is a synchronous method. If it takes a long time here, it will block until the processing is completed.

LoadingCache is the main operation object of the cache, and the put and get methods are commonly used.

to sum up

The above introduces the Guava functions that I think are the most commonly used. As Google’s open source Java development core library, I personally feel that it is still very useful. After the introduction, it can not only quickly implement some commonly used functions in development, but also make the code more elegant and concise. I think it applies to every Java project. You can also discover other features of Guava by yourself. Its Github address is: https://github.com/google/guava.

Guess you like

Origin blog.csdn.net/AI_mashimanong/article/details/109289274