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

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

一、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 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3187 引用 • 8213 回帖

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • Flutter

    Flutter 是谷歌的移动 UI 框架,可以快速在 iOS 和 Android 上构建高质量的原生用户界面。 Flutter 可以与现有的代码一起工作,它正在被越来越多的开发者和组织使用,并且 Flutter 是完全免费、开源的。

    39 引用 • 92 回帖 • 1 关注
  • Gitea

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

    4 引用 • 16 回帖 • 5 关注
  • CSS

    CSS(Cascading Style Sheet)“层叠样式表”是用于控制网页样式并允许将样式信息与网页内容分离的一种标记性语言。

    198 引用 • 550 回帖
  • OnlyOffice
    4 引用 • 3 关注
  • 笔记

    好记性不如烂笔头。

    308 引用 • 793 回帖 • 1 关注
  • Log4j

    Log4j 是 Apache 开源的一款使用广泛的 Java 日志组件。

    20 引用 • 18 回帖 • 31 关注
  • Hexo

    Hexo 是一款快速、简洁且高效的博客框架,使用 Node.js 编写。

    21 引用 • 140 回帖 • 1 关注
  • jsDelivr

    jsDelivr 是一个开源的 CDN 服务,可为 npm 包、GitHub 仓库提供免费、快速并且可靠的全球 CDN 加速服务。

    5 引用 • 31 回帖 • 58 关注
  • Angular

    AngularAngularJS 的新版本。

    26 引用 • 66 回帖 • 537 关注
  • RESTful

    一种软件架构设计风格而不是标准,提供了一组设计原则和约束条件,主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

    30 引用 • 114 回帖 • 2 关注
  • Bug

    Bug 本意是指臭虫、缺陷、损坏、犯贫、窃听器、小虫等。现在人们把在程序中一些缺陷或问题统称为 bug(漏洞)。

    75 引用 • 1737 回帖 • 5 关注
  • Windows

    Microsoft Windows 是美国微软公司研发的一套操作系统,它问世于 1985 年,起初仅仅是 Microsoft-DOS 模拟环境,后续的系统版本由于微软不断的更新升级,不但易用,也慢慢的成为家家户户人们最喜爱的操作系统。

    222 引用 • 473 回帖 • 1 关注
  • BAE

    百度应用引擎(Baidu App Engine)提供了 PHP、Java、Python 的执行环境,以及云存储、消息服务、云数据库等全面的云服务。它可以让开发者实现自动地部署和管理应用,并且提供动态扩容和负载均衡的运行环境,让开发者不用考虑高成本的运维工作,只需专注于业务逻辑,大大降低了开发者学习和迁移的成本。

    19 引用 • 75 回帖 • 642 关注
  • 创造

    你创造的作品可能会帮助到很多人,如果是开源项目的话就更赞了!

    179 引用 • 995 回帖
  • CodeMirror
    1 引用 • 2 回帖 • 129 关注
  • MyBatis

    MyBatis 本是 Apache 软件基金会 的一个开源项目 iBatis,2010 年这个项目由 Apache 软件基金会迁移到了 google code,并且改名为 MyBatis ,2013 年 11 月再次迁移到了 GitHub。

    170 引用 • 414 回帖 • 387 关注
  • Electron

    Electron 基于 Chromium 和 Node.js,让你可以使用 HTML、CSS 和 JavaScript 构建应用。它是一个由 GitHub 及众多贡献者组成的活跃社区共同维护的开源项目,兼容 Mac、Windows 和 Linux,它构建的应用可在这三个操作系统上面运行。

    15 引用 • 136 回帖 • 1 关注
  • Python

    Python 是一种面向对象、直译式电脑编程语言,具有近二十年的发展历史,成熟且稳定。它包含了一组完善而且容易理解的标准库,能够轻松完成很多常见的任务。它的语法简捷和清晰,尽量使用无异义的英语单词,与其它大多数程序设计语言使用大括号不一样,它使用缩进来定义语句块。

    543 引用 • 672 回帖 • 1 关注
  • Postman

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

    4 引用 • 3 回帖 • 3 关注
  • Android

    Android 是一种以 Linux 为基础的开放源码操作系统,主要使用于便携设备。2005 年由 Google 收购注资,并拉拢多家制造商组成开放手机联盟开发改良,逐渐扩展到到平板电脑及其他领域上。

    334 引用 • 323 回帖
  • FreeMarker

    FreeMarker 是一款好用且功能强大的 Java 模版引擎。

    23 引用 • 20 回帖 • 463 关注
  • DevOps

    DevOps(Development 和 Operations 的组合词)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合。

    47 引用 • 25 回帖
  • SQLServer

    SQL Server 是由 [微软] 开发和推广的关系数据库管理系统(DBMS),它最初是由 微软、Sybase 和 Ashton-Tate 三家公司共同开发的,并于 1988 年推出了第一个 OS/2 版本。

    21 引用 • 31 回帖
  • 禅道

    禅道是一款国产的开源项目管理软件,她的核心管理思想基于敏捷方法 scrum,内置了产品管理和项目管理,同时又根据国内研发现状补充了测试管理、计划管理、发布管理、文档管理、事务管理等功能,在一个软件中就可以将软件研发中的需求、任务、bug、用例、计划、发布等要素有序的跟踪管理起来,完整地覆盖了项目管理的核心流程。

    6 引用 • 15 回帖 • 114 关注
  • Swagger

    Swagger 是一款非常流行的 API 开发工具,它遵循 OpenAPI Specification(这是一种通用的、和编程语言无关的 API 描述规范)。Swagger 贯穿整个 API 生命周期,如 API 的设计、编写文档、测试和部署。

    26 引用 • 35 回帖 • 1 关注
  • Pipe

    Pipe 是一款小而美的开源博客平台。Pipe 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    132 引用 • 1114 回帖 • 124 关注
  • Maven

    Maven 是基于项目对象模型(POM)、通过一小段描述信息来管理项目的构建、报告和文档的软件项目管理工具。

    186 引用 • 318 回帖 • 302 关注