`
shangjava
  • 浏览: 1190659 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

9.Java中的集合、枚举、泛型【下】

阅读更多

(本章主要讲解Java里面会遇到的所有集合类以及相关用法,还有JDK1.5里面出来的一些关于集合和算法的新内容,主要是方便我们在开发过程中适当地注意选择,而且本章的内容相对前边章节比较少,但是代码量比较大,但是大部分内容都是个人的一些总结。当然这一个章节会涉及到JDK本身提供的一些数据结构的相关内容,以及最基本几个数据结构的相关性能分析。本来是打算先写Java的IO相关的内容,刚好最近有个项目需要经常性使用到Java里面的东西,所以先提供这一个章节给大家。很多人来Email说Java的基础数据类型一个章节的内容太少了,这点我必须说一句抱歉,因为基础数据类型是我第一次写,后来才想到使用后边这样的方式来写BLOG的内容,基础章节的东西我后边会提供专程的章节来讲解控制流程、以及各种关键字的清单。该系列教程的主要目的是为了整理一份个人使用的开发文档,如果有人转载,请来Email告知,谢谢!在整个过程里面如果发现有笔误的地方希望来Email提点:silentbalanceyh@126.com)

本章目录
1.基本概念
2.常用集合——列表、队列、栈
3.常用集合——Set集合、哈希表
4.泛型、枚举

3.常用集合——Set集合、哈希表
  i.AbstractSet<E>(1.2)类和AbstractMap<K,V>(1.2)相关:
  Set和Map部分需要进行比较方式讲解可能更能够理解里面每一种集合的用法
  1)HashSet<E>(1.2)、HashMap<K,V>(1.2)和HashTable<K,V>(1.0)
  HashSet<E>(1.2)类:
public classHashSet<E>extendsAbstractSet<E>implementsSet<E>,Cloneable,Serializable
  该类实现了接口Set,由哈希表支持[实际是一个HashMap实例],它不保证集合的迭代顺序,特别是不保证顺序恒久不变,但是允许null元素。对该集合迭代使用的时间和HashSet实例的大小(元素的数量)和底层的HashMap实例(桶的数量)的“容量”的和成比例,如果迭代性在程序设计的时候很重要,不要将初始化容量设置得太高。该实现不是同步的,而且其遍历方法等同于数组,都是快速失败的,若需要对该集合进行同步方法就需要调用下边的方法:
Set s = Collections.synchronizedSet(newHashSet(...));
  HashMap<K,V>(1.2)类:
public classHashMap<K,V>extendsAbstractMap<K,V>implementsMap<K,V>,Cloneable,Serializable
  该类是基于哈希表的Map接口实现,此实现提供了所有可选的映射操作,并且允许使用null值和null键。(除了非同步和允许使用null之外,HashMap类和Hashtable类大致相同。),同样的该类不保证映射的顺序,特别是它不保证该顺序恒久不变。HashMap的实例有两个影响性能的参数:初始容量和加载因子
  容量是哈希表中的桶的数量,初始容量就是哈希表在创建的时候的容量。
  加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度。
  当哈希表中的条目数超出了加载因子和当前容量的乘积的时候,需要针对哈希表结构进行重建操作,而哈希表内部具有大约两倍的桶数目。
  Hashtable<K,V>(1.0)类:
public classHashtable<K,V>extendsDictionary<K,V>implementsMap<K,V>,Cloneable,Serializable
  该类是HashMap的前身,主要特点和HashMap<K,V>差不多,但是有一点点区别就是:键和值都不允许使用null,而且该类的实现是线程同步的。
  先看几个简单的例子,再来考虑它们相互之间的一些区别和联系
  ——[1]HashSet<E>的基本例子——
packageorg.susan.java.collection;

importjava.util.Arrays;
importjava.util.HashSet;
importjava.util.Iterator;
importjava.util.Set;
/**
*一个使用HashSet的简单例子
**/
public classHashSetDemo {
public static voidmain(Stringargs[]){
HashSet<String> set =newHashSet<String>();
set.add("A");
set.add("B");
set.add("C");
set.add("B");
set.add("D");
printSet(set);
System.out.println();
String[] strArray = {"E","F","G"};
set.addAll(Arrays.asList(strArray));
printSet(set);
}
private static voidprintSet(Set<String> set){
Iterator<String> iterator = set.iterator();
while(iterator.hasNext()){
System.out.print("["+ iterator.next() +"],");
}
}
}
  根据上边这段代码可以得到下边的输出:【*:因为集合本身是无序的,而且迭代不保证顺序,有可能输出的顺序不一样,但是集合元素的内容应该是相同的。】
[D],[A],[B],[C],
[D],[E],[F],[G],[A],[B],[C],
  ——[2]从HashSet<E>里面删除某个元素——
packageorg.susan.java.collection;

importjava.util.HashSet;
importjava.util.Iterator;
importjava.util.Set;
/**
*HashSet的删除方法remove(Object obj)
**/
public classHashSetDemo {
public static voidmain(Stringargs[]){
HashSet<Integer> set =newHashSet<Integer>();
set.add(newInteger(12));
set.add(newInteger(33));
set.add(55);
set.remove("B");
set.remove(12);
printSet(set);
}
private static voidprintSet(Set<Integer> set){
Iterator<Integer> iterator = set.iterator();
while(iterator.hasNext()){
System.out.print("["+ iterator.next() +"],");
}
}
}
  先看上边这段代码的结果:
[33],[55],
  【*:与ArrayList的结果有区别的是,该remove方法只有一个参数Object,而且没有重载形式,因为HashSet本身是无序的,所以和ArrayList不一样的是,不支持索引,所以在删除的时候,进行了一次自动拆解箱的操作。注意代码set.remove(12),这个代码传入的是12的原始类型,但是在这个过程里面转换成为了Integer的类型。如果这一段是ArrayList类型而不是HashSet类型,就会抛出异常,因为ArrayList有一个带int类型的重载方法,是删除某个索引位置的元素,而且HashSet的remove方法如果删除失败的话不会抛出异常,因为是返回的一个特殊值false,这一点可以直接在代码中修改set.remove("B")为System.out.println(set.remove("B"))来验证】
  ——[3]HashMap<K,V>的不同遍历——
packageorg.susan.java.collection;

importjava.util.Collection;
importjava.util.HashMap;
importjava.util.Iterator;
importjava.util.Set;
importjava.util.Map.Entry;
/**
*使用HashMap的基本例子
**/
public classHashMapDemo {
public static voidmain(Stringargs[]){
HashMap<Integer,String> map =newHashMap<Integer,String>();
map.put(1,"One");
map.put(2,"Two");
map.put(3,"Three");
map.put(4,"Four");
//键遍历
System.out.println("------Key Iterator------");
Set<Integer> keys = map.keySet();
Iterator<Integer> iterator = keys.iterator();
while(iterator.hasNext()){
Integer key = iterator.next();
System.out.print("[Key:"+ key +"--");
System.out.println("Value:"+ map.get(key) +"]");
}
// 值遍历
System.out.println("------Value Iterator------");
Collection<String> values = map.values();
Iterator<String> vIterator = values.iterator();
while(vIterator.hasNext()){
Stringvalue = vIterator.next();
System.out.println("[Value:"+ value +"]");
}
//映射关系遍历
System.out.println("------Entry Iterator------");
Set<Entry<Integer,String>> set = map.entrySet();
Iterator<Entry<Integer,String>> sIterator = set.iterator();
while(sIterator.hasNext()){
Entry<Integer,String> entry = sIterator.next();
System.out.print("[Key:"+ entry.getKey() +"--");
System.out.println("Value:"+ entry.getValue() +"]");
}
}
}
  这里提供了三种不同的方式遍历,有几点需要说明:
  • 通过键遍历的时候,键使用的返回是Set<E>,因为键是一个不重复的,所以为了保证键的不重复性,设计的时候使用的Set<E>
  • 通过值遍历的时候,值使用的时候返回是Collection<E>,因为该集合是可以重复的,而且不需要保证值的不重复性
  • 通过关系遍历的时候,使用的格式需要注意Entry是Map的内部类Map.Entry,而且每一个项的类型需要注意
  • 只能通过Map的键去获取值,不能通过Map的值去获取键
  下边是这段程序的输出:
------Key Iterator------
[Key:1--Value:One]
[Key:2--Value:Two]
[Key:3--Value:Three]
[Key:4--Value:Four]
------Value Iterator------
[Value:One]
[Value:Two]
[Value:Three]
[Value:Four]
------Entry Iterator------
[Key:1--Value:One]
[Key:2--Value:Two]
[Key:3--Value:Three]
[Key:4--Value:Four]
  ——[4]Hashtable<K,V>的简单用法——
packageorg.susan.java.collection;

importjava.util.Enumeration;
importjava.util.Hashtable;

public classHashtableDemo {
public static voidmain(Stringargs[]){
Hashtable<Integer,String> tables =newHashtable<Integer,String>();
tables.put(1,"One");
tables.put(2,"Two");
tables.put(3,"Three");
Enumeration<Integer> iterator =null;
for(iterator = tables.keys();iterator.hasMoreElements();){
Integer key = iterator.nextElement();
System.out.print("[Key:"+ key +"--");
System.out.println("Value:"+ tables.get(key) +"]");
}
}
}
  上边代码的程序输出:
[Key:3--Value:Three]
[Key:2--Value:Two]
[Key:1--Value:One]
  【*:从上边的代码段可以看出,HashMap和Hashtable没有太多的区别,所以这段代码仅仅提供Hashtable的键遍历方式就可以了,至于遍历是使用for还是使用while,这个可以根据本身的情况自行选择。而且需要注意一点是HashMap在遍历的时候返回的迭代器是Iterator,而Hashtable返回的迭代器是Enumeration。】
  上边的代码段都是1.5过后带泛型的代码段,下边提供一段老版本(1.4或者1.4以下的版本)的写法:
packageorg.susan.java.collection;

importjava.util.HashMap;
importjava.util.Iterator;
importjava.util.Map.Entry;

public classOleMap {
public static voidmain(Stringargs[]){
HashMap map =newHashMap();
map.put(1,"One");
map.put(2,"Two");
Iterator iterator = map.entrySet().iterator();
while(iterator.hasNext())
{
Entry entry = (Entry)iterator.next();
System.out.print("[Key:"+ entry.getKey() +"--");
System.out.println("Value:"+ entry.getValue()+"]");
}
}
}
  这段代码的输出这里不列出来了,老版本的写法没有泛型概念,就会出现一个类型的强制转换过程,在理解的时候可能简单很多,不过这种写法用1.5编译的时候会存在很多类型检查的警告
  上边已经基本接触了Hashtable和HashMap,总结一下这两个的特点和相互之间的区别,因为HashSet本身不是一个映射表的概念,就可以当作一个普通的Set集合的实现来使用,所以HashSet加入比较是没有太大的实际意义:
  • 返回的迭代器不一样:Hashtable返回的迭代器是Enumeration,而HashMap返回的迭代器是Iterator
  • HashMap不是同步的,而Hashtable是线程同步的,意思就是说前者是线程不安全的,系统开销比较小;后者是线程安全的,开销相对大点点
  • HashTable不允许null键和null值,而HashMap是允许null键和null值
  • HashTable有一个contains(Object value)方法和containsValue(Object value)功能是一模一样
  最后再介绍一下上边代码没有涉及到的常用方法:
HashMap<K,V>类:
booleancontainsKey(Object key):如果该映射里面包含了指定的一个键的映射关系返回为true,反之返回为false
booleancontainsValue(Object value):如果该映射里面包含了指定的一个或者多个值的映射关系,返回为true,反之返回为false
voidputAll(Map<?extendsK,?extendsV> m):不单独复制某个映射关系,而是直接把一个映射关系复制到某个映射里面,类似集合中的addAll方法
Hashtable<K,V>类:
booleancontains(Object value):如果该映射里面包含了指定的一个或者多个值的映射关系,返回为true,反之返回为false
booleancontainsKey(Object key):如果该映射里面包含了指定的一个键的映射关系返回为true,反之返回为false
booleancontainsValue(Object value):如果该映射里面包含了指定的一个或者多个值的映射关系,返回为true,反之返回为false
Enumeration<V>elements():返回哈希表中的值的枚举
Collection<V>values():返回哈希表中的值的集合
Enumeration<K>keys():返回哈希表中的键的枚举
Set<K>keySet():返回哈希表中的键的集合
protected voidrehash():重组该hash表。
  这里注意区分返回为键枚举以及键集合的方法和返回为值枚举和值集合的方法【elements() && values();keys() && keySet()】
  2)TreeSet<E>(1.2)类TreeMap(K,V>(1.2)类:
  上边介绍的HashSet和HashMap都是无序的,接下来介绍两个有序的映射:
  TreeSet<E>(1.2)类:
public classTreeSet<E>extendsAbstractSet<E>implementsNavigableSet<E>,Cloneable,Serializable
  基于TreeMap的NavigableSet实现,使用元素的自然顺序对元素进行排序,或者根据创建的Comparator进行排序。
  ——[$]TreeSet简单例子——
packageorg.susan.java.collection;

importjava.util.Iterator;
importjava.util.TreeSet;
/**
*TreeSet集合的一个例子
**/
public classTreeSetDemo {
public static voidmain(Stringargs[]){
TreeSet<Integer> tree = new TreeSet<Integer>();
tree.add(22);
tree.add(11);
tree.add(33);
tree.add(44);
System.out.println(tree);
Iterator<Integer> iterator = tree.iterator();
while(iterator.hasNext()){
System.out.print(iterator.next() +",");
}
System.out.println();
iterator = tree.descendingIterator();
while(iterator.hasNext()){
System.out.print(iterator.next() +",");
}
}
}
  上边这段代码演示了TreeSet的基本用法,其实TreeSet有一个很大的特点,不论用什么样子的方式将元素添加到这个集合里面,这个集合里面的元素总是通过某个比较器进行了排序操作的,可以看下边的输出:
[11, 22, 33, 44]
11,22,33,44,
44,33,22,11,
  从上边的结果可以知道,TreeSet本身就是已经排好序的集合,所以正序遍历结果和本身内部的结果一样的顺序,虽然添加的时候是22,11,33,44。至于TreeSet的方法,大部分和Set差不多,这里不做详细介绍,有兴趣的可以去查阅以下API。
  ——[$]TreeMap简单例子——
  介于红黑树(Red-Block tree)的NavigableMap实现,该映射根据键的自然排序进行排序,或者根据键的Comparator进行排序
packageorg.susan.java.collection;

importjava.util.Iterator;

相关推荐

Global site tag (gtag.js) - Google Analytics