Guava-ジャンクコードを保存し、エレガントで効率的に記述し、効率をN倍に向上させる

最近、クラスメートのコードを見ると、コードがGoogleのオープンソースのGuavaコアライブラリのコンテンツを多く使用していることがわかりました。これにより、コードがはるかにシンプルで明確になり、Guavaの最も便利な機能を共有することを学びました。

Guavaプロジェクトは、GoogleのオープンソースJavaコアライブラリです。主に、データ検証 、 不変コレクション 、コレクションのカウント、コレクション拡張操作、I / O、キャッシング、文字列など、Java開発で頻繁に使用される機能が含まれています。 操作など また、Guavaは Googleの内部Javaプロジェクトで広く使用されて おり、他社でも広く使用されています。Guavaの優れたクラスライブラリでさえ、新しいバージョンのJDK直接導入されている ため、品質に疑いの余地はありません。

使用法は、mavanの導入に直接依存しています。

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

データ検証

データ検証は非常に簡単に言うことができます。1つは空でない判断 であり 、もう1つは 期待値の判断 です。空でない判断すべてのJava開発者はこれに精通しており、最初はNullPointExceptionに対処することが多いと思います。私たちがそれを扱う方法は、当然、簡単に解決できるif(xx == null)です。期待値の判断も同様です。データ値が目的の結果であるかどうかを確認してください。

このような簡単な操作でも、間違えることがよくありますか?さらに、書かれたコードは常に1行ずつ判断されるため、どのように見てもエレガントになります。幸いなことに、今初めてグアバを試してみましょう。

空でない判断

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

Guavaを導入した後、Preconditions.checkNotNullを直接使用してnull以外の判断を行うことができます。利点は2つあり、1つはセマンティクスが明確でコードが洗練されていること、もう1つはエラーメッセージをカスタマイズできることです。したがって、パラメーターが空の場合、エラーメッセージは明確です。特定のパラメータを直接見つけることができます。

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

期待値判断

空でない判定と同様に、現在の値と期待値を比較でき、等しくない場合は、カスタムエラーメッセージをスローできます。

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

範囲外ですか

Preconditionsクラスを使用して、配列とコレクションの要素が範囲外かどうかを確認することもできます。

// 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)

コードでリストをすばやく作成する方法もGuavaによって提供されており、Guavaのコレクションによって作成された多くのポーズについては後で詳しく説明します。

不変のコレクション

不変のコレクション を作成することは、Guavaの個人的なお気に入りの理由の1つです。これは、削除、変更、または追加できないコレクションを作成する ことが非常に実用的だからです。このようなコレクションで問題を心配する必要はありません。一般に、次の利点があります。

  1. スレッドセーフ。要素は変更できないため、複数のスレッドで自由に使用でき、同時実行の問題はありません。
  2. 第三者に安心して提供することができ、変更することはできません。
  3. 変更できないため、メモリフットプリントを削減します。内部実装により、メモリフットプリントを最大限に節約できます。
  4. 定数コレクションとして使用できます。

作成方法

そんなに言って、どうやって使うの?すばやくコードを取得します。

// 创建方式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

トラバーサル結果は通常どおり印刷できますが、追加、削除、および変更が行われた場合、UnsupportedOperationExceptionが直接報告されます。

実際、JDKには不変のコレクションも用意されており、次のように作成できます。

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

予防

  1. Guavaで作成された不変のコレクションは、null値を拒否します。これは、Googleの内部調査では、95%のケースでnull値を入力する必要がないためです。
  2. JDKによって提供される不変のコレクションが正常に作成された後、元のコレクションの追加された要素は不変のコレクションに反映され、Guavaの不変のコレクションにはこの問題はありません。
   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. 不変コレクションの要素が参照オブジェクトである場合、参照オブジェクトの属性を変更できます。

その他の不変のコレクション

不変のコレクション上記のセットに加えて、多くの不変のコレクションがあります。以下は、不変のコレクションとグアバの他のコレクションとの対応です。

 

集合運転工場

実際、ここでは1つの作成方法のみを紹介しますが、なぜ個別に紹介するのですか?あなたがそれを見れば、あなたはそれがうまくいくと叫ぶでしょう  。JDKにはコレクション関連の操作方法が多数用意されていますが、非常に便利ですが、Guavaには、最後に使用したときに気に入るように、非常に便利な方法がいくつか追加されています。

コレクションを作成します。

// 创建一个 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は、コレクションごとにファクトリメソッド作成メソッドを追加します。いくつかのコレクションのファクトリメソッド作成メソッドを上に示しました。とても使いやすいですか?また、作成時にいくつかの要素を直接投入することもできます。これはすばらしいことで、もう1つずつ追加する必要はありません。

交差点と和集合の差を設定する

単純すぎるので、コードと出力結果を見てください。

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]

セット数

カウントできるコレクションや、値がリストであるMapコレクションを設計する必要があることが多いため、これは非常に便利です。理解できない場合は、このように書いたかどうかに関係なく、次のコードを確認してください。

  1. 同じ要素の出現回数を数えます(以下のコードでできるだけ短く記述しました)。ネイティブJDKの書き込み:
   // 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
    */

コードは可能な限り最適化されていますが、コードの量はまだかなり多いので、Guavaの違いは何ですか?HashMultisetクラスは主にGuavaで使用されます。以下を参照してください。

   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
    */

はい、要素を追加する限り、繰り返されるかどうかを気にする必要はありません。最後に、countメソッドを使用して、繰り返される要素の数をカウントできます。見た目もエレガントなHashMultisetは、Guavaに実装されているCollectionクラスで、要素の数を簡単に数えることができます。

  1. 1対多の値は、ListのMapコレクションです。多くの動物を種類ごとに分類する必要があるシーンを想定すると、最終的には同様のコードを書くことになると思います。ネイティブJDKの書き込み:
   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("猫")); // [加菲, 汤姆]

クエリcatの最後の行は、catカテゴリで「Garfield」と「Tom」を取得しました。このコードは単純に面倒です。Guavaはどうですか?

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

HashMultimapは重複するキー値をスローする可能性があり、最終的に取得したときにすべての値値を取得できます。出力結果はJDKの記述と同じですが、コードは非常に更新されています。

文字列操作

開発で最も一般的に使用されるデータタイプとして、文字列操作の拡張により、開発をより効率的にすることができます。

キャラクタースプライシング

実際、JDK 8には文字列スプライシングメソッドが組み込まれていますが、null要素の除外、先頭と末尾のスペースの削除などの追加操作を行わない単純なスプライシングです。JDK8でのストリングスプライシングのいくつかの方法を見てみましょう。

// 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

null値も文字列にスプライスされていることがわかりますが、これは私たちが望むものではない場合がありますが、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); // 旺财,汤姆,杰瑞,空值

skipNulls()を使用してnull値をスキップし、useFornull(String)を使用してnull値の表示テキストをカスタマイズしていることがわかります。

文字列分割

JDKには文字列分割が付属しています。使用したに違いないと思います。これが文字列の分割方法ですが、この方法には問題があります。最後の要素が空の場合は破棄されます。奇妙なのは最初の要素です。要素は空ですが、破棄されないため、非常に混乱します。例を使用して、この問題を示しましょう。

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

自分でテストすることもできます。最後の要素は空ではなく、消えるだけです。

Guavaを使用する場合、どのように機能しますか?GuavaはSplitterクラスを提供し、分割ロジックを直感的に制御するための一連の操作モードを備えています。

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

キャッシュ

開発では、アクセス速度を向上させるために小規模キャッシュを使用する必要がある場合があります。現時点では、プロのキャッシュミドルウェアを導入することは無駄に感じるかもしれません。今は大丈夫です。Guavaはシンプルなキャッシュクラスを提供し、推定容量、有効期限などに応じて、追加された要素を自動的に期限切れにすることができます。それでも、過度のメモリ使用を防ぐために、占有される可能性のあるメモリスペースを見積もる必要があります。

それでは、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;
    }
}

この例は、主にCacheLoader、MyRemovalListener、LoadingCacheに分かれています。

loadメソッドはCacheLoaderで書き直されます。このメソッドは、クエリキャッシュがヒットを逃したときに呼び出されます。ここで直接nullを返します。実際、ヒットがない場合、キー例外メッセージに対してCacheLoaderがnullを返します。

MyRemovalListenerは、キャッシュ要素に障害が発生したときのモニタークラスです。要素キャッシュに障害が発生すると、onRemovalメソッドが自動的に呼び出されます。このメソッドは同期メソッドであることに注意してください。ここで時間がかかると、処理が完了するまでブロックされます。

LoadingCacheはキャッシュの主要な操作オブジェクトであり、putメソッドとgetメソッドが一般的に使用されます。

総括する

上記で紹介したGuava関数は、最も一般的に使用されていると思います。Guavaは、GoogleのオープンソースJava開発コアライブラリとして、個人的にはまだ非常に便利だと感じています。導入後は、開発で一般的に使用されるいくつかの関数をすばやく実装できるだけでなく、コードをよりエレガントで簡潔にすることもできます。すべてのJavaプロジェクトに当てはまると思います。Guavaの他の機能を自分で発見することもできます。そのGithubアドレスはhttps://github.com/google/guavaです。

おすすめ

転載: blog.csdn.net/AI_mashimanong/article/details/109289274