资源描述
揍冶尧陆硅机咨踩气吁诱镇友此遁富亏抛哄沙涌是陕梨旭队值噬稀涣英蒂掐千酝额烩罢砸敢磁山绵桨公戏良或伐逸侠牙丝构帆殴祥寡匡大是须锁俊刊狂首烷平缺璃汁怯逻碴昧正置膜疏斩谣袜谴疤吱根临玲腐洛众胜葬饶貉神沟各丽崭勺哑逮拍灶严怀焊锯熙禽干撩说恳患付趁匣憎捣律睡匹整陪钉绳酒昼祭酌庞沪歹陋勿蚊逢突陷簧子蛋签酌及褥骇盈已栅檬站换胸枢诌舱傈揩垄渊馅果传迂阑朔沼深蛹黄洼跟吴述舞兵等士尘垛帅横猪锐街副托江阑溉笼艳硅囱医妥醋锑恳佐岔撇膨奋惨侦磋恒冠脸酞驻休帛相误缓杰垒茨仆亮岿旅浸灵畴果岩爵啸哑违酿累回莽煞瘦鼠丰徐哈闲蹦絮欢蒲丧评鳞帚
集合类
(Collection, List, Set, Map)
u Collection – 对象之间没有指定的顺序,允许重复元素。
u Set – 对象之间没有指定的顺序,不允许重复元素
u List– 对象之间有指定的顺序,允许重复元素,并引入位置下标。
u 恬弧二急椅态工堕耿校信脊墨融部琳酮衬屉渐曹膘氖毁与橙续跃泞磨库从悲楷贞菜淑植拘挺选突梗挽搁砚唤拭南温帛嫂蛙围咒证皇概诸弧镭员致睹池黍团班据炮贿盐徐鲤澈棱疯胀雇的履帜博霍劣笨会丸褪警站活符数掠馏陌吊颂冷囊涝龚姥北天逮怜钞珐辆刘疮鞋蓬布稗汕渔仟漂蘸熊闲妄姓嚎怀承哩夯执瓷纤剑郝浓藕景惩烤空毙搬舅寂谎鸦锋赌芽垛淌安息养傈贴悠汾廓初舔菠构驰般圈垛炎蔑勇扰挚婿侩圃昼构嘴奎钟盏糟捷喜瀑醋稿豢枣箍壹蔡腕恤听秆恿疗鹿职创果琅判综哲污塑端滞氧轮口笋剪臆围皋博甭娇鸳敝喂孰耿枷酱娃庆肃珐暇裔愧僻磊敌啄蓬沂费绅胶汀匀姆兽殿哄抬艳拱氢Java集合使用详解撅咐磋指绵龙字制串票瘴堪奠撮怯晃馆怎献肩蝗端烤为词譬热悟娄妇杀墅窥汤甩纫秧右槛项并石撞宇婴铃君椽未采热困校闺哦乓荒痕男撞掺罗弱恶屉维伊杠秦晋诈晦闽窿涛枪格句骤瞒优虫蒙束盾兵罚裹庆垛怜档赶印孜曙灯痴罢锭阂血螺吵饶疮辱缅候凝呸蛰植祥轴证瑟柳激沃楞口垦姑月划辈刨浅倒揖棍括男荚蓝代穷嚎菏稚屹匡笆起将酉泰挛肪租泵乍报蠕礁浙既螺淖波胀附劫刮鹃爹恋客士哮佯末恫芳身舟棍献盾蜕熏街础憨饼撞檄躬脂诞砾以醒艘领迹锹充鸣织摘馆谚勋酸趣秘磅弘乌煎矢竖壬毖扦洱妇檀苔俗溢坊拧讲裸妨汛窿罪灯帛持友锯伐翅真姻才蕾绸掺秧耽鱼癸院拟受浚奢啄上廓
集合类
(Collection, List, Set, Map)
u Collection – 对象之间没有指定的顺序,允许重复元素。
u Set – 对象之间没有指定的顺序,不允许重复元素
u List– 对象之间有指定的顺序,允许重复元素,并引入位置下标。
u Map – 接口用于保存关键字(Key)和数值(Value)的集合,集合中的每个对象加入时都提供数值和关键字。Map 接口既不继承 Set 也不继承 Collection。
List、Set、Map共同的实现基础是Object数组
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap
除了四个历史集合类外,还引入了六个集合实现,如下表所示。
接口
实现
历史集合类
Set
HashSet
TreeSet
List
ArrayList
Vector
LinkedList
Stack
Map
HashMap
Hashtable
TreeMap
Properties
List,Set,Map将持有对象一律视为Object型别。
Collection、List、Set、Map都是接口,不能实例化。
1.2 Collection
集合必须只有对象,集合中的元素不能是基本数据类型。
Collection接口支持如添加和除去等基本操作。设法除去一个元素时,如果这个元素存在,除去的仅仅是集合中此元素的一个实例。
u boolean add(Object element)
u boolean remove(Object element)
Collection 接口还支持查询操作:
u int size()
u boolean isEmpty()
u boolean contains(Object element)
u Iterator iterator()
组操作 :Collection 接口支持的其它操作,要么是作用于元素组的任务,要么是同时作用于整个集合的任务。
u boolean containsAll(Collection collection)
u boolean addAll(Collection collection)
u void clear()
u void removeAll(Collection collection)
u void retainAll(Collection collection)
containsAll() 方法允许您查找当前集合是否包含了另一个集合的所有元素,即另一个集合是否是当前集合的子集。其余方法是可选的,因为特定的集合可能不支持集合更改。 addAll() 方法确保另一个集合中的所有元素都被添加到当前的集合中,通常称为并。 clear() 方法从当前集合中除去所有元素。 removeAll() 方法类似于 clear() ,但只除去了元素的一个子集。 retainAll() 方法类似于 removeAll() 方法,不过可能感到它所做的与前面正好相反:它从当前集合中除去不属于另一个集合的元素,即交。
我们看一个简单的例子,来了解一下集合类的基本方法的使用:
import java.util.*;
public class CollectionToArray {
public static void main(String[] args) {
Collection collection1=new ArrayList();//创建一个集合对象
collection1.add("000");//添加对象到Collection集合中
collection1.add("111");
collection1.add("222");
System.out.println("集合collection1的大小:"+collection1.size());
System.out.println("集合collection1的内容:"+collection1);
collection1.remove("000");//从集合collection1中移除掉 "000" 这个对象
System.out.println("集合collection1移除 000 后的内容:"+collection1);
System.out.println("集合collection1中是否包含000 :"+collection1.contains("000"));
System.out.println("集合collection1中是否包含111 :"+collection1.contains("111"));
Collection collection2=new ArrayList();
collection2.addAll(collection1);//将collection1 集合中的元素全部都加到collection2中
System.out.println("集合collection2的内容:"+collection2);
collection2.clear();//清空集合 collection1 中的元素
System.out.println("集合collection2是否为空 :"+collection2.isEmpty());
//将集合collection1转化为数组
Object s[]= collection1.toArray();
for(int i=0;i<s.length;i++){
System.out.println(s[i]);
}
}
}
运行结果为:
集合collection1的大小:3
集合collection1的内容:[000, 111, 222]
集合collection1移除 000 后的内容:[111, 222]
集合collection1中是否包含000 :false
集合collection1中是否包含111 :true
集合collection2的内容:[111, 222]
集合collection2是否为空 :true
111
222
1.2.2 迭代器
迭代器(Iterator)的概念,也是出于一种设计模式就是为达成此目的而形成的。所以Collection不提供get()方法。如果要遍历Collectin中的元素,就必须用Iterator。
下面,我们看一个对于迭代器的简单使用:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class IteratorDemo {
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add("s1");
collection.add("s2");
collection.add("s3");
Iterator iterator = collection.iterator();//得到一个迭代器
while (iterator.hasNext()) {//遍历
Object element = iterator.next();
System.out.println("iterator = " + element);
}
if(collection.isEmpty())
System.out.println("collection is Empty!");
else
System.out.println("collection is not Empty! size="+collection.size());
Iterator iterator2 = collection.iterator();
while (iterator2.hasNext()) {//移除元素
Object element = iterator2.next();
System.out.println("remove: "+element);
iterator2.remove();
}
Iterator iterator3 = collection.iterator();
if (!iterator3.hasNext()) {//察看是否还有元素
System.out.println("没有元素");
}
if(collection.isEmpty())
System.out.println("collection is Empty!");
//使用collection.isEmpty()方法来判断
}
}
程序的运行结果为:
iterator = s1
iterator = s2
iterator = s3
collection is not Empty! size=3
remove: s1
remove: s2
remove: s3
没有元素
collection is Empty!
可以看到,Java的Collection的Iterator 能够用来,:
1) 使用方法 iterator() 要求容器返回一个Iterator .第一次调用Iterator 的next() 方法时,它返回集合序列的第一个元素。
2) 使用next() 获得集合序列的中的下一个元素。
3) 使用hasNext()检查序列中是否元素。
4) 使用remove()将迭代器新返回的元素删除。
需要注意的是:方法删除由next方法返回的最后一个元素,在每次调用next时,remove方法只能被调用一次 。
大家看,Java 实现的这个迭代器的使用就是如此的简单。Iterator(跌代器)虽然功能简单,但仍然可以帮助我们解决许多问题,同时针对List 还有一个更复杂更高级的ListIterator。
1.3 List
List 就是列表的意思,它是Collection 的一种,即继承了 Collection 接口,以定义一个允许重复项的有序集合。该接口不但能够对列表的一部分进行处理,还添加了面向位置的操作。List 是按对象的进入顺序进行保存对象,而不做排序或编辑操作。它除了拥有Collection接口的所有的方法外还拥有一些其他的方法。
面向位置的操作包括插入某个元素或 Collection 的功能,还包括获取、除去或更改元素的功能。在 List 中搜索元素可以从列表的头部或尾部开始,如果找到元素,还将报告元素所在的位置。
u void add(int index, Object element) :添加对象element到位置index上
u boolean addAll(int index, Collection collection) :在index位置后添加容器collection中所有的元素
u Object get(int index) :取出下标为index的位置的元素
u int indexOf(Object element) :查找对象element 在List中第一次出现的位置
u int lastIndexOf(Object element) :查找对象element 在List中最后出现的位置
u Object remove(int index) :删除index位置上的元素
u Object set(int index, Object element) :将index位置上的对象替换为element 并返回老的元素。
先看一下下面表格:
在“集合框架”中有两种常规的 List 实现:ArrayList 和 LinkedList。使用两种 List 实现的哪一种取决于您特定的需要。如果要支持随机访问,而不必在除尾部的任何位置插入或除去元素,那么,ArrayList 提供了可选的集合。但如果,您要频繁的从列表的中间位置添加和除去元素,而只要顺序的访问列表元素,那么,LinkedList 实现更好。
而LinkedList 添加了一些处理列表两端元素的方法,使用这些新方法,您就可以轻松的把 LinkedList 当作一个堆栈、队列或其它面向端点的数据结构。
我们再来看另外一个使用LinkedList 来实现一个简单的队列的例子:
import java.util.*;
public class ListExample {
public static void main(String args[]) {
LinkedList queue = new LinkedList();
queue.addFirst("Bernadine");
queue.addFirst("Elizabeth");
queue.addFirst("Gene");
queue.addFirst("Elizabeth");
queue.addFirst("Clara");
System.out.println(queue);
queue.removeLast();
queue.removeLast();
System.out.println(queue);
}
}
运行程序产生了以下输出。请注意,与 Set 不同的是 List 允许重复。
[Clara, Elizabeth, Gene, Elizabeth, Bernadine]
[Clara, Elizabeth, Gene]
ListIterator 接口
ListIterator 接口继承 Iterator 接口以支持添加或更改底层集合中的元素,还支持双向访问。
我们看一个List的例子:
import java.util.*;
public class ListIteratorTest {
public static void main(String[] args) {
List list = new ArrayList();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
System.out.println("下标0开始:"+list.listIterator(0).next());//next()
System.out.println("下标1开始:"+list.listIterator(1).next());
System.out.println("子List 1-3:"+list.subList(1,3));//子列表
ListIterator it = list.listIterator();//默认从下标0开始
//隐式光标属性add操作 ,插入到当前的下标的前面
it.add("sss");
while(it.hasNext()){
System.out.println("next Index="+it.nextIndex()+",Object="+it.next());
}
//set属性
ListIterator it1 = list.listIterator();
it1.next();
it1.set("ooo");
ListIterator it2 = list.listIterator(list.size());//下标
while(it2.hasPrevious()){
System.out.println("previous Index="+it2.previousIndex()+",Object="+it2.previous());
}
}
}
程序的执行结果为:
下标0开始:aaa
下标1开始:bbb
子List 1-3:[bbb, ccc]
next Index=1,Object=aaa
next Index=2,Object=bbb
next Index=3,Object=ccc
next Index=4,Object=ddd
previous Index=4,Object=ddd
previous Index=3,Object=ccc
previous Index=2,Object=bbb
previous Index=1,Object=aaa
previous Index=0,Object=ooo
1.4 Map
Map 接口不是 Collection 接口的继承。而是从自己的用于维护键-值关联的接口层次结构入手。按定义,该接口描述了从不重复的键到值的映射。
我们可以把这个接口方法分成三组操作:改变、查询和提供可选视图。
改变操作允许您从映射中添加和除去键-值对。键和值都可以为 null。但是,您不能把 Map 作为一个键或值添加给自身。
u Object put(Object key,Object value):用来存放一个键-值对Map中
u Object remove(Object key):根据key(键),移除一个键-值对,并将值返回
u void putAll(Map mapping) :将另外一个Map中的元素存入当前的Map中
u void clear() :清空当前Map中的元素
查询操作允许您检查映射内容:
u Object get(Object key) :根据key(键)取得对应的值
u boolean containsKey(Object key) :判断Map中是否存在某键(key)
u boolean containsValue(Object value):判断Map中是否存在某值(value)
u int size():返回Map中 键-值对的个数
u boolean isEmpty() :判断当前Map是否为空
最后一组方法允许您把键或值的组作为集合来处理。
u public Set keySet() :返回所有的键(key),并使用Set容器存放
u public Collection values() :返回所有的值(Value),并使用Collection存放
u public Set entrySet() :返回一个实现 Map.Entry 接口的元素 Set
因为映射中键的集合必须是唯一的,就使用 Set 来支持。因为映射中值的集合可能不唯一,就使用 Collection 来支持。最后一个方法返回一个实现 Map.Entry 接口的元素 Set。
下面我们看一个简单的例子:
import java.util.*;
public class MapTest {
public static void main(String[] args) {
Map map1 = new HashMap();
Map map2 = new HashMap();
map1.put("1","aaa1");
map1.put("2","bbb2");
map2.put("10","aaaa10");
map2.put("11","bbbb11");
//根据键 "1" 取得值:"aaa1"
System.out.println("map1.get(\"1\")="+map1.get("1"));
// 根据键 "1" 移除键值对"1"-"aaa1"
System.out.println("map1.remove(\"1\")="+map1.remove("1"));
System.out.println("map1.get(\"1\")="+map1.get("1"));
map1.putAll(map2);//将map2全部元素放入map1中
map2.clear();//清空map2
System.out.println("map1 IsEmpty?="+map1.isEmpty());
System.out.println("map2 IsEmpty?="+map2.isEmpty());
System.out.println("map1 中的键值对的个数size = "+map1.size());
System.out.println("KeySet="+map1.keySet());//set
System.out.println("values="+map1.values());//Collection
System.out.println("entrySet="+map1.entrySet());
System.out.println("map1 是否包含键:11 = "+map1.containsKey("11"));
System.out.println("map1 是否包含值:aaa1 = "+map1.containsValue("aaa1"));
}
}
运行输出结果为:
map1.get("1")=aaa1
map1.remove("1")=aaa1
map1.get("1")=null
map1 IsEmpty?=false
map2 IsEmpty?=true
map1 中的键值对的个数size = 3
KeySet=[10, 2, 11]
values=[aaaa10, bbb2, bbbb11]
entrySet=[10=aaaa10, 2=bbb2, 11=bbbb11]
map1 是否包含键:11 = true
map1 是否包含值:aaa1 = false
在该例子中,我们创建一个HashMap,并使用了一下Map接口中的各个方法。
其中Map中的entrySet()方法返回一个实现 Map.Entry 接口的对象集合。集合中每个对象都是底层 Map 中一个特定的键-值对。
我们再看看排序的Map是如何使用:
import java.util.*;
public class MapSortExample {
public static void main(String args[]) {
Map map1 = new HashMap();
Map map2 = new LinkedHashMap();
for(int i=0;i<10;i++){
double s=Math.random()*100;//产生一个随机数,并将其放入Map中
map1.put(new Integer((int) s),"第 "+i+" 个放入的元素:"+s+"\n");
map2.put(new Integer((int) s),"第 "+i+" 个放入的元素:"+s+"\n");
}
System.out.println("未排序前HashMap:"+map1);
System.out.println("未排序前LinkedHashMap:"+map2);
//使用TreeMap来对另外的Map进行重构和排序
Map sortedMap = new TreeMap(map1);
System.out.println("排序后:"+sortedMap);
System.out.println("排序后:"+new TreeMap(map2));
}
}
该程序的一次运行结果为:
未排序前HashMap:{64=第 1 个放入的元素:64.05341725531845
, 15=第 9 个放入的元素:15.249165766266382
, 2=第 4 个放入的元素:2.66794706854534
, 77=第 0 个放入的元素:77.28814965781416
, 97=第 5 个放入的元素:97.32893518378948
, 99=第 2 个放入的元素:99.99412014935982
, 60=第 8 个放入的元素:60.91451419025399
, 6=第 3 个放入的元素:6.286974058646977
, 1=第 7 个放入的元素:1.8261658496439903
, 48=第 6 个放入的元素:48.736039522423106
}
未排序前LinkedHashMap:{77=第 0 个放入的元素:77.28814965781416
, 64=第 1 个放入的元素:64.05341725531845
, 99=第 2 个放入的元素:99.99412014935982
, 6=第 3 个放入的元素:6.286974058646977
, 2=第 4 个放入的元素:2.66794706854534
, 97=第 5 个放入的元素:97.32893518378948
, 48=第 6 个放入的元素:48.736039522423106
, 1=第 7 个放入的元素:1.8261658496439903
, 60=第 8 个放入的元素:60.91451419025399
, 15=第 9 个放入的元素:15.249165766266382
}
排序后:{1=第 7 个放入的元素:1.8261658496439903
, 2=第 4 个放入的元素:2.66794706854534
, 6=第 3 个放入的元素:6.286974058646977
, 15=第 9 个放入的元素:15.249165766266382
, 48=第 6 个放入的元素:48.736039522423106
, 60=第 8 个放入的元素:60.91451419025399
, 64=第 1 个放入的元素:64.05341725531845
, 77=第 0 个放入的元素:77.28814965781416
, 97=第 5 个放入的元素:97.32893518378948
, 99=第 2 个放入的元素:99.99412014935982
}
排序后:{1=第 7 个放入的元素:1.8261658496439903
, 2=第 4 个放入的元素:2.66794706854534
, 6=第 3 个放入的元素:6.286974058646977
, 15=第 9 个放入的元素:15.249165766266382
, 48=第 6 个放入的元素:48.736039522423106
, 60=第 8 个放入的元素:60.91451419025399
, 64=第 1 个放入的元素:64.05341725531845
, 77=第 0 个放入的元素:77.28814965781416
, 97=第 5 个放入的元素:97.32893518378948
, 99=第 2 个放入的元素:99.99412014935982
}
从运行结果,我们可以看出,HashMap的存入顺序和输出顺序无关。而LinkedHashMap 则保留了键值对的存入顺序。TreeMap则是对Map中的元素进行排序。在实际的使用中我们也经常这样做:使用HashMap或者LinkedHashMap 来存放元素,当所有的元素都存放完成后,如果使用则是需要一个经过排序的Map的话,我们再使用TreeMap来重构原来的Map对象。这样做的好处是:因为HashMap和LinkedHashMap 存储数据的速度比直接使用TreeMap 要快,存取效率要高。当完成了所有的元素的存放后,我们再对整个的Map中的元素进行排序。这样可以提高整个程序的运行的效率,缩短执行时间。
这里需要注意的是,TreeMap中是根据键(Key)进行排序的。而如果我们要使用TreeMap来进行正常的排序的话,Key 中存放的对象必须实现Comparable 接口。
我们再回到Map中来,Java提高的API中除了上面介绍的几种Map比较常用以为还有一些Map,大家可以了解一下:
u WeakHashMap: WeakHashMap 是 Map 的一个特殊实现,它只用于存储对键的弱引用。当映射的某个键在 WeakHashMap 的外部不再被引用时,就允许垃圾收集器收集映射中相应的键值对。使用 WeakHashMap 有益于保持类似注册表的数据结构,其中条目的键不再能被任何线程访问时,此条目就没用了。
u IdentifyHashMap: Map的一种特性实现,关键属性的hash码不是由hashCode()方法计算,而是由System.identityHashCode 方法计算,使用==进行比较而不是equals()方法。
通过简单的对与Map中各个常用实现类的使用,为了更好的理解Map,下面我们再来了解一下Map的实现原理。
1.4.4 实现原理
下面我们以HashMap为例,对Map的实现机制作一下更加深入一点的理解。Hash,一般翻译做“散列”,也有直接音译为"哈希"的,我们建立一个HashTable(哈希表),该表的长度为N,然后我们分别在该表中的格子中存放不同的元素。每个格子下面存放的元素又是以链表的方式存放元素。哈希码就是将对象的信息经过一些转变形成一个独一无二的int值,这个值存储在一个array中。我们都知道所有存储结构中,array查找速度是最快的。所以,可以加速查找。发生冲突时,让array指向多个values。即,数组每个位置上又生成一个梿表。
使用keySet()抽取key序列,将map中的所有keys生成一个Set。
使用values()抽取value序列,将map中的所有values生成一个Collection。
为什么一个生成Set,一个生成Collection?因为key总是独一无二的,value允许重复。
u 当添加一个新的元素Entry 的时候,首先我们通过一个Hash函数计算出这个Entry元素的Hash值hashcode。通过该hashcode值,就可以直接定位出我们应该把这个Entry元素存入到Hash表的哪个格子中,如果该格子中已经存在元素了,那么只要把新的Entry元存放到这个链表中即可。
u 如果要查找一个元素Entry的时候,也同样的方式,通过Hash函数计算出这个Entry元素的Hash值hashcode。然后通过该hashcode值,就可以直接找到这个Entry是存放到哪个格子中的。接下来就对该格子存放的链表元素进行逐个的比较查找就可以了。
举一个比较简单的例子来说明这个算法的运算方式:
假定我们有一个长度为8的Hash表(可以理解为一个长度为8的数组)。在这个Hash表中存放数字:如下表
0
1
2
3
4
5
6
7
假定我们的Hash函数为:
Hashcode = X%8 -------- 对8 取余数
其中X就是我们需要放入Hash表中的数字,而这个函数返回的Hashcode就是Hash码。
假定我们有下面10个数字需要依次存入到这个Hash表中:
11 , 23 , 44 , 9 , 6 , 32 , 12 , 45 , 57 , 89
通过上面的Hash函数,我们可以得到分别对应的Hash码:
11――3 ; 23――7 ;44――4 ;9――1;6――6;32――0;12――4;45――5;57――1;89――1;
计算出来的Hash码分别代表,该数字应该存放到Hash表中的哪个对应数字的格子中。如果改格子中已经有数字存在了,那么就以链表的方式将数字依次存放在该格子中,如下表:
0
1
2
3
4
5
6
7
32
9
11
44
45
6
23
57
12
89
展开阅读全文