首先java中集合类主要有两大分支
本文仅分析部分原理和集合类的特点,不分析源码,旨在对java的集合类有一个整体的认识,理解各个不同类的关联和区别,让大家在不同的环境下学会选择不同的类来处理。
Java中的集合类包含的内容很多而且很重要,很多数据的存储和处理(排序,去重,筛选等)都需要通过集合类来完成。
首先java中集合类主要有两大分支:
(1)Collection (2)Map
先看它们的类图:
(1)Collection
Collection
(2)Map
Map
可以看到它们之间的关系纷繁复杂,如果不系统的学习一下,还真是不知道有什么区别,该怎么选择。由于HashSet的内部实现原理是使用了HashMap,所以我们的学习路线为先学习Map集合类,然后再来学习Collection集合类。
(1)HashMap和Hashtable ( 注意table是小写的t,搞不懂为什么要这样,老是会写错。。。)
首先来看HashMap和HashTable,这两兄弟经常被放到一起来比较,那么它们有什么不一样呢?
a.HashMap不是线程安全的;HashTable是线程安全的,其线程安全是通过Sychronize实现。
b.由于上述原因,HashMap效率高于HashTable。
c.HashMap的键可以为null,HashTable不可以。
d.多线程环境下,通常也不是用HashTable,因为效率低。HashMap配合Collections工具类使用实现线程安全。同时还有ConcurrentHashMap可以选择,该类的线程安全是通过Lock的方式实现的,所以效率高于Hashtable。
好,比较了他们的不一样后,来讲讲它们的原理。
数组,链表,哈希表。各有优劣,顺便提一下,数组连续内存空间,查找速度快,增删慢;链表充分利用了内存,存储空间是不连续的,首尾存储上下一个节点的信息,所以寻址麻烦,查找速度慢,但是增删快;哈希表呢,综合了它们两个的有点,一个哈希表,由数组和链表组成。假设一条链表有1000个节点,现在查找最后一个节点,就得从第一个遍历到最后一个;如果用哈希表,将这条链表分为10组,用一个容量为10数组来存储这10组链表的头结点(a[0] = 0 , a[1] = 100 , a[2] = 200 …)。这样寻址就快了。
HashMap实现原理就是上述原理了,当然其具体实现还有很多其他的东西。Hashtable同理,只不过做了同步处理。
Hash碰撞,不同的key根据hash算法算出的值可能一样,如果一样就是所谓的碰撞。
优化措施:
(1) HashMap的扩容代价非常大,要生成一个新的桶数组,然后要把所有元素都重新Hash落桶一次,几乎等于重新执行了一次所有元素的put。所以如果我们对Map的大小有一个范围的话,可以在构造时给定大小,一般大小设置为:(int) ((float) expectedSize / 0.75F + 1.0F)。
(2) key的设计尽量简洁。
HashMap一些功能实现:
a.按值排序
HashMap按值排序通过Collections的sort方法,在实现排序之前,我们先看看HashMap的几种遍历方式:
//Collection And Map
public static void testCM(){
//Collection
Map<Integer , String> hs = new HashMap<Integer , String>();
int i = 0;
hs.put(199, "序号:"+201);
while(i<50){
hs.put(i, "序号:"+i);
i++;
}
hs.put(-1, "序号:"+200);
hs.put(200, "序号:"+200);
//遍历方式一:for each遍历HashMap的entryset,注意这种方式在定义的时候就必须写成
//Map<Integer , String> hs,不能写成Map hs;
for(Entry<Integer , String> entry : hs.entrySet()){
System.out.println("key:"+entry.getKey()+" value:"+entry.getValue());
}
//遍历方式二:使用EntrySet的Iterator
Iterator<Map.Entry<Integer , String>> iterator = hs.entrySet().iterator();
while(iterator.hasNext()){
Entry<Integer , String> entry = iterator.next();
System.out.println("key:"+entry.getKey()+" value:"+entry.getValue());
};
//遍历方式三:for each直接使用HashMap的keyset
for(Integer key : hs.keySet()){
System.out.println("key:"+key+" value:"+hs.get(key));
};
//遍历方式四:使用keyset的Iterator
Iterator keyIterator = hs.keySet().iterator();
while(keyIterator.hasNext()){
Integer key = (Integer)keyIterator.next();
System.out.println("key:"+key+" value:"+hs.get(key));
(1)使用keyset的两种方式都会遍历两次,所以效率没有使用EntrySet高。
(2)HashMap输出是无序的,这个无序不是说每次遍历的结果顺序不一样,而是说与插入顺序不一样。
接下来我们看按值排序,注释比较详细就不赘述过程了。
//对HashMap排序
public static void sortHashMap(Map<Integer , String> hashmap){
System.out.println("排序后");
//第一步,用HashMap构造一个LinkedList
Set<Entry<Integer , String>> sets = hashmap.entrySet();
LinkedList<Entry<Integer , String>> linkedList = new LinkedList<Entry<Integer , String>>(sets);
//用Collections的sort方法排序
Collections.sort(linkedList , new www.thd178.com/ Comparator<Entry<Integer , String>>(){
@Override
public int compare(Entry<Integer , String> o1, Entry<Integer , String>www.2018yulpt.com o2) {
// TODO Auto-generated method stub
/*String object1 = (String) o1.getValue();
String object2 = (String) o2.getValue();
return object1.compareTo(object2);*/
return o1.getValue().compareTo(o2.getValue());
}
});
//第三步,将排序后的list赋值给LinkedHashMap
Map<Integer , String> map = new LinkedHashMap();
for(Entry<Integer , String> entry : linkedList){
map.put(entry.getKey(), entry.getValue());
}
for(Entry<Integer , String> entry : map.entrySet()){
System.out.println("key:"+entry.getKey()+" value:"+entry.getValue());
b.按键排序
HashMap按键排序要比按值排序方法容易实现,而且方法很多,下面一一介绍。
第一种:还是熟悉的配方还是熟悉的味道,用Collections的sort方法,只是更改一下比较规则。
第二种:TreeMap是按键排序的,默认升序,所以可以通过TreeMap来实现。
public static void sortHashMapByKey(Map hashmap){
System.out.println("按键排序后");
//第一步:先创建一个TreeMap实例,构造函数传入一个Comparator对象。
TreeMap<Integer , String> treemap = new TreeMap<Integer , String>(new Comparator<Integer>(){
@Override
public int compare(Integer o1,Integer o2) {
// TODO Auto-generated method stub
return Integer.compare(o1, o2);
}
});
//第二步:将要排序的HashMap添加到我们构造的TreeMap中。
treemap.putAll(hashmap);
for(Entry<Integer , String> entry : treemap.entrySet()){
System.out.println("key:"+entry.getKey()+" value:"+entry.getValue());
第三种:可以通过keyset取出所有的key,然后将key排序,再有序的将key-value键值对存到LinkedHashMap中,这个就不贴代码了,有兴趣的可以自己去尝试一下。
c.value去重
对于HashMap而言,它的key是不能重复的,但是它的value是可以重复的,有的时候我们要将重复的部分剔除掉。
方法一:将HashMap的key-value对调,然后赋值给一个新的HashMap,由于key的不可重复性,此时就将重复值去掉了。最后将新得到的HashMap的key-value再对调一次即可。
d.HashMap线程同步
第一种:
Map<Integer , String> hs = new HashMap<Integer , String>(www.ccyl178.com/);
hs = Collections.synchronizedMap(hs);
1
2
第二种:
ConcurrentHashMap<Integer , String> hs = new ConcurrentHashMap<Integer , String>();
1
(2)IdentifyHashMap
IdentityHashMap与HashMap基本相似,只是当两个key严格相等时,即key1==key2时,它才认为两个key是相等的 。IdentityHashMap也允许使用null,但不保证键值对之间的顺序。
(3)WeakHashMap
WeakHashMap与HashMap的用法基本相同,区别在于:后者的key保留对象的强引用,即只要HashMap对象不被销毁,其对象所有key所引用的对象不会被垃圾回收,HashMap也不会自动删除这些key所对应的键值对对象。但WeakHashMap的key所引用的对象没有被其他强引用变量所引用,则这些key所引用的对象可能被回收。WeakHashMap中的每个key对象保存了实际对象的弱引用,当回收了该key所对应的实际对象后,WeakHashMap会自动删除该key所对应的键值对。
接下来是Collection接口及其子类:
(4)ArrayList , LinkedList , Vector
(1)首先,说说它们的关系和区别。ArrayList和Vector本质都是用数组实现的,而LinkList是用双链表实现的;所以,Arraylist和Vector在查找效率上比较高,增删效率比较低;LinkedList则正好相反。ArrayList是线程不安全的,Vector是线程安全的,效率肯定没有ArrayList高了。实际中一般也不怎么用Vector,可以自己做线程同步,也可以用Collections配合ArrayList实现线程同步。
(2)Tips
前面多次提到扩容的代价很高,所以如果能确定容量的大致范围就可以在创建实例的时候指定,注意,这个仅限于ArrayList和Vector哟:
ArrayList arrayList = new ArrayList(www.xyyulept.com100);
arrayList.ensureCapacity(200);
Vector vector = new Vector(100);
vector.ensureCapacity(200);
(3)其他功能实现
a.排序
List的排序的话就是使用Collections的sort方法,构造Comparator或者让List中的对象实现Comparaable都可以,这里就不贴代码了。
b.去重
第一种:用Iterator遍历,遍历出来的放到一个临时List中,放之前用contains判断一下。
第二种:利用set的不可重复性,只需三步走。
//第一步:用HashSet的特性去重
HashSet tempSet = new HashSet(arrayList);
//第二步:将arrayList清除
tempSet.clear();
//第三步:将去重后的重新赋给List
arrayList.addAll(tempSet);
(5)Stack
Stack呢,是继承自Vector的,所以用法啊,线程安全什么的跟Vector都差不多,只是有几个地方需要注意:
第一:add()和push(),stack是将最后一个element作为栈顶的,所以这两个方法对stack而言是没什么区别的,但是,它们的返回值不一样,add()返回boolean,就是添加成功了没有;push()返回的是你添加的元素。为了可读性以及将它跟栈有一丢丢联系,推荐使用push。
第二:peek()和pop(),这两个方法都能得到栈顶元素,区别是peek()只是读取,对原栈没有什么影响;pop(),从字面上就能理解,出栈,所以原栈的栈顶元素就没了。
(6)HashSet和TreeSet
Set集合类的特点就是可以去重,它们的内部实现都是基于Map的,用的是Map的key,所以知道为什么可以去重复了吧。
既然要去重,那么久需要比较,既然要比较,那么久需要了解怎么比较的,不然它将1等于2了,你怎么办?
比较是基于hascode()方法和equals()方法的,所以必要情况下需要重新这两个方法。
好了,到了总结的时候了,其实你会发现集合类虽然看起来多,但是都是很有规律的。ArrayList,LinkedList一个无序,一个有序;HashSet,TreeSet一个无序,一个有序;HashMap,LinkedHasmMap,一个无序,一个有序;Vector和HashTable,Stack是线程安全的,但是效率低;线程不安全的类都可以配合Collections得到线程安全的类。
首先java中集合类主要有两大分支的更多相关文章
- java环境中基于jvm的两大语言:scala,groovy
一.java环境中基于jvm的两大语言:scala,groovy 可以在java项目里混编这两种语言: scala:静态语言,多范式语言,糅合了面向对象.面向过程:可以与java和net互操作:融汇了 ...
- Java中Compareable和Comparator两种比较器的区别
Java中Compareable和Comparator两种比较器的区别 参考原文链接:https://www.cnblogs.com/ldy-blogs/p/8488138.html 1.引言 在ja ...
- Java中HashMap遍历的两种方式
Java中HashMap遍历的两种方式 转]Java中HashMap遍历的两种方式原文地址: http://www.javaweb.cc/language/java/032291.shtml 第一种: ...
- Java中取小数点后两位(四种方法)
摘自http://irobot.iteye.com/blog/285537 Java中取小数点后两位(四种方法) 一 Long是长整型,怎么有小数,是double吧 java.text.D ...
- java中数组复制的两种方式
在java中数组复制有两种方式: 一:System.arraycopy(原数组,开始copy的下标,存放copy内容的数组,开始存放的下标,需要copy的长度); 这个方法需要先创建一个空的存放cop ...
- Java中产生随机数的两个方法
Java中产生随机数的两个方法 一.利用random方法来生成Java随机数. 在Java语言中生成Java随机数相对来说比较简单,因为有一个现成的方法可以使用.在Math类中,Java语言提供了一个 ...
- java中集合类详解
集合类说明及区别 Collection ├List │├LinkedList │├ArrayList │└Vector │ └Stack └Set Map ├Hashtable ├HashMap └W ...
- JAVA中集合类的使用
总的说来常用的集合类有两大类:Collection 和 Map 1) Collection接口有List和Set两大类子接口,List有ArrayList.LinkedList.Vector子类,Se ...
- java中String初始化的两种方式
转自:http://www.diybl.com/course/3_program/java/javajs/2007104/75886.html 字符串可能是任何程序语言中都会出现的对象,j ...
随机推荐
- 类加载, 静态变量初始化, String不可变, 泛型使用, 内部类
1.java变量类型 java变量类型分:基本数据类型变量和Object数据类型变量,变量也是占用者内存的 例如: int i = 3; i这个变量保存的就是整形3, 占32位 Object a = ...
- [python]记录Windows下安装matplot的经历
最近学习在看<机器学习实战>一书,第二章的时候要用到Natplotlib画图,于是便开始安装Matplotlib.本文所用到的所有安装包都可以在文末的链接中找到. 首先从Matplotli ...
- KNN算法的R语言实现
近邻分类 简言之,就是将未标记的案例归类为与它们最近相似的.带有标记的案例所在的类. 应用领域: 1.计算机视觉:包含字符和面部识别等 2.推荐系统:推荐受众喜欢电影.美食和娱乐等 3.基因工程:识别 ...
- Java 多线程(二)之 Thread 优先级
目录 Thread 中线程优先级相关属性 相关函数 优先级初始化 设置优先级 获取优先级 默认优先级 指定优先级 注意事项 优先级继承 @ Thread 中线程优先级相关属性 每个线程均有优先级,在 ...
- NetBeans的(默认)快捷键
NetBeans的(默认)快捷键 1.完成代码:ctrl+\ //任何地方按下此组合键,均会提示相应的参考字段: 2.错误提示:alt + enter //顾名思义,当系统报错时,按下此组合可以查看 ...
- Mvc4_@Styles.Render提高性能
在页面上可以用@Styles.Render("~/Content/css") 来加载css 首先要在App_Start 里面BundleConfig.cs 文件里面 添加要包含的c ...
- SSM整合配置(Spring+Spring MVC+Mybatis)
一.配置准备 通过Maven工程,在eclipse中整合SSM,并在Tomcat服务器上运行 在进行配置前,先理清楚要配置哪些文件,如图,除web.xml外,其余三个配置文件名称均可自定义: 如图 ...
- Unity3D — — UGUI之简易背包
Uinity版本:2017.3 最近在学Siki老师的<黑暗之光RPG>教程,由于教程内用的是NGUI实现,而笔者本人用的是UGUI,所以在这里稍微写一下自己的实现思路(大致上和NGUI一 ...
- 全局最小割StoerWagner算法详解
前言 StoerWagner算法是一个找出无向图全局最小割的算法,本文需要读者有一定的图论基础. 本文大部分内容与词汇来自参考文献(英文,需***),用兴趣的可以去读一下文献. 概念 无向图的割:有无 ...
- 【Alpha】功能规格说明书
更新说明:从用户需求分析中剥离有关用户场景分析部分,加入功能规格说明书. Github地址:https://github.com/buaase/Phylab-Web/blob/master/docs/ ...