Java 中節省 90% 時間的常用的工具類

初念初戀發表於2021-08-03

前言

你們有木有喜歡看程式碼的領導啊,我的領導就喜歡看我寫的程式碼,有事沒事就喜歡跟我探討怎麼寫才最好,哈哈哈...挺好。

今天我們就一起來看看可以節省 90% 的加班時間的第三方開源庫吧,第一個介紹的必須是 Apache 下的 Commons 庫。第二個是 google 開源的 Guava 庫。

Apache Commons

Apache Commons 是一個功能非常強大、經常被使用到的庫。它有 40 個左右的類庫,包含了對字串、日期、陣列等的操作。

Lang3

Lang3 是一個處理 Java 中基本物件的包,比如用 StringUtils 類操作字串、ArrayUtils 類運算元組、DateUtils 類可以處理日期、MutablePair 類可以返回多個欄位等等。

包結構:

image-20210719140346416

maven 依賴

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.11</version>
</dependency>

字串操作

對字串快速操作,在 if else 的少寫判空條件。

public static void main(String[] args) {
    boolean blank = StringUtils.isBlank(" ");//注意此處是null哦  這和isEmpty不一樣的
    System.out.println(blank);

    boolean empty = StringUtils.isEmpty(" ");//注意這裡是false
    System.out.println(empty);

    boolean anyBlank = StringUtils.isAnyBlank("a", " ", "c");// 其中一個是不是空字串
    System.out.println(anyBlank);

    boolean numeric = StringUtils.isNumeric("1");//字串是不是全是數字組成,"." 不算數字
    System.out.println(numeric);

    String remove = StringUtils.remove("abcdefgh", "a");//移除字串
    System.out.println(remove);
}

輸出結果:

true
false
true
true
bcdefgh

Process finished with exit code 0

日期操作

終於可以不用 SimpleDateFormat 格式化日期了,DateUtils.iterator 可以獲取一段時間。

public static void main(String[] args) throws ParseException {

    Date date = DateUtils.parseDate("2021-07-15", "yyyy-MM-dd");

    Date date1 = DateUtils.addDays(date, 1);//加一天
    System.out.println(date1);

    boolean sameDay = DateUtils.isSameDay(date, new Date());//比較
    System.out.println(sameDay);
    /*
        獲取一段日期
        RANGE_WEEK_SUNDAY 從週日開始獲取一週日期
        RANGE_WEEK_MONDAY 從週一開始獲取一週日期
        RANGE_WEEK_RELATIVE 從當前時間開始獲取一週日期
        RANGE_WEEK_CENTER 以當前日期為中心獲取一週日期
        RANGE_MONTH_SUNDAY 從週日開始獲取一個月日期
        RANGE_MONTH_MONDAY 從週一開始獲取一個月日期
        */
    Iterator<Calendar> iterator = DateUtils.iterator(date, DateUtils.RANGE_WEEK_CENTER);
    while (iterator.hasNext()) {
        Calendar next = iterator.next();
        System.out.println(DateFormatUtils.format(next, "yyyy-MM-dd"));
    }
}

輸出結果:

Fri Jul 16 00:00:00 CST 2021
false
2021-07-12
2021-07-13
2021-07-14
2021-07-15
2021-07-16
2021-07-17
2021-07-18

Process finished with exit code 0

返回多個欄位

有時候在一個方法中需要返回多個值的時候,經常會使用 HashMap 返回或者是 JSON 返回。Lang3 下已經幫我們提供了這樣的工具類,不需要再多寫 HashMap 和 JSON 了。

public static void main(String[] args) {

    MutablePair<Integer, String> mutablePair = MutablePair.of(2, "這是兩個值");
    System.out.println(mutablePair.getLeft() + "  " + mutablePair.getRight());

    MutableTriple<Integer, String, Date> mutableTriple = MutableTriple.of(2, "這是三個值", new Date());
    System.out.println(mutableTriple.getLeft() + " " + mutableTriple.getMiddle() + " " + mutableTriple.getRight());
}

輸出結果:

2  這是兩個值
2 這是三個值 Fri Jul 16 15:24:40 CST 2021

Process finished with exit code 0

ArrayUtils 陣列操作

ArrayUtils 是專門處理陣列的類,可以讓方便的處理陣列而不是需要各種迴圈操作。

public static void main(String[] args) {

    //合併陣列
    String[] array1 = new String[]{"value1", "value2"};
    String[] array2 = new String[]{"value3", "value4"};
    String[] array3 = ArrayUtils.addAll(array1, array2);
    System.out.println("array3:"+ArrayUtils.toString(array3));

    //clone 陣列
    String[] array4 = ArrayUtils.clone(array3);
    System.out.println("array4:"+ArrayUtils.toString(array4));

    //陣列是否相同
    boolean b = EqualsBuilder.reflectionEquals(array3, array4);
    System.out.println(b);

    //反轉陣列
    ArrayUtils.reverse(array4);
    System.out.println("array4反轉後:"+ArrayUtils.toString(array4));

    //二維陣列轉 map
    Map<String, String> arrayMap = (HashMap) ArrayUtils.toMap(new String[][]{
        {"key1", "value1"}, {"key2", "value2"}
    });
    for (String s : arrayMap.keySet()) {
        System.out.println(arrayMap.get(s));
    }
}

輸出結果:

array3:{value1,value2,value3,value4}
array4:{value1,value2,value3,value4}
true
array4反轉後:{value4,value3,value2,value1}
value1
value2

Process finished with exit code 0

EnumUtils 列舉操作

  • getEnum(Class enumClass, String enumName) 通過類返回一個列舉,可能返回空;
  • getEnumList(Class enumClass) 通過類返回一個列舉集合;
  • getEnumMap(Class enumClass) 通過類返回一個列舉map;
  • isValidEnum(Class enumClass, String enumName) 驗證enumName是否在列舉中,返回true或false。
public enum ImagesTypeEnum {
    JPG,JPEG,PNG,GIF;
}
    public static void main(String[] args) {
        ImagesTypeEnum imagesTypeEnum = EnumUtils.getEnum(ImagesTypeEnum.class, "JPG");
        System.out.println("imagesTypeEnum = " + imagesTypeEnum);
        System.out.println("--------------");
        List<ImagesTypeEnum> imagesTypeEnumList = EnumUtils.getEnumList(ImagesTypeEnum.class);
        imagesTypeEnumList.stream().forEach(
                imagesTypeEnum1 -> System.out.println("imagesTypeEnum1 = " + imagesTypeEnum1)
        );
        System.out.println("--------------");
        Map<String, ImagesTypeEnum> imagesTypeEnumMap = EnumUtils.getEnumMap(ImagesTypeEnum.class);
        imagesTypeEnumMap.forEach((k, v) -> System.out.println("key:" + k + ",value:" + v));
        System.out.println("-------------");
        boolean result = EnumUtils.isValidEnum(ImagesTypeEnum.class, "JPG");
        System.out.println("result = " + result);
        boolean result1 = EnumUtils.isValidEnum(ImagesTypeEnum.class, null);
        System.out.println("result1 = " + result1);
    }

輸出結果:

imagesTypeEnum = JPG
--------------
imagesTypeEnum1 = JPG
imagesTypeEnum1 = JPEG
imagesTypeEnum1 = PNG
imagesTypeEnum1 = GIF
--------------
key:JPG,value:JPG
key:JPEG,value:JPEG
key:PNG,value:PNG
key:GIF,value:GIF
-------------
result = true
result1 = false

Process finished with exit code 0

collections4 集合操作

commons-collections4 增強了 Java 集合框架,提供了一系列簡單的 API 方便操作集合。

maven 依賴

 <dependency>  
    <groupId>org.apache.commons</groupId>  
    <artifactId>commons-collections4</artifactId>  
    <version>4.4</version>  
</dependency> 

CollectionUtils 工具類

這是一個工具類,可以檢查 null 元素不被加入集合,合併列表,過濾列表,兩個列表的並集、差集、合集。有部分功能在 Java 8 中可以被 Stream API 替換。

public static void main(String[] args) {

    //null 元素不能加進去
    List<String> arrayList1 = new ArrayList<>();
    arrayList1.add("a");
    CollectionUtils.addIgnoreNull(arrayList1, null);
    System.out.println(arrayList1.size());

    //排好序的集合,合併後還是排序的
    List<String> arrayList2 = new ArrayList<>();
    arrayList2.add("a");
    arrayList2.add("b");

    List<String> arrayList3 = new ArrayList<>();
    arrayList3.add("c");
    arrayList3.add("d");
    System.out.println("arrayList3:" + arrayList3);

    List<String> arrayList4 = CollectionUtils.collate(arrayList2, arrayList3);
    System.out.println("arrayList4:" + arrayList4);

    //交集
    Collection<String> strings = CollectionUtils.retainAll(arrayList4, arrayList3);
    System.out.println("arrayList3和arrayList4的交集:" + strings);

    //並集
    Collection<String> union = CollectionUtils.union(arrayList4, arrayList3);
    System.out.println("arrayList3和arrayList4的並集:" + union);

    //差集
    Collection<String> subtract = CollectionUtils.subtract(arrayList4, arrayList3);
    System.out.println("arrayList3和arrayList4的差集:" + subtract);

    // 過濾,只保留 b
    CollectionUtils.filter(arrayList4, s -> s.equals("b"));
    System.out.println(arrayList4);
}

輸出結果:

1
arrayList3:[c, d]
arrayList4:[a, b, c, d]
arrayList3和arrayList4的交集:[c, d]
arrayList3和arrayList4的並集:[a, b, c, d]
arrayList3和arrayList4的差集:[a, b]
[b]

Process finished with exit code 0

Bag 統計次數

用於統計值在集合中出現的次數。

public static void main(String[] args) {
    Bag bag = new HashBag<String>();
    bag.add("a");
    bag.add("b");
    bag.add("a");
    bag.add("c", 3);
    System.out.println(bag);
    System.out.println(bag.getCount("c"));
}

輸出結果:

[2:a,1:b,3:c]
3

Process finished with exit code 0

beanutils Bean 操作

beanutils 是通過反射機制對 JavaBean 進行操作的。比如對 Bean 進行復制、map 轉物件、物件轉 Map。

maven 依賴

<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.4</version>
</dependency>
public class User {
    
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}
public static void main(String[] args) throws Exception {
    User user1 = new User();
    user1.setName("李四");
    User user2 = (User) BeanUtils.cloneBean(user1);
    System.out.println(user2.getName());

    //User 轉 map
    Map<String, String> describe = BeanUtils.describe(user1);
    System.out.println(describe);

    //Map 轉 User
    Map<String, String> beanMap = new HashMap();
    beanMap.put("name", "張三");
    User user3 = new User();
    BeanUtils.populate(user3, beanMap);
    System.out.println(user3.getName());
}

輸出結果:

李四
{name=李四}
張三

Process finished with exit code 0

Guava

Google 開源的一個基於 Java 擴充套件專案,包含了一些基本工具、集合擴充套件、快取、併發工具包、字串處理等。

maven 依賴

<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>30.1.1-jre</version>
</dependency>

Map<String, List> 型別

在java 程式碼中經常會遇到需要寫 Map<String, List> map 的區域性變數的時候。有時候業務情況還會更復雜一點。

public static void main(String[] args) {
    //以前
    Map<String, List<String>> map = new HashMap<>();
    List<String> list = new ArrayList<>();
    list.add("張三");
    list.add("李四");
    map.put("名稱", list);
    System.out.println(map.get("名稱"));

    //現在
    Multimap<String, String> multimap = ArrayListMultimap.create();
    multimap.put("名稱", "張三");
    multimap.put("名稱", "李四");
    System.out.println(multimap.get("名稱"));
}

輸出結果:

[張三, 李四]
[張三, 李四]

Process finished with exit code 0

value 不能重複的 Map

在 Map 中 value 的值時可以重複的,Guava 可以建立一個 value 不可重複的 Map,並且 Map 和 value 可以對調。

public static void main(String[] args) {
    //會報異常
    BiMap<String ,String> biMap = HashBiMap.create();
    biMap.put("key1", "value");
    biMap.put("key2", "value");
    System.out.println(biMap.get("key1"));
}

輸出結果:

Exception in thread "main" java.lang.IllegalArgumentException: value already present: value
	at com.google.common.collect.HashBiMap.put(HashBiMap.java:287)
	at com.google.common.collect.HashBiMap.put(HashBiMap.java:262)
	at org.example.clone.Test.main(Test.java:17)

Process finished with exit code 1
public static void main(String[] args) {
    BiMap<String ,String> biMap = HashBiMap.create();
    biMap.put("key1", "value1");
    biMap.put("key2", "value2");
    System.out.println(biMap.get("key1"));

    //key-value 對調
    biMap = biMap.inverse();
    System.out.println(biMap.get("value1"));
}

輸出結果:

value1
key1

Process finished with exit code 0

Guava cache

寫業務的時候肯定會使用快取,當不想用第三方作為快取的時候,Map 又不夠強大,就可以使用 Guava 的快取。

快取的併發級別

Guava提供了設定併發級別的API,使得快取支援併發的寫入和讀取。與ConcurrentHashMap類似,Guava cache的併發也是通過分離鎖實現。在通常情況下,推薦將併發級別設定為伺服器cpu核心數。

CacheBuilder.newBuilder()
		// 設定併發級別為cpu核心數,預設為4
		.concurrencyLevel(Runtime.getRuntime().availableProcessors()) 
		.build();

快取的初始容量設定

我們在構建快取時可以為快取設定一個合理大小初始容量,由於Guava的快取使用了分離鎖的機制,擴容的代價非常昂貴。所以合理的初始容量能夠減少快取容器的擴容次數。

CacheBuilder.newBuilder()
		// 設定初始容量為100
		.initialCapacity(100)
		.build();

設定最大儲存

Guava Cache可以在構建快取物件時指定快取所能夠儲存的最大記錄數量。當Cache中的記錄數量達到最大值後再呼叫put方法向其中新增物件,Guava會先從當前快取的物件記錄中選擇一條刪除掉,騰出空間後再將新的物件儲存到Cache中。

public static void main(String[] args) {
    Cache<String, String> cache = CacheBuilder.newBuilder().maximumSize(2).build();
    cache.put("key1", "value1");
    cache.put("key2", "value2");
    cache.put("key3", "value3");
    System.out.println(cache.getIfPresent("key1")); //key1 = null
}

輸出結果:

null

Process finished with exit code 0

過期時間

expireAfterAccess() 可以設定快取的過期時間。

public static void main(String[] args) throws InterruptedException {
    //設定過期時間為2秒
    Cache<String, String> cache1 = CacheBuilder.newBuilder().maximumSize(2).expireAfterAccess(2, TimeUnit.SECONDS).build();
    cache1.put("key1", "value1");
    Thread.sleep(1000);
    System.out.println(cache1.getIfPresent("key1"));
    Thread.sleep(2000);
    System.out.println(cache1.getIfPresent("key1"));
}

輸出結果:

value1
null

Process finished with exit code 0

LoadingCache

使用自定義ClassLoader載入資料,置入記憶體中。從LoadingCache中獲取資料時,若資料存在則直接返回;若資料不存在,則根據ClassLoaderload方法載入資料至記憶體,然後返回該資料。

public class Test {

    public static void main(String[] args) throws Exception {
        System.out.println(numCache.get(1));
        Thread.sleep(1000);
        System.out.println(numCache.get(1));
        Thread.sleep(1000);
        numCache.put(1, 6);
        System.out.println(numCache.get(1));

    }

    private static LoadingCache<Integer, Integer> numCache = CacheBuilder.newBuilder().
            expireAfterWrite(5L, TimeUnit.MINUTES).
            maximumSize(5000L).
            build(new CacheLoader<Integer, Integer>() {
                @Override
                public Integer load(Integer key) throws Exception {
                    System.out.println("no cache");
                    return key * 5;
                }
            });
}

輸出結果:

no cache
5
5
6

Process finished with exit code 0

總結

通過 Apache Commons 和 Guava 兩個第三方的開源工具庫,可以減少迴圈、ifelse 的程式碼。寫出的程式碼更有健壯性並且可以在新人面前裝一波。Apache Commons 和 Guava 有許許多多的工具類,這裡只列出了小小的部分,可以在官網例子中檢視到各種用法。

最後

我是一個正在被打擊還在努力前進的碼農。如果文章對你有幫助,記得點贊、關注喲,謝謝!

相關文章