List,Set,Map用法以及区别

List,Set,Map用法以及区别


Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java JDK不能提供直接继承自Collection的类,Java JDK提供的类都是继承自Collection的"子接口",如:List和Set。 

注意:Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同key,每个key只能映射一个value。Map接口提供3种集合的视图,Map的内容可以被当做一组key集合,一组value集合,或者一组key-value映射。 


详细介绍: 
List特点:元素有放入顺序,元素可重复 
Map特点:元素按键值对存储,无放入顺序 
Set特点:元素无放入顺序,元素不可重复(注意:元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的) 
List接口有三个实现类:LinkedList,ArrayList,Vector 
LinkedList:底层基于链表实现,链表内存是散乱的,每一个元素存储本身内存地址的同时还存储下一个元素的地址。链表增删快,查找慢 
ArrayList和Vector的区别:ArrayList是非线程安全的,效率高;Vector是基于线程安全的,效率低 
Set接口有两个实现类:HashSet(底层由HashMap实现),LinkedHashSet 
SortedSet接口有一个实现类:TreeSet(底层由平衡二叉树实现) 
Query接口有一个实现类:LinkList 
Map接口有三个实现类:HashMap,HashTable,LinkeHashMap 
  HashMap非线程安全,高效,支持null;HashTable线程安全,低效,不支持null 
SortedMap有一个实现类:TreeMap 
其实最主要的是,list是用来处理序列的,而set是用来处理集的。Map是知道的,存储的是键值对 
set 一般无序不重复.map kv 结构 list 有序 

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 = 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 = entry.getValue();
     //something TODO
}

 

ListIterator继承了Iterator,提供了对List的双向遍历的方法。

需要注意的是,调用Iterator的remove方法,删除的是最后一次调用next()(or previous(),if possible)所返回的元素

如果进行迭代时用调用此方法之外的其他方式修改了该迭代器所指向的 collection,则迭代器的行为是不确定的。

也就是说,调用Iterator时,最好不要调用Collection的add/remove等方法

另:容器类的toString()方法也是通过调用iterator()方法来实现遍历

  对集合使用增强的for循环也是隐式地调用iterator()方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值