首页 > 代码库 > [转]Java之Collection/Map
[转]Java之Collection/Map
转自:
http://www.cnblogs.com/mingcn/archive/2010/10/22/JavaContainer.html
Java复习笔记——容器知识点总结
Java中容器分两类,一种是单值的Collection,一种是储存键-值对的Map
Collection
又分两种,Set和List,区别在于Set是不可重复的,而List是可重复的
Set
常用有两种:HashSet和TreeSet,其内部实现是一个Map,它的元素就相当于Map中的Key,而Value是一个Object常量
1 2 3 4 5 6 7 | private transient HashMap<E,Object> map; private static final Object PRESENT = new Object(); public HashSet() { map = new HashMap<E,Object>(); } |
1 2 3 4 5 6 7 | private transient NavigableMap<E,Object> m; private static final Object PRESENT = new Object(); public TreeSet() { this ( new TreeMap<E,Object>()); } |
所有对Set的操作都被最终转嫁为对Map的操作,具体细节在下面Map中讲述
List
对Collection接口进行了简单的扩充,你可以将任何对象放到放到一个List容器中,并在需要时从中取出。
常用的有ArrayList和LinkedList,都是有顺序,可重复的集合类型
ArrayList:顾名思义,数据列表,其实就是封装了一个数组,因此它的访问速度极快
1 2 3 | private transient Object[] elementData; private int size; |
然后封装了一些对数组的常用操作如插入、删除等。
说到ArrayList不得不提下Arrays类和数组[]
ArrayList可以储存不同类型的对象(虽然一般不推荐这样做),而数组只能是同一类型
ArrayList可以动态增加长度,但效率不高,而数组只能是固定长度,却十分高效。每当执行add/insert等添加元素的方法,都会先检查数组长度是否足够,如果是,它就会以当前容量的3/2倍+1来重新构建一个数组,将旧元素Copy到新数组中,然后丢弃旧数组,在这个临界点的扩容操作,应该来说是比较影响效率的。
ArrayList提供常用方法,add/get/indexOf/remove等,而相应的Arrays没有提供add和remove方法,查询元素索引要先sort再binarySearch
ArrayList排序需外部方法Collections.sort(..),数组排序则使用Arrays.sort(..),二者都可以使用自然顺序(实现Comparable)或用Comparator指定
LinkedList:很显然的是链表,内部实现是带头结点的双向链表,适合于在链表中间需要频繁进行插入和删除操作
1 2 3 4 5 6 7 8 9 10 11 12 | private transient Entry<E> header = new Entry<E>( null , null , null ); private transient int size = 0 ; private static class Entry<E> { E element; Entry<E> next; Entry<E> previous; //.. } |
Map
是一种把键和值对象进行关联的容器,类比Collection,可以这样说:Collection对象中某个值的ID是它在容器中的索引值,而在Map中,某个值的ID是它对应的键。这样我们使用Map时就不用局限于int型的索引值,可以用任何类型的对象作索引。正是由于Key的索引特性,Map中不允许有同值的Key存在(前文讲到Set内部实现是Map中的Key的集合,所以Set的元素不能重复)。当然,Value是可以重复的,甚至可以是同一个Reference。Map在处理相同的Key时,将新值存入,旧值被替换并返回。
HashMap:采用Hash算法,以便快速查找某个元素。
将键的哈希值作为内存索引依据,内部实现是一个适当长度的链式数组,由Key的Hash值对应数组下标,再间接对应内存,是一种比较高效的存取方式。Key的hashCode()相同的情况下,放在同一个单项链表结构中。
一个HashMap中Key的类型应该重写hashCode()方法,保证在两个对象equals为true时返回相同的值,否则在重复性检查时会直接被当做不同的键,造成不可预期的后果。
Hash容器中判断重复的方式:
先比较hash(key.hashCode())是否相同,hash(int)是一个内部算法,具体细节不作讨论
不同,则不重复
相同,则
比较两个Key是否是同一引用
是,则重复
不是,再调用equals方法
返回true则重复
返回false则不重复
TreeMap:采用树型储存结构按序存放,因此它便有一些扩展的方法,比如firstKey(),lastKey()等,你还可以从TreeMap中指定一个范围以取得其子Map。
内部实现是一颗二叉排序树,其中序遍历结果为递增序列。所以要求他的Key必须是Comparable或者创建TreeMap的时候指定Comparator。
当Key实现Comparable<E>接口时,必须实现comparaTo(E e)方法,当使用外部比较器(Comparator<T>)时,需实现Comparator<T>的compare(T t1, T t2)方法
相关知识
泛型(Generic)
泛型允许Coding的时候可以定义一些可变的类型,但必须在使用前进行声明具体是哪种类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class testGeneric<T> { T t; testGeneric(T t) { this .t = t; } } class test { testGeneric<String> tgs = new testGeneric( "Hi,Gineric!" ); testGeneric<Integer> tgi = new testGeneric( 100 ); //AutoBoxing { System.out.println(tgs.t); //Output:Hi,Gineric! System.out.println(tgi.t); //AutoUnBoxing&Output:100 } } |
需要注意Java 泛型的类型参数之实际类型在编译时会被消除(upcast to Object),所以无法在运行时得知其类型参数的类型。Java 编译器在编译泛型时会自动加入类型转换的编码,故运行速度不会因为使用泛型而加快。
迭代器(Iterator)
提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。
对于遍历一个容器中所有的元素,Iterator模式是首选的方式
Collection定义了Iterator<E> iterator()方法,子类都各自实现了该方法,我们直接调用即可
Map中虽没有定义,我们可以利用map.entrySet()的iterator()方法
1 2 3 4 5 6 7 | Iterator i = someMap.entrySet().iterator(); while (i.hasNext()) { Map.Entry entry = (Map.Entry) i.next(); Object key = i.getKey(); Object value = http://www.mamicode.com/i.getValue(); //something TODO } |
或者转换为Collection(Set)用增强For循环
1 2 3 4 5 6 | Set<Map.Entry> entrySet = someMap.entrySet(); for (Map.Entry entry : entrySet){ Object key = entry.getKey(); Object value = http://www.mamicode.com/entry.getValue(); //something TODO } |
ListIterator继承了Iterator,提供了对List的双向遍历的方法。
需要注意的是,调用Iterator的remove方法,删除的是最后一次调用next()(or previous(),if possible)所返回的元素
如果进行迭代时用调用此方法之外的其他方式修改了该迭代器所指向的 collection,则迭代器的行为是不确定的。
也就是说,调用Iterator时,最好不要调用Collection的add/remove等方法
另:容器类的toString()方法也是通过调用iterator()方法来实现遍历
对集合使用增强的for循环也是隐式地调用iterator()方法
还有Stack、Queue等结构,操作简单,不作赘述
[转]Java之Collection/Map