浅谈Java的集合体系
集合体系框架图
集合接口
Java集合类库将接口(interface)与实现(implementation)分离,如上图,Set是一个集合接口,而HashSet与TreeSet都是实现了Set接口的子类。
从上图中也可以看出,集合的基本接口是Collection。Collection<E>接口里有个iterator()方法(来自Iterable接口),该方法返回了一个实现了Iterator接口的对象,可以使用这个迭代器对象依次访问集合中的元素。
既然要迭代访问集合中的元素,那么就必须要考虑到如何获取下一个元素,如何判断已经遍历到结尾,如何移除一个元素等,所以Iterator接口就定义了这三个方法分别对应上述三个问题:next()、hasNext()、remove()等。
使用Iterator遍历的简单方式是for…each。而由集合框架体系图可以知道,Collection扩展了Iterable接口,并且所有集合都实现自Collection接口,所以我们可以知道:标准类库的任何集合都可以使用for…each循环。
使用for each的简单举例如下:
List<String> strs = new ArrayList<String>()
//此处省略,add some params;
for(String str: strs){
//do sth;
}
具体的集合
链表
如果从一个数组的中间位置删除一个元素,之后的所有元素都要向前移动一个位置。插入一个元素也是如此(之后的元素统一要向后移动),代价是比较大的。
而链表则不同。链表将每个对象存放在独立的节点中,每个节点还存放着下一个节点的引用,并且链表都是双向链接的(存放着上、下节点的引用)。所以,从链表中删除一个元素,只需要更新所删除元素附近的节点即可。可以想象一下,几个小朋友手拉手,站在中间的小明同学离开了,那么只要小明左边和右边的小朋友重新拉手就好了。
链表是一个有序集合,每个对象的位置比较重要。由于迭代器是描述集合位置的,所以,依赖位置的元素的添加由迭代器来负责。
使用迭代器添加元素只针对有序集合有意义,像Set这种无序集合就没有位置可言,但这些无序集合也都是Iterator的派生类,那么如果Iterator接口定义了add方法,就导致像Set这种集合也必须强制实现Iterator里的这个add方法,因此,Iterator接口里就没有定义add方法,而把add方法定义在一个子接口ListIterator中。所以你再看一下开始的框架体系图,分出来一个ListIterator接口之后,仅仅让有顺序需求的集合去实现它。
链表也有软肋,例如不支持快速随机访问。如果要查看链表中第n个元素,就必须从头开始,越过n-1个元素。使用链表的唯一理由是:尽可能减少在列表中间插入或删除元素所付出的代价。如果列表中只有少数几个元素,用ArrayList就OK了。如果需要对集合进行随机访问,就使用数组或者ArrayList,不要使用链表。
数组列表
有两种访问元素的协议,一种是上文提到的使用迭代器,还有一种是使用get和set来随机访问。数组对于后者很管用。
ArrayList封装了一个动态再分配的对象数组。Vector也是个数组。二者的区别是,Vector的所有方法都是同步的,如果只有一个线程访问Vector,代码要在同步操作上浪费大量时间。而ArrayList不是同步的,因此,在不需要同步时使用ArrayList。
散列集
散列表可以快速查找对象。散列表为每个对象计算一个散列码(hash code,由对象中的实例域产生的一个整数)。不同数据域的对象产生不同的散列码。所以,如果自己定义类,就要负责实现这个类的hashCode方法。但是要注意,自己实现的hashCode方法应该与equals兼容(a.equals(b),则a与b的散列码必须相同)。
在Java中,散列表用链表数组实现,每个链表称为桶(bucket)。(如上图有16个桶)。在查找表中对象的位置时,先计算对象的散列码,然后与桶的总数取余。(散列码 % 桶的总数)。如果计算出的对象所在的桶已经满了,没法放置该对象,这就造成散列冲突(hashcollision)。这时,用新对象与桶中所有对象比较,看该对象是否已存在。如果某个桶快要满了,就要进行再散列,创建一个更大的桶,并将所有元素插入到这个新桶中,丢弃原来的桶。
HashSet实现了基于散列表的集,不关心元素的顺序时,使用HashSet。
树集
TreeSet是个有序集合,以任意顺序将元素插入到集合中,对集合遍历时,每个值将自动按照排序后的顺序呈现。排序是用树结构完成的(如红黑树)。在需要排序时使用TreeSet,不需要排序时使用HashSet。
那么TreeSet是如何排列元素的呢?
TreeSet假定插入的元素都实现了Comparable接口。如果插入自定义对象,就必须通过实现Comparable接口定义排序规则,然后它就会去找这个覆盖的方法查看规则。
但是这样有个灾难性的问题:如果某个对象在某个集合中按A方式排序,在另一个集合中按B方式排序,那就没辙了,能覆盖的方法只有一个,规则只能定一次。
所以,game over?不。TreeSet基于上面的问题,便有了另一种排序方式:使用Comparator接口。该接口声明了一个compare方法。如果按照A方式排序,那么就定义一个实现了Comparator接口的类,将这个类的对象塞到TreeSet的构造器中。可能需要一个例子说明一下:
public static void main(String args[]){
SortedSet<Item> parts = new TreeSet<Item>();
parts.add(new Item("张三","100"));
parts.add(new Item("李四","130"));
parts.add(new Item("王二麻子","120"));
SortedSet<Item> sortByWeight = new TreeSet<Item>(new Comparator<Item>(){
public int compare(Item a,Item b){
String weight1 = a.getWeight();
String weight2 =b.getWeight();
return weight1.compareTo(weight2);
};
});
sortByWeight.addAll(parts);
System.out.println(sortByWeight.toString());
}
队列和双端队列
队列可以让人们有效的在尾部添加一个元素,在头部删除一个元素。双端队列可以在头部、尾部同时添加或者删除元素。
Deque接口由ArrayDeque和LinkedList类实现。这两个类都提供了双端队列,且必要时可以增加队列长度。
映射表
HashMap和TreeMap是映射表的两个通用的实现。HashMap对键进行散列,TreeMap用键的整体排序对元素进行排序,并将其组织成搜索树。
浅谈Java的集合体系的更多相关文章
- 浅谈Java的集合框架
浅谈Java的集合框架 一. 初识集合 重所周知,Java有四大集合框架群,Set.List.Queue和Map.四种集合的关注点不同,Set 关注事物的唯一性,List 关注事物的索引列表,Q ...
- 浅谈JAVA集合框架
浅谈JAVA集合框架 Java提供了数种持有对象的方式,包括语言内置的Array,还有就是utilities中提供的容器类(container classes),又称群集类(collection cl ...
- !! 浅谈Java学习方法和后期面试技巧
浅谈Java学习方法和后期面试技巧 昨天查看3303回复33 部落用户大酋长 下面简单列举一下大家学习java的一个系统知识点的一些介绍 一.java基础部分:java基础的时候,有些知识点是非常重要 ...
- 【转】浅谈Java中的hashcode方法(这个demo可以多看看)
浅谈Java中的hashcode方法 哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: public native i ...
- 浅谈java类集框架和数据结构(2)
继续上一篇浅谈java类集框架和数据结构(1)的内容 上一篇博文简介了java类集框架几大常见集合框架,这一篇博文主要分析一些接口特性以及性能优化. 一:List接口 List是最常见的数据结构了,主 ...
- 浅谈Java线程安全
浅谈Java线程安全 - - 2019-04-25 17:37:28 线程安全 Java中的线程安全 按照线程安全的安全程序由强至弱来排序,我们可以将Java语言中各种操作共享的数据分为以下五类 ...
- 【转】浅谈Java中的hashcode方法
哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: public native int hashCode(); 根据这个 ...
- 浅谈Java中的hashcode方法(转)
原文链接:http://www.cnblogs.com/dolphin0520/p/3681042.html 浅谈Java中的hashcode方法 哈希表这个数据结构想必大多数人都不陌生,而且在很多地 ...
- 浅谈Java中set.map.List的区别
就学习经验,浅谈Java中的Set,List,Map的区别,对JAVA的集合的理解是想对于数组: 数组是大小固定的,并且同一个数组只能存放类型一样的数据(基本类型/引用类型),JAVA集合可以存储和操 ...
随机推荐
- 【angularjs】【学习心得】路由实战篇
今天还是来说一下angular中的路由模块.我们实际项目中,各个页面的切换是经常会与Auth相关的.比如我网站的后台,是需要登录过的用户才能进去,那么我们用angularJS做前端路由的时候应该怎么完 ...
- 负载均衡软件LVS分析三(配置)
LVS集群有DR.TUN.NAT三种配置模式,可以对www服务.FTP服务.MAIL服务等做负载均衡,下面通过搭建www服务的负载均衡实例,讲述基于DR模式的LVS集群配置. 一. Director ...
- Mongoose与bluebird结合使用实例
nodejs的所有调用几乎是全异步的,而所有的IO操作也都是通过回调函数才能知道结果. 如果一个异步调用依赖另一个异步调用,如果没有Promise的话,有可能陷入传说中的回调地狱. bluebird实 ...
- Treeview显示磁盘下的文件,并且可操作
#region TreeView树形显示磁盘下文件夹 /// <summary> /// IconIndexs类 对应ImageList中5张图片的序列 /// </summary& ...
- java算法 蓝桥杯 扶老奶奶街
一共有5个红领巾,编号分别为A.B.C.D.E,老奶奶被他们其中一个扶过了马路. 五个红领巾各自说话: A :我和E都没有扶老奶奶 B :老奶奶是被C和E其中一个扶过大街的 C :老奶奶是被我和D其中 ...
- 性能监控之Java程序执行解析
大家好,最近接触javassist技术,研究过程中对Java程序执行过程进行了一系列探索,弄清楚了几个盲区(仅针对个人而言),现将经验与大家分享. 1.编码->.java 通常指写代码的过程,最 ...
- WinForm 制作一个简单的计算器
namespace WindowsFormsApplication6 { public partial class Form1 : Form { //存储上次点击了什么按钮,0代表什么都没有点击,1代 ...
- C++编程练习(16)----“排序算法 之 快速排序“
快速排序 基本思想: 通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的. 算法介绍: 设要排序的 ...
- swift注意
赋值的时候要想为空 可以用 ? 例如 var age1:Int? // ?表示age1的类型为可选类型,其值可以为空print(age1) 判断一个字符串为空字符串if str_empty.isE ...
- 测试指南(适用于Feature/promotion/bug)
1.提前了解需求,在需求的业务基础和开发的架构基础上分析测试关键点,给出测试策略,甚至需要准备测试数据: 2.分析需求时不要受开发影响,要有自己的分析和判断,包括测试范围,测试时间: 3.在开始测试之 ...