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

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

一、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 引用 • 8207 回帖 • 1 关注

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • Wide

    Wide 是一款基于 Web 的 Go 语言 IDE。通过浏览器就可以进行 Go 开发,并有代码自动完成、查看表达式、编译反馈、Lint、实时结果输出等功能。

    欢迎访问我们运维的实例: https://wide.b3log.org

    30 引用 • 218 回帖 • 601 关注
  • PWL

    组织简介

    用爱发电 (Programming With Love) 是一个以开源精神为核心的民间开源爱好者技术组织,“用爱发电”象征开源与贡献精神,加入组织,代表你将遵守组织的“个人开源爱好者”的各项条款。申请加入:用爱发电组织邀请帖
    用爱发电组织官网:https://programmingwithlove.stackoverflow.wiki/

    用爱发电组织的核心驱动力:

    • 遵守开源守则,体现开源&贡献精神:以分享为目的,拒绝非法牟利。
    • 自我保护:使用适当的 License 保护自己的原创作品。
    • 尊重他人:不以各种理由、各种漏洞进行未经允许的抄袭、散播、洩露;以礼相待,尊重所有对社区做出贡献的开发者;通过他人的分享习得知识,要留下足迹,表示感谢。
    • 热爱编程、热爱学习:加入组织,热爱编程是首当其要的。我们欢迎热爱讨论、分享、提问的朋友,也同样欢迎默默成就的朋友。
    • 倾听:正确并恳切对待、处理问题与建议,及时修复开源项目的 Bug ,及时与反馈者沟通。不抬杠、不无视、不辱骂。
    • 平视:不诋毁、轻视、嘲讽其他开发者,主动提出建议、施以帮助,以和谐为本。只要他人肯努力,你也可能会被昔日小看的人所超越,所以请保持谦虚。
    • 乐观且活跃:你的努力决定了你的高度。不要放弃,多年后回头俯瞰,才会发现自己已经成就往日所仰望的水平。积极地将项目开源,帮助他人学习、改进,自己也会获得相应的提升、成就与成就感。
    1 引用 • 487 回帖 • 2 关注
  • IBM

    IBM(国际商业机器公司)或万国商业机器公司,简称 IBM(International Business Machines Corporation),总公司在纽约州阿蒙克市。1911 年托马斯·沃森创立于美国,是全球最大的信息技术和业务解决方案公司,拥有全球雇员 30 多万人,业务遍及 160 多个国家和地区。

    16 引用 • 53 回帖 • 122 关注
  • PHP

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

    164 引用 • 407 回帖 • 524 关注
  • Lute

    Lute 是一款结构化的 Markdown 引擎,支持 Go 和 JavaScript。

    25 引用 • 191 回帖 • 24 关注
  • OAuth

    OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 oAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 oAuth 是安全的。oAuth 是 Open Authorization 的简写。

    36 引用 • 103 回帖 • 8 关注
  • Latke

    Latke 是一款以 JSON 为主的 Java Web 框架。

    70 引用 • 532 回帖 • 713 关注
  • Solidity

    Solidity 是一种智能合约高级语言,运行在 [以太坊] 虚拟机(EVM)之上。它的语法接近于 JavaScript,是一种面向对象的语言。

    3 引用 • 18 回帖 • 349 关注
  • 大疆创新

    深圳市大疆创新科技有限公司(DJI-Innovations,简称 DJI),成立于 2006 年,是全球领先的无人飞行器控制系统及无人机解决方案的研发和生产商,客户遍布全球 100 多个国家。通过持续的创新,大疆致力于为无人机工业、行业用户以及专业航拍应用提供性能最强、体验最佳的革命性智能飞控产品和解决方案。

    2 引用 • 14 回帖 • 3 关注
  • WordPress

    WordPress 是一个使用 PHP 语言开发的博客平台,用户可以在支持 PHP 和 MySQL 数据库的服务器上架设自己的博客。也可以把 WordPress 当作一个内容管理系统(CMS)来使用。WordPress 是一个免费的开源项目,在 GNU 通用公共许可证(GPLv2)下授权发布。

    45 引用 • 113 回帖 • 310 关注
  • 微服务

    微服务架构是一种架构模式,它提倡将单一应用划分成一组小的服务。服务之间互相协调,互相配合,为用户提供最终价值。每个服务运行在独立的进程中。服务于服务之间才用轻量级的通信机制互相沟通。每个服务都围绕着具体业务构建,能够被独立的部署。

    96 引用 • 155 回帖 • 1 关注
  • Elasticsearch

    Elasticsearch 是一个基于 Lucene 的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于 RESTful 接口。Elasticsearch 是用 Java 开发的,并作为 Apache 许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

    116 引用 • 99 回帖 • 266 关注
  • 尊园地产

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

    1 引用 • 22 回帖 • 688 关注
  • gRpc
    10 引用 • 8 回帖 • 55 关注
  • WiFiDog

    WiFiDog 是一套开源的无线热点认证管理工具,主要功能包括:位置相关的内容递送;用户认证和授权;集中式网络监控。

    1 引用 • 7 回帖 • 544 关注
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    941 引用 • 1458 回帖 • 152 关注
  • 链滴

    链滴是一个记录生活的地方。

    记录生活,连接点滴

    132 引用 • 3647 回帖
  • 电影

    这是一个不能说的秘密。

    120 引用 • 597 回帖 • 1 关注
  • Sym

    Sym 是一款用 Java 实现的现代化社区(论坛/BBS/社交网络/博客)系统平台。

    下一代的社区系统,为未来而构建

    523 引用 • 4581 回帖 • 694 关注
  • Kotlin

    Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,由 JetBrains 设计开发并开源。Kotlin 可以编译成 Java 字节码,也可以编译成 JavaScript,方便在没有 JVM 的设备上运行。在 Google I/O 2017 中,Google 宣布 Kotlin 成为 Android 官方开发语言。

    19 引用 • 33 回帖 • 28 关注
  • C++

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

    106 引用 • 152 回帖
  • Gzip

    gzip (GNU zip)是 GNU 自由软件的文件压缩程序。我们在 Linux 中经常会用到后缀为 .gz 的文件,它们就是 Gzip 格式的。现今已经成为互联网上使用非常普遍的一种数据压缩格式,或者说一种文件格式。

    9 引用 • 12 回帖 • 114 关注
  • 微软

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

    8 引用 • 44 回帖
  • HHKB

    HHKB 是富士通的 Happy Hacking 系列电容键盘。电容键盘即无接点静电电容式键盘(Capacitive Keyboard)。

    5 引用 • 74 回帖 • 409 关注
  • 开源中国

    开源中国是目前中国最大的开源技术社区。传播开源的理念,推广开源项目,为 IT 开发者提供了一个发现、使用、并交流开源技术的平台。目前开源中国社区已收录超过两万款开源软件。

    7 引用 • 86 回帖
  • Netty

    Netty 是一个基于 NIO 的客户端-服务器编程框架,使用 Netty 可以让你快速、简单地开发出一个可维护、高性能的网络应用,例如实现了某种协议的客户、服务端应用。

    49 引用 • 33 回帖 • 22 关注
  • Hadoop

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

    82 引用 • 122 回帖 • 620 关注