Java 集合体系总结 Set、List、Map、Queue

本贴最后更新于 2315 天前,其中的信息可能已经沧海桑田

一、java 集合类基本概念

有时我们需要集中存放多个数据,一般情况下,数组就是一个很好的选择,前提是我们事先已经明确知道我们将要保存的对象的数量。一旦在数组初始化时指定了这个数组长度。这样数组长度就不可变了,如果我们想要保存一个可以动态增长的数据,java 集合类就是一个很好的设计方案。

集合类主要负责保存其他数据,所以集合类一般也被成为容器类。所以的集合类都位于 java.util 包下。

  1. Collection

一组服从某种规则的元素   1.1) List 必须保持元素特定的顺序   1.2) Set 不能有重复元素   1.3) Queue 保持一个队列(先进先出)的顺序 2) Map

一组成对的"键值对"对象

Collection 和 Map 的区别在于容器中每个位置保存元素的个数

(1)Collection 每个位置只能保存一个元素 (2)Map 保存的是“键值对”,就像一个小型数据库。我们可以通过键找到对应的值

二、Java 集合类架构层次关系

1.Iterface Iterable

迭代器接口,这是 Collection 类的父接口。实现这个 Iterable 接口的对象允许使用 foreach 进行遍历,也就是说,所有的 Collection 集合对象都具有“foreach 可遍历性”。这个 Iterable 接口只有一个方法:iterator()。他返回一个代表当前集合对象的泛型迭代器,用于之后的遍历操作。

1.1 Collection

Collection 是最基本的集合接口,一个 Collection 代表一组 Object 的集合,这些 Object 被称作 Collection 的元素。Collection 是一个接口,用以提供规范定义,不能被实例化使用

1)Set

Set 集合类似于一个桶,放进 Set 集合里的多个对象之间没有明显的顺序。Set 继承自 Collection 接口,不能包含有重复元素。

Set 判断两个对象相同不是使用”==“运算符,而是根据 equals 方法。也就是说,我们在加入一个新元素时,如果这个新元素对象和 Set 中已有对你好进行注意 equals 比较都返回 false,则 Ser 就会接受这个新元素对象,否则拒绝。

因为 Set 的这个制约,在使用 Set 集合时,应该注意两点:1 是 Set 集合里的元素的实现类实现一个有效的 equals(Object)方法 2 对 Set 的构造函数,传入的 Collection 参数不能包含重复的元素

1.1)HashSet

HastSet 是 Set 接口的典型实现,HashSet 使用 HASH 算法来存储集合中的元素,因此具有良好的存取和查找性能。当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode()方法来得到该对象的 hashCode 值,然后根据该 HashCode 值决定该对象在 HashSet 的存储位置。

1.1.1)LinkedHashSet

LinkedHashSet 集合也是根据元素的 hashCode 值来决定元素的存储位置,但和 HashSet 不同是,它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存。

当遍历 LinkedHashSet 集合里的元素是,LinkedHashSet 将会按元素的添加顺序来访问集合里的元素。

LinkedHashSet 需要维护元素的插入顺序,因此性能略低于 HashSet 的性能,但在迭代访问 Set 全部元素时,将会有很好的性能。

1.2)SortedSet

此接口主要用于排序操作,即实现此接口的子类都属于排序的子类

1.2.1)TreeSet

TreeSet 是 Sorted 接口的实现类,TreeSet 可以确保集合元素属于排序状态

1.3)EnumSet

EnumSet 是一个专门为美剧类设计的集合类,EnumSet 中所有元素都必须是指定悲剧类型的的枚举值,该枚举类型在创建 Enumset 时显示或隐式的指定。EnumSet 的集合元素也是有序的。

2)List

List 集合代表一个元素有序,可重复的集合,集合中每个元素都有其对应的顺序索引。List 集合允许加入重复元素,因为他可以通过索引来访问指位置的集合元素,list 集合默认按元素的添加顺序设置元素的索引

2.1)ArrayList

ArrayList 是基于数组实现的 List 类,他封装了一个动态的增长的,允许再分配的 Object[]数组。

2.2)Vector

Vector 和 ArrayList 在用法上几乎完全相同,但由于 Vector 是一个古老的集合,所以 Vector 提供一些方法名很长的方法,之后将 Vector 改为实现 List 接口,统一归入集合框架体系铜

2.2.1)Stack

Stack 是 Vector 提供的一个子类,用于模拟栈这种数据结构

2.3)LinkedList

implement List,Deque。实现 List 接口,能对他进行队列操作,即可以根据索引来随机访问集合中元素。同时他还实现 Deque 接口,即能将 LinkedList 当做双端队列使用。自然也可以被当做“栈来使用”。

1.2Map

Map 用于保存具有“映射关系”数据,因此 Map 集合里保存着两组值,一组值用于保存 Map 里的 key,另外一组值用于保存 Map 里的 value。key 和 value 都可以是任何引用类型的数据。Map 的 key 不允许重复,即同一个 Map 对象的任何两个 Key 通过 equals 方法比较结果总是返回 false;

Map 的这些实现类和子接口中 key 集的存储形式和 Set 集合完全相同(即 key 不能重复)

Map 的这些实现类和子接口中 value 集的存储形式和 List 非常类似(即 value 可以重复,根据索引来查找)

1)HashMap

和 HashSet 不能保证元素的顺序一样,HashMap 也不能保证 key-value 对的顺序。并且类似于 HashSet 判断两个 key 是否相等的标准也是:两个 key 通过 equals()方法比较返回 true。

同时两个 key 的 hashCode 值也必须相等。

1.1)LinkedHashMap

LinkedHashMap 也使用双向链表来维护 key-value 对的顺序,与 key-value 对的插入顺序一致(注意和 TreeMap 对所有的 key-value 进行排序进行区份)

2)HashTable

2.1)properties

3)sortedMap

正如 Ser 接口派生出 SortedSet 子接口,SortedSet 接口有一个 TreeSet 实现类一样,Map 接口也派生出一个 SortedMap 实现类

3.1)TreeMap

TreeMap 就是一个红黑树数据结构,每一个 key-value 对即作为红黑树一个节点。TreeMap 存储 key-value 对(节点)时,需要根据 key 对及诶单进行排序。TreeMap 可以包含保证所有的 key-value 对都处于有序状态。同样,TreeMap 也有两种排序方式:自然排序,定制排序

3.Java 集合类的 Demo

1.Set

HashSet

import java.util.*

//类 A 的 equals()方法总是返回 true,但没有重写其 hashCode()方法。不能保证当前对象是 HashSet 的唯一对象

class A { public bollean equals(Object obj) { return true;

} } //类 B 的 hashCode()方法总是返回 true,但没有重写其 equals()方法。不能保证当前对象是 HashSet 的唯一对象 class B { public bollean hashCode(Object obj) { return 1;

} } //类 C 的 hashCode()方法总是返回 2,且有重写其 equals()方法 class C { public int hashCode() { return 2; } public boolean equals(Object obj) { return true; } }

public class HashSetTest { public static void main(String[] args) { HashSet books=new HashSet();

     //分别向books集合中添加两个A对象,两个B对象,两个C对象

    books.add(new A());
    books.add(new A());

    books.add(new B());
    books.add(new B());

    books.add(new C());
    books.add(new C());
    System.out.println(books);

} }

结果

[B@1, B@1, C@2, A@3bc257, A@785d65]

可以看出,如果两个对象通过 equals()方法比较返回 true,但这两个对象的 hashCode 方法返回不同的 hashCode 值时,这将导致 HashSet 会把这两个对象保存在 Hash 表的不同位置,从而使对象可以添加成功,这就与 Set 集合的规则有些出入了。所以,我们要明确的是:equals()决定是否可以加入 HashSet,而 hashCode()决定存放的位置,他们两者必须同时满足才能允许一个新元素加入 HashSet。

但是要注意的是:如果两个对象的 hashCode 相同,但是他们的 equals 返回值不同,HashSet 会在这个位置用链式结构来保存多个对象。而 HashSet 访问集合元素时也是根据元素的 HashCode 的值来快速定位的,这种链式结构会导致性能下降。

所以如果需要把某个类的对象保存到 HashSet 集合中,我们在重写这个类的 equasl()方法和 hashCode()方法时,应该尽量保证两个对象通过 equals()方法毕节哦返回 true 时,他们 hashCode()方法返回值也相等。

LinkedHashSet

import java.util.*; public class LinkedHashSetTest { public static void main(String[] args) { LinkedHashSet books=new LinkedHashSet(); books.add('Java1'); books.add('Java2');
System.out.println(books); //删除 Java1 books.remove("Java1"); //重新添加 Java1 books.add("Java1"); System.out.println(books); } }

输出

[Java1, Java2] [Java1, Java2]

元素顺序总是与添加顺序一致,同时要明白的是,LinkedHashSetTest 是 HashSet 的子类,因为它不允许集合元素重复

TreeSet

import java.util.*;

public class Test { public static void main(String[] args) { TreeSet nums = new TreeSet(); //向 TreeSet 中添加四个 Integer 对象 nums.add(5); nums.add(2); nums.add(10); nums.add(-9);

    //输出集合元素,看到集合元素已经处于排序状态
    System.out.println(nums);
    [-9, 2, 5, 10]
    //输出集合里的第一个元素
    System.out.println(nums.first());
    -9
    //输出集合里的最后一个元素
    System.out.println(nums.last());
    10
    //返回小于4的子集,不包含4
    System.out.println(nums.headSet(4));
    [-9, 2]
    //返回大于5的子集,如果Set中包含5,子集中还包含5
    System.out.println(nums.tailSet(5));
    [5, 10]
    //返回大于等于-3,小于4的子集。
    System.out.println(nums.subSet(-3 , 4));
    [2]
}

}

与 HashSet 集合采用 hash 算法来决定元素的存储位置不同,TreeSet 采用红黑树的数据结构来存储集合元素。TreeSet 支持两种排序方式:自然排序,定制排序

1.自然排序

TreeSet 会调用集合元素的 compareTo(Object obj)方法来比较元素之间的太小关系,然后将集合元素按升序排序,即自然排序,如果试图把一个对象添加到 TreeSet 时,则该对象的类必须实现 Comparable 接口,否则程序会抛出异常。

当把一个对象加入 TreeSet 集合中时,TreeSet 会调用该对象的 compareTo(Object obj)方法与容器中的其他对象比较大小,然后根据红黑树结构找到他的存储位置。如果两个对象通过 compareTo(Object obj)方法比较相等,新对象将无法添加到 TreeSet 集合中(牢记 Set 不允许重复的概念)。

注意:当需要把一个对象放入 TreeSet 中,重写改对象对应类的 equals()方法是,应该保证该方法时,应该保证该方法与 compareTo(Object obj)方法有一致的结果,即如果有两个对象通过 equals()方法比较返回 true 时,这两个对象通过 compareTo(Object obj)方法比较结果应该也为 0(即相等)

对与 Set 来说,它定义了 equals()为唯一性判断的标准,而对于到了具体的实现,HashSet、TreeSet 来说,它们又会有自己特有的唯一性判断标准,只有同时满足了才能判定为唯一性

2.定制排序

TreeSet 的自然排序是根据集合元素的大小,TreeSet 将它们以升序排序。如果我们需要实现定制排序,则可以通过 Comparator 接口。该接口里包含一个 int compare(to1,to2)方法,该方法用户比较大小。

import java.util.*;

class M { int age; public M(int age) { this.age = age; } public String toString() { return "M[age:" + age + "]"; } }

public class Test { public static void main(String[] args) { TreeSet ts = new TreeSet(new Comparator() { //根据 M 对象的 age 属性来决定大小 public int compare(Object o1, Object o2) { M m1 = (M)o1; M m2 = (M)o2; return m1.age > m2.age ? -1 : m1.age < m2.age ? 1 : 0; } }); ts.add(new M(5)); ts.add(new M(-3)); ts.add(new M(9)); System.out.println(ts); } }

EnumSet

import java.util.*;

enum Season { SPRING,SUMMER,FALL,WINTER } public class EnumSetTest { public static void main(String[] args) { //创建一个 EnumSet 集合,集合元素就是 Season 枚举类的全部枚举值 EnumSet es1 = EnumSet.allOf(Season.class); //输出[SPRING,SUMMER,FALL,WINTER] System.out.println(es1);

    //创建一个EnumSet空集合,指定其集合元素是Season类的枚举值。
    EnumSet es2 = EnumSet.noneOf(Season.class); 
    //输出[]
    System.out.println(es2); 
    //手动添加两个元素
    es2.add(Season.WINTER);
    es2.add(Season.SPRING);
    //输出[SPRING,WINTER]
    System.out.println(es2);

    //以指定枚举值创建EnumSet集合
    EnumSet es3 = EnumSet.of(Season.SUMMER , Season.WINTER); 
    //输出[SUMMER,WINTER]
    System.out.println(es3);

    EnumSet es4 = EnumSet.range(Season.SUMMER , Season.WINTER); 
    //输出[SUMMER,FALL,WINTER]
    System.out.println(es4);

    //新创建的EnumSet集合的元素和es4集合的元素有相同类型,
    //es5的集合元素 + es4集合元素 = Season枚举类的全部枚举值
    EnumSet es5 = EnumSet.complementOf(es4); 
    //输出[SPRING]
    System.out.println(es5);
}

}

输出

[SPRING, SUMMER, FALL, WINTER] [] [SPRING, WINTER] [SUMMER, WINTER] [SUMMER, FALL, WINTER] [SPRING]

以上是 Set 集合类的 Demo,下面讲讲如何选择这些集合类呢?

(1)HashSet 的性能总是比 TreeSet 好(贴别是最常用的添加、查询元素等操作),因为 TreeSet 需要额外的红黑树算法来维护集合元素的次序。只有当需要一个保持排序的 Set 时,才应该使用 TreeSet,否则都应该使用 HashSet

(2)对于普通的插入,删除操作,LinkedHashSet 比 HashSet 略慢一线,这是由于维护链表所带来的开销造成的。不过,因为有了链接的存在,遍历 LinkedHashSet 会更快

(3)EnumSet 是所有 Set 实现类中性能最好的,但它只能保存一个枚举类的枚举值作为集合元素。

(4)HashSet,TreeSet,EnumSet 都是“线程不安全”的。

2.List

ArrayList

如果一开始就知道 ArrayList 集合需要保存多少元素,则可以在创建他们时就指定大小,这样可以减少重新分配的次数,提供性能,ArrayList 还提供了如下方法来重新分配 Object[]数组。

  1. ensureCapacity(int minCapacity): 将 ArrayList 集合的 Object[]数组长度增加 minCapacity
  2. trimToSize(): 调整 ArrayList 集合的 Object[]数组长度为当前元素的个数。 程序可以通过此方法来减少 ArrayList 集合对象占用的内存空间

import java.util.*;

public class Test { public static void main(String[] args) { List books = new ArrayList(); //向 books 集合中添加三个元素 books.add(new String("轻量级 Java EE 企业应用实战")); books.add(new String("疯狂 Java 讲义")); books.add(new String("疯狂 Android 讲义")); System.out.println(books);

    //将新字符串对象插入在第二个位置
    books.add(1, new String("疯狂Ajax讲义"));
    for (int i = 0; i < books.size(); i++) {
        System.out.println(books.get(i));
    }

    //删除第三个元素
    books.remove(2);
    System.out.println(books);

    //判断指定元素在List集合中位置:输出1,表明位于第二位
    System.out.println(books.indexOf(new String("疯狂Ajax讲义")));  //①
    //将第二个元素替换成新的字符串对象
    books.set(1, new String("LittleHann"));
    System.out.println(books);

    //将books集合的第二个元素(包括)
    //到第三个元素(不包括)截取成子集合
    System.out.println(books.subList(1, 2));
}

}

输出

[轻量级 Java EE 企业应用实战, 疯狂 Java 讲义, 疯狂 Android 讲义] 轻量级 Java EE 企业应用实战 疯狂 Ajax 讲义 疯狂 Java 讲义 疯狂 Android 讲义 [轻量级 Java EE 企业应用实战, 疯狂 Ajax 讲义, 疯狂 Android 讲义] 1 [轻量级 Java EE 企业应用实战, LittleHann, 疯狂 Android 讲义] [LittleHann]

Stack

注意 Stack 的后进先出的特点

import java.util.*;

public class Test { public static void main(String[] args) { Stack v = new Stack(); //依次将三个元素 push 入"栈" v.push("疯狂 Java 讲义"); v.push("轻量级 Java EE 企业应用实战"); v.push("疯狂 Android 讲义");

    //输出:[疯狂Java讲义, 轻量级Java EE企业应用实战 , 疯狂Android讲义]
    System.out.println(v);

    //访问第一个元素,但并不将其pop出"栈",输出:疯狂Android讲义
    System.out.println(v.peek());

    //依然输出:[疯狂Java讲义, 轻量级Java EE企业应用实战 , 疯狂Android讲义]
    System.out.println(v);

    //pop出第一个元素,输出:疯狂Android讲义
    System.out.println(v.pop());

    //输出:[疯狂Java讲义, 轻量级Java EE企业应用实战]
    System.out.println(v);
}

}

输出

[疯狂 Java 讲义, 轻量级 Java EE 企业应用实战, 疯狂 Android 讲义] 疯狂 Android 讲义 [疯狂 Java 讲义, 轻量级 Java EE 企业应用实战, 疯狂 Android 讲义] 疯狂 Android 讲义 [疯狂 Java 讲义, 轻量级 Java EE 企业应用实战]

LinkedList

import java.util.*;

public class Test { public static void main(String[] args) { LinkedList books = new LinkedList();

    //将字符串元素加入队列的尾部(双端队列)
    books.offer("疯狂Java讲义");

    //将一个字符串元素加入栈的顶部(双端队列)
    books.push("轻量级Java EE企业应用实战");

    //将字符串元素添加到队列的头(相当于栈的顶部)
    books.offerFirst("疯狂Android讲义");

    for (int i = 0; i < books.size() ; i++ )
    {
        System.out.println(books.get(i));
    }

    //访问、并不删除栈顶的元素
    System.out.println(books.peekFirst());
    //访问、并不删除队列的最后一个元素
    System.out.println(books.peekLast());
    //将栈顶的元素弹出"栈"
    System.out.println(books.pop());
    //下面输出将看到队列中第一个元素被删除
    System.out.println(books);
    //访问、并删除队列的最后一个元素
    System.out.println(books.pollLast());
    //下面输出将看到队列中只剩下中间一个元素:
    //轻量级Java EE企业应用实战
    System.out.println(books);
}

}

输出

疯狂 Android 讲义 轻量级 Java EE 企业应用实战 疯狂 Java 讲义 疯狂 Android 讲义 疯狂 Java 讲义 疯狂 Android 讲义 [轻量级 Java EE 企业应用实战, 疯狂 Java 讲义] 疯狂 Java 讲义 [轻量级 Java EE 企业应用实战]

Queue

import java.util.*;

public class PriorityQueueTest { public static void main(String[] args) { PriorityQueue pq = new PriorityQueue(); //下面代码依次向 pq 中加入四个元素 pq.offer(6); pq.offer(-3); pq.offer(9); pq.offer(0);

    //输出pq队列,并不是按元素的加入顺序排列,
    //而是按元素的大小顺序排列,输出[-3, 0, 9, 6]
    System.out.println(pq);
    //访问队列第一个元素,其实就是队列中最小的元素:-3
    System.out.println(pq.poll());
}

}

PriorityQueue 不允许插入 null 元素,它还需要对队列元素进行排序,PriorityQueue 的元素有两种排序方式

  1. 自然排序: 采用自然顺序的 PriorityQueue 集合中的元素对象都必须实现了 Comparable 接口,而且应该是同一个类的多个实例,否则可能导致 ClassCastException 异常

  2. 定制排序 创建 PriorityQueue 队列时,传入一个 Comparator 对象,该对象负责对队列中的所有元素进行排序 关于自然排序、定制排序的原理和之前说的 TreeSet 类似

ArrayDeque

import java.util.*;

public class Test { public static void main(String[] args) { ArrayDeque stack = new ArrayDeque(); //依次将三个元素 push 入"栈" stack.push("疯狂 Java 讲义"); stack.push("轻量级 Java EE 企业应用实战"); stack.push("疯狂 Android 讲义");

    //输出:[疯狂Java讲义, 轻量级Java EE企业应用实战 , 疯狂Android讲义]
    System.out.println(stack);

    //访问第一个元素,但并不将其pop出"栈",输出:疯狂Android讲义
    System.out.println(stack.peek());

    //依然输出:[疯狂Java讲义, 轻量级Java EE企业应用实战 , 疯狂Android讲义]
    System.out.println(stack);

    //pop出第一个元素,输出:疯狂Android讲义
    System.out.println(stack.pop());

    //输出:[疯狂Java讲义, 轻量级Java EE企业应用实战]
    System.out.println(stack);
}

}

[疯狂 Android 讲义, 轻量级 Java EE 企业应用实战, 疯狂 Java 讲义] 疯狂 Android 讲义 [疯狂 Android 讲义, 轻量级 Java EE 企业应用实战, 疯狂 Java 讲义] 疯狂 Android 讲义 [轻量级 Java EE 企业应用实战, 疯狂 Java 讲义]

以上就是 List 集合类的编程应用场景。我们来梳理一下思路

java 提供的 List 就是一个“线性表接口”,ArrayList(基于数组的线性表),LinkedList(基于链的线性表)是线性表的两种典型实现

Queue 代表了队列,Deque 代表了双端队列(即可以作为队列使用,也可以作为栈使用)

因为数组以一块连续内存来保存所有的数组元素,所以数组在随机访问时性能最好。

内部以链表作为底层实现的集合在执行插入,删除操作时有很好的的性能

遍历

我们之前说过,Collection 接口继承了 Iterable 接口,也就是说,我们以上学习到的所有的 Collection 集合类都具有"可遍历性"

Iterable 接口也是 java 集合框架的成员,它隐藏了各种 Collection 实现类的底层细节,向应用程序提供了遍历 Collection 集合元素的统一编程接口:

  1. boolean hasNext(): 是否还有下一个未遍历过的元素
  2. Object next(): 返回集合里的下一个元素
  3. void remove(): 删除集合里上一次 next 方法返回的元素 iteration

import java.util.*;

public class Test { public static void main(String[] args) { //创建一个集合 Collection books=new HashSet(); books.add("1"); books.add("2"); books.add("3"); //获取 books 集合对应的迭代器 Iterator it=books.iterator(); while(it.hasNext()) { String book=(String)it.next(); System.out.println(book); if (book.equals("2")) { //从集合中删除上一次 next 方法返回的元素 it.remove(); } //对 book 变量赋值,不会改变集合元素本身 book = "测试字符串"; } System.out.println(books);

}

}

输出

3 2 1 [3, 1]

从代码可以看出,iterator 必须依附于 Collection 对象,若有一个 iterator 对象,必然有一个与之关联的 Collection 对象。

除了可以使用 iterator 接口迭代访问 Collection 集合里的元素之外,使用 java5 提供的 foreach 循环迭代访问集合元素更加便捷

foreach 实现遍历

import java.util.*;

public class Test { public static void main(String[] args) { //创建一个集合 Collection books = new HashSet(); books.add(new String("1")); books.add(new String("2")); books.add(new String("3"));

    for (Object obj : books)
    {
        //此处的book变量也不是集合元素本身
        String book = (String)obj;
        System.out.println(book);
        if (book.equals("2"))
        {
            //下面代码会引发ConcurrentModificationException异常
            //books.remove(book);
        }
    }
    System.out.println(books);
}

}

输出

3 2 1 [3, 2, 1]

Map

HashMap,HashTable

import java.util.*;

class A { int count; public A(int count) { this.count = count; } //根据 count 的值来判断两个对象是否相等。 public boolean equals(Object obj) { if (obj == this) return true; if (obj!=null && obj.getClass()==A.class) { A a = (A)obj; return this.count == a.count; } return false; } //根据 count 来计算 hashCode 值。 public int hashCode() { return this.count; } } class B { //重写 equals()方法,B 对象与任何对象通过 equals()方法比较都相等 public boolean equals(Object obj) { return true; } } public class Test { public static void main(String[] args) { Hashtable ht = new Hashtable(); ht.put(new A(60000) , "疯狂 Java 讲义"); ht.put(new A(87563) , "轻量级 Java EE 企业应用实战"); ht.put(new A(1232) , new B()); System.out.println(ht);

    //只要两个对象通过equals比较返回true,
    //Hashtable就认为它们是相等的value。
    //由于Hashtable中有一个B对象,
    //它与任何对象通过equals比较都相等,所以下面输出true。
    System.out.println(ht.containsValue("测试字符串"));  //①

    //只要两个A对象的count相等,它们通过equals比较返回true,且hashCode相等
    //Hashtable即认为它们是相同的key,所以下面输出true。
    System.out.println(ht.containsKey(new A(87563)));   //②

    //下面语句可以删除最后一个key-value对
    ht.remove(new A(1232));    //③

    //通过返回Hashtable的所有key组成的Set集合,
    //从而遍历Hashtable每个key-value对
    for (Object key : ht.keySet())
    {
        System.out.print(key + "---->");
        System.out.print(ht.get(key) + "\n");
    }
}

}

输出

{A@ea60=疯狂 Java 讲义, A@1560b=轻量级 Java EE 企业应用实战, A@4d0=B@547c9586} true true A@ea60----> 疯狂 Java 讲义 A@1560b----> 轻量级 Java EE 企业应用实战

当使用自定义类作为 HashMap,Hashtable 的 key 时,如果重写该类的 equals(Object obj)和 hashCode()方法,则应该保证两个方法的判断标准一致-当两个 key 通过 equals()方法比较返回 true 时,两个 key 的 hashCode()的返回值也应该相同。

LinkedHashMap

import java.util.*;

public class Test { public static void main(String[] args) { LinkedHashMap scores = new LinkedHashMap(); scores.put("语文" , 80); scores.put("英文" , 82); scores.put("数学" , 76); //遍历 scores 里的所有的 key-value 对 for (Object key : scores.keySet()) { System.out.println(key + "------>" + scores.get(key)); } } }

输出

语文------>80 英文------>82 数学------>76

properties

import java.util.; import java.io.;

public class Test { public static void main(String[] args) throws Exception { Properties props = new Properties(); //向 Properties 中增加属性 props.setProperty("username" , "yeeku"); props.setProperty("password" , "123456");

    //将Properties中的key-value对保存到a.ini文件中
    props.store(new FileOutputStream("a.ini"), "comment line");   //①

    //新建一个Properties对象
    Properties props2 = new Properties();
    //向Properties中增加属性
    props2.setProperty("gender" , "male");

    //将a.ini文件中的key-value对追加到props2中
    props2.load(new FileInputStream("a.ini") );    //②
    System.out.println(props2);
}

}

输出

{password=123456, gender=male, username=yeeku}

TreeMap

import java.util.*;

class R implements Comparable { int count; public R(int count) { this.count = count; } public String toString() { return "R[count:" + count + "]"; } //根据 count 来判断两个对象是否相等。 public boolean equals(Object obj) { if (this == obj) return true; if (obj!=null && obj.getClass()==R.class) { R r = (R)obj; return r.count == this.count; } return false; } //根据 count 属性值来判断两个对象的大小。 public int compareTo(Object obj) { R r = (R)obj; return count > r.count ? 1 : count < r.count ? -1 : 0; } } public class TreeMapTest { public static void main(String[] args) { TreeMap tm = new TreeMap(); tm.put(new R(3) , "轻量级 Java EE 企业应用实战"); tm.put(new R(-5) , "疯狂 Java 讲义"); tm.put(new R(9) , "疯狂 Android 讲义");

    System.out.println(tm);

    //返回该TreeMap的第一个Entry对象
    System.out.println(tm.firstEntry());

    //返回该TreeMap的最后一个key值
    System.out.println(tm.lastKey());

    //返回该TreeMap的比new R(2)大的最小key值。
    System.out.println(tm.higherKey(new R(2)));

    //返回该TreeMap的比new R(2)小的最大的key-value对。
    System.out.println(tm.lowerEntry(new R(2)));

    //返回该TreeMap的子TreeMap
    System.out.println(tm.subMap(new R(-1) , new R(4)));
}

}

输出

{R[count:-5]=疯狂 Java 讲义, R[count:3]=轻量级 Java EE 企业应用实战, R[count:9]=疯狂 Android 讲义} R[count:-5]=疯狂 Java 讲义 R[count:9] R[count:3] R[count:-5]=疯狂 Java 讲义 {R[count:3]=轻量级 Java EE 企业应用实战}

从代码中可以看出,类似于 TreeSet 中判断两个元素是否相等的标准,TreeMap 中判断两个 key 相等的标准是

  1. 两个 key 通过 compareTo()方法返回 0
  2. equals()放回 true

EnumMap

import java.util.*;

enum Season { SPRING,SUMMER,FALL,WINTER } public class Test { public static void main(String[] args) { //创建一个 EnumMap 对象,该 EnumMap 的所有 key //必须是 Season 枚举类的枚举值 EnumMap enumMap = new EnumMap(Season.class); enumMap.put(Season.SUMMER , "夏日炎炎"); enumMap.put(Season.SPRING , "春暖花开"); System.out.println(enumMap); } }

输出

{SPRING=春暖花开, SUMMER=夏日炎炎}

与创建普通 Map 有所区别的是,创建 EnumMap 是必须指定一个枚举类,从而将该 EnumMap 和指定枚举类关联起来

以上就是 Map 集合类的编程小 demo。我们来梳理一下思路

(1)HashMap 和 Hashtable 的效率大致相同,因为它们的实现机制几乎完全一样。但 HashMap 通常比 Hashtable 要快一点,因为 Hashtable 需要二外的线程同步控制

(2)TreeMap 通常比 HashMap,Hashtable 要慢(尤其是在插入,删除 key-value 对要慢),因为 TreeMap 底层采用的

作者:Fysddsw_lc
链接:https://juejin.im/post/5a98b571518825555c1d0f8f
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3169 引用 • 8208 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • 运维

    互联网运维工作,以服务为中心,以稳定、安全、高效为三个基本点,确保公司的互联网业务能够 7×24 小时为用户提供高质量的服务。

    148 引用 • 257 回帖
  • 阿里巴巴

    阿里巴巴网络技术有限公司(简称:阿里巴巴集团)是以曾担任英语教师的马云为首的 18 人,于 1999 年在中国杭州创立,他们相信互联网能够创造公平的竞争环境,让小企业通过创新与科技扩展业务,并在参与国内或全球市场竞争时处于更有利的位置。

    43 引用 • 221 回帖 • 200 关注
  • InfluxDB

    InfluxDB 是一个开源的没有外部依赖的时间序列数据库。适用于记录度量,事件及实时分析。

    2 引用 • 59 关注
  • 宕机

    宕机,多指一些网站、游戏、网络应用等服务器一种区别于正常运行的状态,也叫“Down 机”、“当机”或“死机”。宕机状态不仅仅是指服务器“挂掉了”、“死机了”状态,也包括服务器假死、停用、关闭等一些原因而导致出现的不能够正常运行的状态。

    13 引用 • 82 回帖 • 50 关注
  • Sublime

    Sublime Text 是一款可以用来写代码、写文章的文本编辑器。支持代码高亮、自动完成,还支持通过插件进行扩展。

    10 引用 • 5 回帖
  • Gitea

    Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。

    4 引用 • 16 回帖
  • Jenkins

    Jenkins 是一套开源的持续集成工具。它提供了非常丰富的插件,让构建、部署、自动化集成项目变得简单易用。

    51 引用 • 37 回帖 • 3 关注
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    165 引用 • 407 回帖 • 514 关注
  • ActiveMQ

    ActiveMQ 是 Apache 旗下的一款开源消息总线系统,它完整实现了 JMS 规范,是一个企业级的消息中间件。

    19 引用 • 13 回帖 • 643 关注
  • 钉钉

    钉钉,专为中国企业打造的免费沟通协同多端平台, 阿里巴巴出品。

    15 引用 • 67 回帖 • 356 关注
  • danl
    89 关注
  • 尊园地产

    昆明尊园房地产经纪有限公司,即:Kunming Zunyuan Property Agency Company Limited(简称“尊园地产”)于 2007 年 6 月开始筹备,2007 年 8 月 18 日正式成立,注册资本 200 万元,公司性质为股份经纪有限公司,主营业务为:代租、代售、代办产权过户、办理银行按揭、担保、抵押、评估等。

    1 引用 • 22 回帖 • 703 关注
  • Mac

    Mac 是苹果公司自 1984 年起以“Macintosh”开始开发的个人消费型计算机,如:iMac、Mac mini、Macbook Air、Macbook Pro、Macbook、Mac Pro 等计算机。

    164 引用 • 594 回帖 • 1 关注
  • V2EX

    V2EX 是创意工作者们的社区。这里目前汇聚了超过 400,000 名主要来自互联网行业、游戏行业和媒体行业的创意工作者。V2EX 希望能够成为创意工作者们的生活和事业的一部分。

    17 引用 • 236 回帖 • 391 关注
  • DNSPod

    DNSPod 建立于 2006 年 3 月份,是一款免费智能 DNS 产品。 DNSPod 可以为同时有电信、网通、教育网服务器的网站提供智能的解析,让电信用户访问电信的服务器,网通的用户访问网通的服务器,教育网的用户访问教育网的服务器,达到互联互通的效果。

    6 引用 • 26 回帖 • 524 关注
  • Postman

    Postman 是一款简单好用的 HTTP API 调试工具。

    4 引用 • 3 回帖
  • CentOS

    CentOS(Community Enterprise Operating System)是 Linux 发行版之一,它是来自于 Red Hat Enterprise Linux 依照开放源代码规定释出的源代码所编译而成。由于出自同样的源代码,因此有些要求高度稳定的服务器以 CentOS 替代商业版的 Red Hat Enterprise Linux 使用。两者的不同在于 CentOS 并不包含封闭源代码软件。

    238 引用 • 224 回帖
  • Firefox

    Mozilla Firefox 中文俗称“火狐”(正式缩写为 Fx 或 fx,非正式缩写为 FF),是一个开源的网页浏览器,使用 Gecko 排版引擎,支持多种操作系统,如 Windows、OSX 及 Linux 等。

    7 引用 • 30 回帖 • 446 关注
  • 持续集成

    持续集成(Continuous Integration)是一种软件开发实践,即团队开发成员经常集成他们的工作,通过每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误。

    14 引用 • 7 回帖
  • SVN

    SVN 是 Subversion 的简称,是一个开放源代码的版本控制系统,相较于 RCS、CVS,它采用了分支管理系统,它的设计目标就是取代 CVS。

    29 引用 • 98 回帖 • 689 关注
  • JWT

    JWT(JSON Web Token)是一种用于双方之间传递信息的简洁的、安全的表述性声明规范。JWT 作为一个开放的标准(RFC 7519),定义了一种简洁的,自包含的方法用于通信双方之间以 JSON 的形式安全的传递信息。

    20 引用 • 15 回帖 • 19 关注
  • OkHttp

    OkHttp 是一款 HTTP & HTTP/2 客户端库,专为 Android 和 Java 应用打造。

    16 引用 • 6 回帖 • 53 关注
  • OpenShift

    红帽提供的 PaaS 云,支持多种编程语言,为开发人员提供了更为灵活的框架、存储选择。

    14 引用 • 20 回帖 • 611 关注
  • 国际化

    i18n(其来源是英文单词 internationalization 的首末字符 i 和 n,18 为中间的字符数)是“国际化”的简称。对程序来说,国际化是指在不修改代码的情况下,能根据不同语言及地区显示相应的界面。

    7 引用 • 26 回帖
  • Vim

    Vim 是类 UNIX 系统文本编辑器 Vi 的加强版本,加入了更多特性来帮助编辑源代码。Vim 的部分增强功能包括文件比较(vimdiff)、语法高亮、全面的帮助系统、本地脚本(Vimscript)和便于选择的可视化模式。

    28 引用 • 66 回帖
  • 小薇

    小薇是一个用 Java 写的 QQ 聊天机器人 Web 服务,可以用于社群互动。

    由于 Smart QQ 从 2019 年 1 月 1 日起停止服务,所以该项目也已经停止维护了!

    34 引用 • 467 回帖 • 711 关注
  • IDEA

    IDEA 全称 IntelliJ IDEA,是一款 Java 语言开发的集成环境,在业界被公认为最好的 Java 开发工具之一。IDEA 是 JetBrains 公司的产品,这家公司总部位于捷克共和国的首都布拉格,开发人员以严谨著称的东欧程序员为主。

    180 引用 • 400 回帖 • 1 关注