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

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

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

    3190 引用 • 8214 回帖 • 1 关注

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • 微软

    微软是一家美国跨国科技公司,也是世界 PC 软件开发的先导,由比尔·盖茨与保罗·艾伦创办于 1975 年,公司总部设立在华盛顿州的雷德蒙德(Redmond,邻近西雅图)。以研发、制造、授权和提供广泛的电脑软件服务业务为主。

    8 引用 • 44 回帖
  • LaTeX

    LaTeX(音译“拉泰赫”)是一种基于 ΤΕΧ 的排版系统,由美国计算机学家莱斯利·兰伯特(Leslie Lamport)在 20 世纪 80 年代初期开发,利用这种格式,即使使用者没有排版和程序设计的知识也可以充分发挥由 TeX 所提供的强大功能,能在几天,甚至几小时内生成很多具有书籍质量的印刷品。对于生成复杂表格和数学公式,这一点表现得尤为突出。因此它非常适用于生成高印刷质量的科技和数学类文档。

    12 引用 • 54 回帖 • 49 关注
  • OkHttp

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

    16 引用 • 6 回帖 • 75 关注
  • SEO

    发布对别人有帮助的原创内容是最好的 SEO 方式。

    35 引用 • 200 回帖 • 27 关注
  • 阿里云

    阿里云是阿里巴巴集团旗下公司,是全球领先的云计算及人工智能科技公司。提供云服务器、云数据库、云安全等云计算服务,以及大数据、人工智能服务、精准定制基于场景的行业解决方案。

    89 引用 • 345 回帖 • 1 关注
  • 周末

    星期六到星期天晚,实行五天工作制后,指每周的最后两天。再过几年可能就是三天了。

    14 引用 • 297 回帖
  • InfluxDB

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

    2 引用 • 76 关注
  • C++

    C++ 是在 C 语言的基础上开发的一种通用编程语言,应用广泛。C++ 支持多种编程范式,面向对象编程、泛型编程和过程化编程。

    107 引用 • 153 回帖
  • Python

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

    545 引用 • 672 回帖
  • CAP

    CAP 指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。

    11 引用 • 5 回帖 • 612 关注
  • 反馈

    Communication channel for makers and users.

    123 引用 • 913 回帖 • 250 关注
  • 支付宝

    支付宝是全球领先的独立第三方支付平台,致力于为广大用户提供安全快速的电子支付/网上支付/安全支付/手机支付体验,及转账收款/水电煤缴费/信用卡还款/AA 收款等生活服务应用。

    29 引用 • 347 回帖 • 5 关注
  • 人工智能

    人工智能(Artificial Intelligence)是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门技术科学。

    135 引用 • 190 回帖
  • Ngui

    Ngui 是一个 GUI 的排版显示引擎和跨平台的 GUI 应用程序开发框架,基于
    Node.js / OpenGL。目标是在此基础上开发 GUI 应用程序可拥有开发 WEB 应用般简单与速度同时兼顾 Native 应用程序的性能与体验。

    7 引用 • 9 回帖 • 394 关注
  • FFmpeg

    FFmpeg 是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。

    23 引用 • 32 回帖 • 1 关注
  • V2Ray
    1 引用 • 15 回帖 • 1 关注
  • 小说

    小说是以刻画人物形象为中心,通过完整的故事情节和环境描写来反映社会生活的文学体裁。

    28 引用 • 108 回帖
  • H2

    H2 是一个开源的嵌入式数据库引擎,采用 Java 语言编写,不受平台的限制,同时 H2 提供了一个十分方便的 web 控制台用于操作和管理数据库内容。H2 还提供兼容模式,可以兼容一些主流的数据库,因此采用 H2 作为开发期的数据库非常方便。

    11 引用 • 54 回帖 • 653 关注
  • sts
    2 引用 • 2 回帖 • 197 关注
  • jsoup

    jsoup 是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址、HTML 文本内容。它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据。

    6 引用 • 1 回帖 • 483 关注
  • CloudFoundry

    Cloud Foundry 是 VMware 推出的业界第一个开源 PaaS 云平台,它支持多种框架、语言、运行时环境、云平台及应用服务,使开发人员能够在几秒钟内进行应用程序的部署和扩展,无需担心任何基础架构的问题。

    5 引用 • 18 回帖 • 172 关注
  • GAE

    Google App Engine(GAE)是 Google 管理的数据中心中用于 WEB 应用程序的开发和托管的平台。2008 年 4 月 发布第一个测试版本。目前支持 Python、Java 和 Go 开发部署。全球已有数十万的开发者在其上开发了众多的应用。

    14 引用 • 42 回帖 • 779 关注
  • IDEA

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

    181 引用 • 400 回帖
  • 互联网

    互联网(Internet),又称网际网络,或音译因特网、英特网。互联网始于 1969 年美国的阿帕网,是网络与网络之间所串连成的庞大网络,这些网络以一组通用的协议相连,形成逻辑上的单一巨大国际网络。

    98 引用 • 344 回帖
  • abitmean

    有点意思就行了

    27 关注
  • 智能合约

    智能合约(Smart contract)是一种旨在以信息化方式传播、验证或执行合同的计算机协议。智能合约允许在没有第三方的情况下进行可信交易,这些交易可追踪且不可逆转。智能合约概念于 1994 年由 Nick Szabo 首次提出。

    1 引用 • 11 回帖 • 2 关注
  • Hadoop

    Hadoop 是由 Apache 基金会所开发的一个分布式系统基础架构。用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力进行高速运算和存储。

    86 引用 • 122 回帖 • 626 关注