Collection是List和Set两个接口的基接口
List在Collection之上增加了"有序"
Set在Collection之上增加了"唯一"
而ArrayList是实现List的类...所以他是有序的.
它里边存放的元素在排列上存在一定的先后顺序
而且ArrayList是采用数组存放元素
另一种List LinkedList采用的则是链表。
Collection和Map接口之间的主要区别在于:Collection中存储了一组对象,而Map存储关键字/值对。
在Map对象中,每一个关键字最多有一个关联的值。
Map:不能包括两个相同的键,一个键最多能绑定一个值。null可以作为键,这样的键只有一个;可以有一个或多个键所对应的
值为null。当get()方法返回null值时,即可以表示Map中没有该键,也可以表示该键所对应的值为null。因此,在Map中不能由get()方法来判断Map中是否存在某个键,而应该用containsKey()方法来判断。
继承Map的类有:HashMap,HashTable
HashMap:Map的实现类,缺省情况下是非同步的,可以通过Map Collections.synchronizedMap(Map m)来达到线程同步
HashTable:Dictionary的子类,缺省是线程同步的。不允许关键字或值为null
当元素的顺序很重要时选用TreeMap,当元素不必以特定的顺序进行存储时,使用HashMap。Hashtable的使用不被推荐,因为HashMap提供了所有类似的功能,并且速度更快。当你需要在多线程环境下使用时,HashMap也可以转换为同步的。
为什么要使用集合类
当你事先不知道要存放数据的个数,或者你需要一种比数组下标存取机制更灵活的方法时,你就需要用到集合类。
理解集合类
集合类存放于java.util包中。
集合类存放的都是对象的引用,而非对象本身,出于表达上的便利,我们称集合中的对象就是指集合中对象的引用(reference)。
集合类型主要有3种:set(集)、list(列表)和map(映射)。
(1)集 (Set):口袋
集(set)是最简单的一种集合,它的对象不按特定方式排序,只是简单的把对象加入集合中,就像往口袋里放东西。
对集中成员的访问和操作是通过集中对象的引用进行的,所以集中不能有重复对象。
集也有多种变体,可以实现排序等功能,如TreeSet,它把对象添加到集中的操作将变为按照某种比较规则将其插入到有序的对象序列中。它实现的是SortedSet接口,也就是加入了对象比较的方法。通过对集中的对象迭代,我们可以得到一个升序的对象集合。
(2)列表 (List):列表
列表的主要特征是其对象以线性方式存储,没有特定顺序,只有一个开头和一个结尾,当然,它与根本没有顺序的集是不同的。
列表在数据结构中分别表现为:数组和向量、链表、堆栈、队列。
关于实现列表的集合类,是我们日常工作中经常用到的,将在后边的笔记详细介绍。
(3)映射 (Map):键值对
映射与集或列表有明显区别,映射中每个项都是成对的。映射中存储的每个对象都有一个相关的关键字(Key)对象,关键字决定了对象在映射中的存储位置,检索对象时必须提供相应的关键字,就像在字典中查单词一样。关键字应该是唯一的。
关键字本身并不能决定对象的存储位置,它需要对过一种散列(hashing)技术来处理,产生一个被称作散列码(hash code)的整数值,散列码通常用作一个偏置量,该偏置量是相对于分配给映射的内存区域起始位置的,由此确定关键字/对象对的存储位置。理想情况下,散列处理应该产生给定范围内均匀分布的值,而且每个关键字应得到不同的散列码。
集合类简介
java.util中共有13个类可用于管理集合对象,它们支持集、列表或映射等集合,以下是这些类的简单介绍
集:
HashSet: 使用HashMap的一个集的实现。虽然集定义成无序,但必须存在某种方法能相当高效地找到一个对象。使用一个HashMap对象实现集的存储和检索操作是在固定时间内实现的.
TreeSet: 在集中以升序对对象排序的集的实现。这意味着从一个TreeSet对象获得第一个迭代器将按升序提供对象。TreeSet类使用了一个TreeMap.
列表:
Vector: 实现一个类似数组一样的表,自动增加容量来容纳你所需的元素。使用下标存储和检索对象就象在一个标准的数组中一样。你也可以用一个迭代器从一个Vector中检索对象。Vector是唯一的同步容器类??当两个或多个线程同时访问时也是性能良好的。(同步的含义:即同时只能一个进程访问,其他等待)
Stack: 这个类从Vector派生而来,并且增加了方法实现栈??一种后进先出的存储结构。
LinkedList: 实现一个链表。由这个类定义的链表也可以像栈或队列一样被使用。
ArrayList: 实现一个数组,它的规模可变并且能像链表一样被访问。它提供的功能类似Vector类但不同步。
映射:
HashTable: 实现一个映象,所有的键必须非空。为了能高效的工作,定义键的类必须实现hashcode()方法和equal()方法。这个类是前面java实现的一个继承,并且通常能在实现映象的其他类中更好的使用。
HashMap: 实现一个映象,允许存储空对象,而且允许键是空(由于键必须是唯一的,当然只能有一个)。
WeakHashMap: 实现这样一个映象:通常如果一个键对一个对象而言不再被引用,键/对象对将被舍弃。这与HashMap形成对照,映象中的键维持键/对象对的生命周期,尽管使用映象的程序不再有对键的引用,并且因此不能检索对象。
TreeMap: 实现这样一个映象,对象是按键升序排列的。
下图是集合类所实现的接口之间的关系:
Set和List都是由公共接口Collection扩展而来,所以它们都可以使用一个类型为Collection的变量来引用。这就意味着任何列表或集构成的集合都可以用这种方式引用,只有映射类除外(但也不是完全排除在外,因为可以从映射获得一个列表。)所以说,把一个列表或集传递给方法的标准途径是使用Collection类型的参数。
List接口
List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。
和下面要提到的Set不同,List允许有相同的元素。
除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。
实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。
ArrayList类
ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。
size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。
每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。ArrayList当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
和LinkedList一样,ArrayList也是非同步的(unsynchronized)。
Map接口
请注意,Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。
HashMap类
HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key。,但是将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者load factor过低。
----------------------------------------------------------------------------
1.-------------------->
List是接口,List特性就是有序,会确保以一定的顺序保存元素.
ArrayList是它的实现类,是一个用数组实现的List.
Map是接口,Map特性就是根据一个对象查找对象.
HashMap是它的实现类,HashMap用hash表实现的Map,就是利用对象的hashcode(hashcode()是Object的方法)进行快速(Hash)散列查找.(关于散列查找,可以参看<<数据结构>>)
2.-------------------->
一般情况下,如果没有必要,推荐代码只同List,Map接口打交道.
比如:List list = new ArrayList();
这样做的原因是list就相当于是一个泛型的实现,如果想改变list的类型,只需要:
List list = new LinkedList();//LinkedList也是List的实现类,也是ArrayList的兄弟类
这样,就不需要修改其它代码,这就是接口编程的优雅之处.
另外的例子就是,在类的方法中,如下声明:
private void doMyAction(List list){}
这样这个方法能处理所有实现了List接口的类,一定程度上实现了泛型函数.
3.--------------------->
如果开发的时候觉得ArrayList,HashMap的性能不能满足你的需要,可以通过实现List,Map(或者Collection)来定制你的自定义类
架构师最基本的素质,总结出两点,1是技术知识广度,2是业务行业深度。
1 架构师是技术领导。 架构师必须要有技术,而且还是领导。架构师要带领自己团队完成自己的任务,完全凭借自己的能力做事情,完全是匹夫之勇,根本不提倡。
2 架构师理解软件流程。 架构师必须了解软件流程,否则无法驱动整个团队前进,如果一个架构师不熟悉开发流程,无法协调产品线相关人员进行高效工作,也无法指导团队成员完成自己的工作。所以来说架构师一般不是空降兵(除非是全新的部门),因为空降兵一般不会熟悉新公司的开发流程,即使是同一个行业的,各个公司的差别还是很大。所以说听说某某去某某公司做首席架构师或者首席科学家,一般是高风险的事情,即使他对这个行业很了解。
3 架构师必须熟悉业务领域。 如果一个架构师不熟悉自己的行业,做的架构就是纸上谈兵,熟悉业务领域的架构师,才能很好的理解需求,做出合适的方案。互联网和网络安全是完全不同的两个方向,即使你熟悉里面的各种具体技术,但是以互联网的架构来做网络安全产品,肯定是100%的失败。
我做PKI的时候,项目最初的使用.net,使用微软的crytoAPI,开发的很顺利。后来来了一位新的架构师,觉得.net不能跨平台,决定?用java,做出产品后,发现找不到arm cpu的jdk,使用平台有限。再次决定使用openssl做,最后这个项目以失败而告终。后来我想过arm cpu都是嵌入式设备用的,根本没有任何嵌入式设备提供CA服务。
4 架构师必须要有广度的知识。 架构师考虑的问题必须全面,必须了解的要广,具体的细节可以不关注,因为细节变化很快。很多具体技术人员出身的架构师,只关注于具体的细节,某些方面做的很好,整体的性能很差。
下面这个项目的成功可以理解为一个笑话。某公司的两个部门都做c程序的,A部门做的平台使用的x86和mips,B部门使用平台是x86和 arm,后来A部门的一个模块要给B部门用,最后发现根本不能运行。最后A部门发现arm平台使用big endian模式,这样A部门的20多名员工,检查代码中所有非零整数,经过数个月苦战,才修改完毕。其实解决方法很简单,因B部门的代码也是首先在 x86做的,做的时候他们考虑了cpu的endian模式。这并不能说B部门架构师很牛,从另一个方面说他的无知。因为板子有跳线,专门切换cpu的是 big endian还是little endian,这样两个部门都可以不修改程序。
5 架构师必须是写程序的高手。
架构师一般都是?发人员出身,一般都是团队的核心。优秀的架构师应该了解团队使用各种技术,有了这些知识,才能和开发人软进行有效沟通。
有一个项目架构使用xml做配置,因为病毒库很庞大,最后导致xml 达30多兆,服务端的java程序使用dom 进行过滤的时候,30兆xml加载很慢,频繁出现out of memory。这个项目后来搁浅。根据以前的经验发现 msxml加载30多M的xml 不过几秒,而且msxml的xpath速度很快,后来专门为此写了JNI处理xml的。
6架构师是优秀的沟通人员。 架构师一定要会忽悠,至少要扯淡。架构师不但要指导本部门员工的工作,也要协调其他部门的资源,还要向用户收集需求,制定规格说明书,重要的把用户的不合理要求砍掉,合理需求遵循自己的思路
如下是在window win7旗舰版环境下使用mysql数据库的安装实录:
一)下载 activiti-5.6.zip(http://activiti.org/downloads/activiti-5.6.zip)
二)将activiti-5.6zip解压到指定目录,假设为“D:\activiti”,注意下面提到的路径均是相对于该路径而言
三)修改数据库类型为mysql
打开文件“setup\build.properties”修改db=mysql(默认为h2)。
四)配置mysql数据库
创建名称为activiti的空数据库,url要改为:“jdbc:mysql://localhost:3306/activiti?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&useServerPrepStmts=true“,否则报编码错误,连接帐号设为activiti,密码也设为activiti。(如果不喜欢默认的连接配置,可以修改文件“setup\build.mysql.properties”中相应的属性值)
五)预下载一些必需的文件到“C:\downloads”
“D:\downloads”这个路径是在build.properties中通过属性“downloads.dir”定义的,也可以手动修改指定其他位置(默认值为:downloads.dir=../../downloads)。
如果确保网络环境很顺畅,这一步也可以不做,安装脚本会自动下载,由于之前的版本我安装时经常下载中断,导致文件损坏、安装异常,所以我习惯了先预先下载,保证安装顺畅点。主要下载如下2个文件,左侧为下载地址,右侧为下载后保存的文件名:
1) http://mirrors.enquira.co.uk/apache/apache-tomcat-6.0.29.zip –> apache-tomcat-6.0.29.zip (2.71MB)
若有tomcat可以不用下载,将tomcat复制到该目录下,如果tomcat的版本不是6.2.29则需要修改setup\build.properties文件中的tomcat.version=6.0.xx
修改为当前的tomcat版本
2) http://activiti.org/downloads/activiti-modeler-5.6.war –> activiti-modeler-5.6.war (31.8MB)
此war文件就是activiti演示的web程序war包必须下载。
3)为了改变演示安装里KickStart使用的数据库,产生一个新的activiti.cfg.jar,并将它放置到apps/apache-tomcat-6.x/webapps/activiti-kickstart/WEB-INF/lib文件夹。
4)将JDBC DRIVER JAR拷贝到setup\files\dependencies\libs中,并将类似libs.webapp.administrator的所有配置文件中如有对H2JAR引用的都增加对JDBC DRIVER JAR的引用
六)修改浏览器的可执行文件路径
打开文件“setup\build.xml”修改属性windows.browser的值为浏览器的启动文件对应的位置。如我一直使用chrome,配置如下:
<property name=”windows.browser” value=”C:/Documents and Settings/dragon/Local Settings/Application Data/Google/Chrome/Application/chrome.exe” />
修改这个的目的是方便下面的”ant demo.start”结束后自动打开浏览器访问相关演示模块的首页。
注:在我的win7下系统上,使用chrome浏览器配置成了C:/Users/Administrator/AppData/Local/Google/Chrome/Application/chrome.exe但是在demo.start启动后没有打开浏览器,配置成Firefox浏览器启动路径也没有生效,原因有待查明中。
七)检查一下你的电脑是否已经使用了tomcat服务
如果使用了tomcat服务,得停掉,否测会冲突。顺便检查一下8080端口有没有被占用,否测也会冲突导致安装失败。
八)安装ant1.7和jdk1.5+环境
主要是设置环境变量ANT_HOME和JAVA_HOME,并将%ANT_HOME%\bin、%JAVA_HOME%\bin设置到path环境变量中。
九)一切就绪后就可以开始体验了
命令行切换到“setup/”,运行”ant demo.start”,等待整个安装过程自动完成吧,安装完毕后默认会自动打开浏览器。若没有启动浏览器,需自己手动打开浏览器在地址栏中输入访问地址。
可访问web应用如下:
演示用户
用户Id |
密码 |
角色 |
Kermit |
Kermit |
管理员 |
gonzo |
gonzo |
经理 |
fozzid |
fozzid |
用户 |
十)新建一个流程并演示。
访问:http://localhost:8080/activiti-modeler/
访问后会看到左侧菜单栏中是当前workspace中自带的一些示例流程。
点击上方菜单new->Business process diagram菜单项,浏览器会打开activiti建模器
如图,此图中描述了一个编写每月财务报告—>审批财务报告的简单流程。(此处用自带的FinancialReportProcess流程做演示)
图中看到的是一个 none start event (左边的圆圈),其次是两个 user tasks: “撰写财务报告“和”批准财务报告“,以 none end event (右边边框加粗型的圆圈) 结束。
以用户fozzie(他是一个会计师)身份登录到Activiti Explorer(http://localhost:8080/activiti-explorer/),选择PROCESSES页签,在列表的Action列中点击流程“Monthly financial report’”的“Start Process”链接来启动一个流程实例。如下图所示:
由于我们是以用户fozzie登录,故在我们启动了这个流程实例后,可以看到一个新的候选任务。我们这是切换到TASKS页签页去看看这个新任务(如下图),可以发现就算其他人启动的流程实例,所有属于用户组accountancy的用户都可以看到这个新的候选任务。
领取任务:
通过Activiti Explorer界面点击claim按钮将执行相同的领取任务操作。现在这个任务将显示在任务领取者的个人任务列表中,领取任务后,可以在my tasks中查看并点击页面上的complete按钮提交。
现在注销掉fozzi用户并以kermit(他是管理者)用户登录,第二个任务现在就显示在待指定任务列表中了。Kermit用户和上一步的操作一样,可以对该任务进行领取和提交,至此,该流程就走完了。如下图所示:
我在安装使用过程中遇到如下一些问题:
1)activiti-modeler-5.6.war不会自动发布,导致访问不了
解决办法:自己将activiti-modeler-5.6.war手工解压到apps\apache-tomcat-6.0.29\webapps\activiti-modeler,并删除apps\apache-tomcat-6.0.29\webapps\activiti-modeler-5.6.war
2)tomcat控制台显示的中文乱码
解决办法:修改文件apps\apache-tomcat-6.0.29\conf\logging.properties,增加如下一行的配置:
java.util.logging.ConsoleHandler.encoding = GBK
3)使用activiti-modeler保存流程图前后台均会报错
解决办法:创建目录apps\apache-tomcat-6.0.29\endorsed,并将jar文件”apps\apache-tomcat-6.0.29\webapps\activiti-modeler\WEB-INF\lib\jaxb-api.jar”复制到该目录下。
4)第一次安装后,新建流程中出现中文会出错,网上找了下没有找到解决方法,我又重新安装了一遍,没有该问题了,目前尚不清楚是由何引起的。
安装eclipse designer插件:
需要eclipse版本为:Eclipse IDE for java EE developers(Helios Service)
打开Help->Install New software。在如下面板中,点击Add按钮,然后填写下列字段:
Name: Activiti BPMN 2.0 designer
Location: http://activiti.org/designer/update/
添加完成后,eclipse会搜索到BPMN designer插件,选中后,按提示操作完成即可。
由于是在线安装,插件可能偏大,我在安装的过程中等待了很长时间,大约半个小时,所以如果安装此插件请耐心等待。
安装完成后,重启eclipse, 导入activiti自带的examples,在src/main/process/路径下,随便找个后缀名为bpmn20.xml的文件打开,就可以看到该插件的效果了,如图:
终于安装完成了,总结一下,在安装过程中,遇到了或多或少的问题,发现activiti的用户数不是那么多,因为好多问题网络上都搜索不到解决办法,都得自己再钻研一下,很是痛苦,不知啊现在国内有那个项目用activiti5的成功经验是否可以拿出来分享一下。
在工作流管理系统中,通常是先给业务流程建模,利用流程设计器,将业务的办理过程用流程支持的节点方式表示出来。
业务建模之后,再确定每个节点上办理的业务,办理业务的过程,通常是以填写完业务表单的方式来完成的。所以需要分析每个节点上填写的表单内容,根据 表单内容建立业务表,表字段等。再将字段绑定到表单中录入控件上,将表单录入的数据保存到数据库中,这样业务表单模块就完成了。
业务表单完成之后,再挂接到流程节点上。
另外可能需要再次完善一下流程节点的一些属性,如增加每个节点的指定办理人。
设置取业务表中的一些关键值用于流程中,如取报销单中的报销金额,请假单的请假天数,用于流程上下文中做条件使用。
最后,在业务表中,需要增加一个流程实例id字段,用于和业务流程关联。在启动业务流程的时候,需要将获得的流程实例id写入这个字段中,使得业务记录能和流程实例关联上。
通过上面这些步骤,就建立好了业务流程了,可以启动流程实例,办理业务了。业务的流转就按照流程建模中定义好的顺序办理,不需要再在业务表 中增加状态字段来控制业务的流转了。业务的办理过程变得有迹可循了,每个流程实例均可以列出运行的轨迹图,或者列表出运行的轨迹。每个节点上办理的业务也 能通过查询业务表单再次展现。
上面我们说过,业务表是存储办理业务数据的数据库表,一般来说,一个业务表只用于一种业务流程中,存储同一类型的业务数据。当流程运行结束的时候, 这些业务数据就被封存,不能在流程的节点中再次被编辑和修改。(除非直接开库修改数据,或者另外做一些模块,直接修改业务数据)
但是,这些封存的业务数据,有可能会被再次启用投入到另外一个业务流程中去使用,这种需求可能是需求肯定是有应用场景的,不管是分段的处理过程还是后期又做的一些业务补充等,都有可能发生。
如果一个业务表,需要再次用于另外一个业务流程当中,则我们只需要给业务表,再增加一个流程实例id字段,就可以了,再次新启动的业务流程获得的流 程实例id就写入这个新的流程实例id字段。和以前的那个流程实例id不相关了。只是如果是编辑同一条业务记录的话,就可能把上次的数据给修改了。这样理 论上是可以支持n个业务流程。
总结一下,一个业务表用于一个业务流程中,用一个流程实例id字段和流程关联,用于另外一个业务流程中,则再建一个流程实例id字段和流程实例关联。
一个业务流程可能会涉及到多张业务表,一张业务表也可能涉及到多个业务流程。