Javaの標準APIのjava.util.Setで要素の重複は起こり得るらしい

f:id:ts0818:20201202182939j:plain

(ちゃ)は、チャノキ学名:Camellia sinensis (L.) Kuntze)のを加工して作られる飲み物である。

茶 - Wikipedia

茶の原産地については、四川雲南説(長江及びメコン川上流)、中国東部から東南部にかけてとの説、いずれも原産地であるという二元説がある

茶 - Wikipedia

しかし、「茶」という字が成立し全国的に通用するようになったのは代になってからであり、それまでは「荼(と)」「茗(めい)」「荈(せん)」「檟(か)」といった文字が当てられていた

茶 - Wikipedia

書籍に現れるものとしては、紀元前2世紀前漢)の『爾雅』に見られる「檟」、または、司馬相如の『凡将篇』に見られる「荈詫(セツタ)」が最初とされる。漢代の『神農本草経』果菜部上品には次のような記述がある。

苦菜。一名荼草。一名選。味苦寒。生川谷。治五蔵邪気。厭穀。胃痹。久服安心益気。聡察少臥。軽身耐老。

茶 - Wikipedia

⇧ 中国4000年は伊達じゃないってことですかね。

そんな、「お茶」ですが、

www.sankeibiz.jp

 奈良県立医科大学(同県橿原市)は27日、新型コロナウイルスが市販のお茶によって無害化する効果を確認したと発表した。基礎研究段階で人での効果は未確認だが、試験管内でウイルスが1分間お茶に触れることで最大99%が感染力を失っており、感染対策の一つとして期待。商品により効果に差があり、メーカーの許可を得て商品名の公表を検討するとしている。

お茶で新型コロナ無害化 1分で最大99% 奈良県立医大 - SankeiBiz(サンケイビズ):自分を磨く経済情報サイト

⇧「コロナ対策」の希望の星となるか、という発見があったそうですね。

「お茶」を飲むようにしますか~。

今回も全く関係ない話でスタートしましたが、今回もJavaについてです。

レッツトライ~。 

 

Javaの標準APIjava.util.Setで要素の重複は起こり得るらしい

っていうか、ネットの情報が、

java-code.jp

HashSetは、要素の重複を許可しない集合構造を表します。また、順番も持ちません。数学での集合にもよく似た構造と考えて良いでしょう。

HashSet | Javaコード入門

techacademy.jp

java.util.HashSetクラスはCollectionインターフェースを実装したクラスで、要素の集まりを扱います。以下の特徴があります。

  1. 要素としてnullを格納することができる。
  2. 要素の重複不可(同じ名言いようの要素を複数格納することはできません)
  3. 要素の取得順は保証されない(格納した順番など、なんらかの順で取得できるわけではありません)

JavaのHashSetクラスの使い方を現役エンジニアが解説【初心者向け】 | TechAcademyマガジン

⇧「要素の重複を許可しない」「要素の重複不可」って謳っていたので、なるほど、「HashSet」使ってれば、要素が重複する心配は無いんだね、って見事に騙されたんですよ、っていうか、「初心者向け」の情報で嘘を教えちゃマズいと思うんだけど...

 

ズバリ、要素の重複でお悩みの方がいらっしゃいました。

teratail.com

SetのJavadocにも以下のようにある通り、
追加された後にSetの要素を変更した場合、動作は保証されず、要素が重複することもあります。

注:可変オブジェクトがセット要素として使用される場合は、細心の注意が必要です。オブジェクトがセット内の要素であるうちに equals 比較に影響する方式でその値が変更された場合、セットの動作は保証されません。

Java - Setで重複が発生してしまう。|teratail

⇧ う~ん、「java.util.Set」のドキュメント(javadoc)に「要素の重複が起こりえるコーディング」の具体的な例を記載してくれていないのでよく分からんけど、要素の重複が起こり得ることもあるんだと。

ネットにある情報、軒並み、「要素の重複が起こり得る」ってことに言及してないんですよね... 

それにしても、「java.util.Set」のAPIの仕様ですが、個々人のコーディングの仕方に左右されるってのは、何とも心許ない仕様ですかね...

せめて、「要素の重複が起こり得るコーディング」の例とかドキュメントに掲載してくれれば良い気もしますが...

 

どうやって重複をなくすか

java.util.Set」「java.util.HashSet」について、要素の重複が起こりえることが分かったため、一意な要素を保証することはできないですと。

とは言え、「重複の無い要素の集合」を必要とする日も来ますと。

って言うか、これからは、「java.util.Set」を使っても「重複の無い要素」を実現できる保証は無くなったわけで、我々は何を信じれば良いのやら...

 

コーディングの創意工夫で乗り切るしかないんだと。 

oboe2uran.hatenablog.com

cyzennt.co.jp

⇧ 上記サイト様が参考になりますかね。

 

nlp100.github.io

paraparaparadise”と”paragraph”に含まれる文字bi-gramの集合を,それぞれ, XとYとして求め,XとYの和集合,積集合,差集合を求めよ.さらに,’se’というbi-gramがXおよびYに含まれるかどうかを調べよ.

第1章: 準備運動 - NLP100 2020

⇧ 上記サイト様の「06.集合」をコーディングしてみました。ただ、「java.util.Set」使っても要素が重複しないような問題なので、「要素の重複」の確認はできなかったんだけど。 

qiita.com

⇧ コーディングは上記サイト様を参考にさせていただきました。

 

package one_hundred_nock.first.six;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class ContainNumber {

  public static void main(String[] args) {
    // 対象のテキスト
    String strFirst ="paraparaparadise";
    String strSecond = "paragraph";

    // bi-gramの集合
    Set<String> X = new HashSet<>();
    Set<String> Y = new HashSet<>();

    nGram(strFirst, 2).entrySet().forEach((entry)-> {
      X.add(entry.getValue());
    });
    // bi-gramの集合ができてるか確認
    System.out.print("Xのn-gram:");
    for (String x: X) {
      System.out.print( x + " ");
    }
    System.out.println();

    nGram(strSecond, 2).entrySet()
      .stream()
      .map(entry -> entry.getValue())
      .distinct()
      .forEach(value -> {
        Y.add(value);
      });
    // bi-gramの集合ができてるか確認
    System.out.print("Yのn-gram:");
    for (String y: Y) {
      System.out.print(y + " ");
    }
    System.out.println();

    StringBuilder sb = new StringBuilder();
    // 和集合
    System.out.println("■和集合");
    Set<String> union = new HashSet<>();
    union.addAll(X);
    union.addAll(Y);
    union.forEach((value) -> {
      sb.append(value).append(" ");
    });
    System.out.println(sb.toString());
    sb.setLength(0);

    // 積集合
    System.out.println("■積集合");
    Set<String> intersection = new HashSet<>();
    intersection.addAll(Y);
    intersection.retainAll(X);
    intersection.forEach((value) -> {
      sb.append(value).append(" ");
    });
    System.out.println(sb.toString());
    sb.setLength(0);

    // 差集合
    System.out.println("■差集合");
    Set<String> differenceSet = new HashSet<>();
    // 作業用
    Set<String> tmpX = X;
    Set<String> tmpY = Y;

    tmpX.removeAll(intersection);
    tmpY.removeAll(intersection);
    differenceSet.addAll(tmpX);
    differenceSet.addAll(tmpY);
    differenceSet.forEach((value) -> {
      sb.append(value).append(" ");
    });
    System.out.println(sb.toString());
    sb.setLength(0);

    // "se"というb-gram がXに含まれるかどうかの確認
    System.out.println("■\"se\"というb-gram がXに含まれるかどうかの確認");
    X.stream()
      .filter((value) -> "se".contains(value))
      .forEach((value) -> {
       sb.append(value).append(" ");
    });
    System.out.println(sb.toString());
    sb.setLength(0);

    // "se"というb-gram がYに含まれるかどうかの確認
    System.out.println("■\"se\"というb-gram がYに含まれるかどうかの確認");
    Y.stream()
      .filter((value) -> "se".contains(value))
      .forEach((value) -> {
       sb.append(value).append(" ");
    });
    System.out.println(sb.toString());
    sb.setLength(0);

  }

  // 文字単位での分割
  public static Map<Integer, String> nGram(String str, int nGram) {
    Map<Integer, String> map = new HashMap<>();
    for (int index = 0; index < str.length(); index++) {
      if (index+nGram <= str.length()) {
        map.put(index, str.substring(index, index+nGram));
      }
    }
    return map;
  }

}

で、実際に実施してみる。

f:id:ts0818:20201202130826p:plain

f:id:ts0818:20201202130857p:plain

java.util.Set」を使っていて、要素が重複するケースに遭遇できていないので、何とも言えないですが、「java.util.Set」で要素が重複することも考慮すべきなんですね。

「初心者向け」のサイトで、「java.util.Setを使っても要素の重複が起こり得ること」に言及していないのは良くない気がしますが、どうなんですかね?

Java 8 から導入された「Stream API」と「ラムダ式」が相変わらず慣れませんが...

今回はこのへんで。