1.LinkedHashMap
LinkedHashMap と HashMap の違いは何ですか?
LinkedHashMap の基礎となる実装?
LinkedHashMap を使用して LRU キャッシュを実装しますか?
1. LinkedHashMap と HashMap の違い
ほとんどの場合、スレッドセーフの問題がなければ、Map は基本的に HashMap を使用できますが、HashMap には問題があります。障害。HashMap のこの欠点は、いくつかのシナリオでは、順序付けされた Map を期待するため、しばしば問題を引き起こします. これは LinkedHashMap です。小さなデモを参照してください:
public static void main(String[] args) {
Map<String, String> map = new LinkedHashMap<String, String>();
map.put("apple", "苹果");
map.put("watermelon", "西瓜");
map.put("banana", "香蕉");
map.put("peach", "桃子");
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
System.out.println(entry.getKey() + "=" + entry.getValue());
}
}
输出为:
apple=苹果
watermelon=西瓜
banana=香蕉
peach=桃子
使用中のLinkedHashMap と HashMap の違いは、LinkedHashMap が順序付けられていることです。上記の例は、挿入順に従ってソートされています。また、LinkedHashMap には、これを基にアクセス順 (get、put) でソートするかどうかを決定するパラメーターもあります. 覚えておいてください、挿入順序に基づいてソートされます. 後でソースコードを読んだ後でその理由がわかります. 例を見てみましょう:
public static void main(String[] args) {
Map<String, String> map = new LinkedHashMap<String, String>(16,0.75f,true);
map.put("apple", "苹果");
map.put("watermelon", "西瓜");
map.put("banana", "香蕉");
map.put("peach", "桃子");
map.get("banana");
map.get("apple");
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
System.out.println(entry.getKey() + "=" + entry.getValue());
}
}
输出为:
watermelon=西瓜
peach=桃子
banana=香蕉
apple=苹果
バナナとリンゴが元の選別に基づいて選別されていることがわかります。
二、ツリーマップ
TreeMap の並べ替え規則
TreeMap のデフォルトの並べ替え規則: キーの辞書順 (昇順) に従って並べ替えます。もちろん、照合をカスタマイズすることもできます:Comparable
インターフェースを実装するか、コンストラクターを作成するときにインスタンスを指定しますComparator
。それぞれの状況を分析します。
デフォルトで並べ替え
Observationput(key,val)
メソッドは、キーが String 型の場合、compareTo
ソートのために String クラスを呼び出します。
//String中的 compareTo 方法
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
//将key转为char数组,并根据char的大小来进行判断
char c1 = v1[k];
char c2 = v2[k];
// 两个字符串前面的字符,如果遇到第一个不相等的,就直接返回 字符的ASCll差值。
if (c1 != c2) {
return c1 - c2;
}
k++;
}
// 两个字符串前面的字符相同时,就返回两个字符串长度的差值。
return len1 - len2;
}
//测试代码如下:
TreeMap<String,String> map = new TreeMap<>();
map.put("jack","good");
map.put("bob","nice");
System.out.println(map.toString());
//输出内容为:
{bob=nice, jack=good}
上記のプット注文をに変更します
map.put("bob","nice");
map.put("jack","good");
//输出内容为:
{bob=nice, jack=good}
上記の結果は、デフォルトの並べ替えルールがキー値の最初の文字に基づいて比較および判断され、小さいものから大きいものへと昇順で並べ替えられることを示しています。注: TreeMap を使用する場合、すべてのキーは Comparable インターフェースを直接的または間接的に実装する必要がありますcannot be cast to java.lang.Comparable
。もちろん、TreeMap(Comparator<? super K> comparator)
コンストラクタを直接使用することも可能です。
Comparable インターフェイスによる並べ替え
キーComparable
インターフェイスを実装し、その中のメソッドを書き直してcompareTo
、並べ替えルールを定義します。生徒のクラスを作成し、年齢別に並べ替えます。
class Student implements Comparable<Student>{
String name;
int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int compareTo(Student o) {
if(this.age<o.age){
return -1;
}else if(this.age>o.age){
return 1;
}
return 0;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
上記の規則は次のとおりです。現在の要素が比較要素より小さい場合は -1 が返され、1 より大きい場合は 0 が返されます。TreeMap の put メソッドのロジックでは、0 より小さいものは左側に、0 より大きいものは右側に配置し、等しいものは上書きすることで昇順ソートを実現しています。降順で、カスタマイズで -1 と 1 を置き換えるだけで十分です。以下は、put ソース コードのロジックの一部です。
do {
parent = t;
cmp = k.compareTo(t.key);//调用我们实现的规则
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
テストコードは次のとおりです。
public class TreeMapTest {
public static void main(String[] args) {
TreeMap<Student,Integer> map = new TreeMap<>();
for(int i=0;i<5;i++){
Student student = new Student();
student.setName("A"+new Random().nextInt(100));
//用来判断相等的情况
if(i>=3){
student.setAge(8);
}
else
student.setAge(new Random().nextInt(30));
map.put(student,Integer.valueOf(i));
}
for(Map.Entry< Student,Integer> entry:map.entrySet()){
System.out.println(entry.getKey()+"--->"+entry.getValue());
}
}
}
//执行结果如下:
Student{name='A3', age=4}--->2
Student{name='A81', age=8}--->4
Student{name='A60', age=9}--->1
Student{name='A4', age=16}--->0
結果から、年齢の値が等しい場合、3 に等しいものは 4 に等しいものによってカバーされていることがわかります。
Comparator インターフェイスを使用してソートする
Comparable
キー値を使用してインターフェイスを実装したくない場合は、TreeMap オブジェクトを作成するときに並べ替え規則を渡すことができます。つまり、Comparator
インターフェイスを実装します。
public class TreeMapTest01 {
public static void main(String[] args) {
TreeMap<Computer,String> map = new TreeMap<>(new Comparator<Computer>() {
@Override
public int compare(Computer o1, Computer o2) {
if(o1.id>o2.id){
return 1;
}else if(o1.id<o2.id){
return -1;
}
return 0;
}
});
map.put(new Computer("er",1),"er");
map.put(new Computer("lenovo",2),"lenovo");
System.out.println(map.toString());
}
}
class Computer{
String name;
int id;
public Computer(String name,int id){
this.name=name;
this.id=id;
}
@Override
public String toString() {
return "Computer{" +
"name='" + name + '\'' +
", id=" + id +
'}';
}
}
上記のテスト クラスは、Computer
id に従って並べ替えられ、匿名の内部クラスによって実装されます。このようにルールを 1 つだけ書くことには何の問題もありません. 時々, 異なるルールの次元に従ってソートする必要があります. これは書くのが美しくありません. 最良の方法は, 異なるルールクラスを書くことです. その時が来たら, どちらを使っても構いません.直接必要なもの。上記のメソッドを次のように変更します。
public class TreeMapTest01 {
public static void main(String[] args) {
TreeMap<Computer,String> map = new TreeMap<>(new ComputerSort02());
map.put(new Computer("er",1),"er");
map.put(new Computer("lenovo",2),"lenovo");
System.out.println(map.toString());
}
}
class Computer{
String name;
int id;
public Computer(String name,int id){
this.name=name;
this.id=id;
}
@Override
public String toString() {
return "Computer{" +
"name='" + name + '\'' +
", id=" + id +
'}';
}
}
//我要根据id排
class ComputerSort01 implements Comparator<Computer>{
@Override
public int compare(Computer o1, Computer o2) {
if(o1.id>o2.id){
return 1;
}else if(o1.id<o2.id){
return -1;
}
return 0;
}
}
//我要根据name长度排
class ComputerSort02 implements Comparator<Computer>{
@Override
public int compare(Computer o1, Computer o2) {
if(o1.name.equals(o2.name))
return 0;
return o1.name.length()- o2.name.length();
}
}
現れた落とし穴
重複したキーがマップに表示される
マップでキーが同じであることがわかった場合は、並べ替えルールが間違って記述されていないかどうかを注意深く確認する必要があります返回-1往左靠,返回1往又靠,返回0就覆盖
。キーが繰り返される理由は、等しいと判断された場合にルールが 0 を返さないためです。
マップには明らかに値がありますが、null を返します
これも上記が原因で、map の get メソッドも比較を使ってデータを取得しており、戻り値が 0 の場合のみデータを取得でき、そうでない場合は null を返します。ソースコードの一部は次のとおりです。
final Entry<K,V> getEntryUsingComparator(Object key) {
@SuppressWarnings("unchecked")
K k = (K) key;
Comparator<? super K> cpr = comparator;
if (cpr != null) {
Entry<K,V> p = root;
while (p != null) {
int cmp = cpr.compare(k, p.key);//自定义的比较器
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p; //只有在相等的时候才会返回
}
}
return null;
}
ツリーマップと言う
TreeMap の最下層は赤黒木で実現されます. 赤黒木の原理は導入しません. 要素を配置するとき, バイナリ法を使用して同じキーがあるかどうかを調べます. そうでない場合は要素赤黒木に配置され、子ノードの 1 つに、赤黒木の自己均衡プロセスによって順序付けられた木が生成されます。get で要素を取得すると、ツリーのノードも二分法で検索し、自分のノードが見つかるまでキー比較で 0 を返した場合は見つかったことを意味し、値 value を返します。ヌル。