集合的储存方式是objict对象存入,取出。存入的的时候自动转化,但是取出的时候一定要强制转换回原来存入的类型。如果你不想强制转化输出,你就需要泛型了—<>来限制你的输入了,算是从源头把控。
这个词也不知道是谁翻译的,太渣了,所谓的泛型还不如翻译成限型—限制类型。
限制类型更加明确而且好理解,也就是说你每次存储创建的数据在开始的时候就进行数据类型限制。输出的时候必然是这种类型。其实也就是参数列表的变形,只不过跟参数列表重复,容易引起混淆,为了区别就用<>代替。
就好像公路,车辆进入公路的时候就要不同类型的车辆进入不同的车道,出来的时候,那一条车道出来的车辆也必然是那一个类型的车辆。就好像双重保险一样。进去什么出来什么。
不过说实话,这样理解好是好了,可是如果没有存入的时候系统自动转化为object类型的话,那么岂不是不需要泛型了。也无需改为限型了。
其实说白了就是一个对集合的标识,集合本来就只能存储一类元素,那么是哪一类呢,这个泛型<>的作用就是表示出是哪一类的元素集合。
泛型就说到这里。
接下来才是重点集合框架。
先说一下数据的三种存储方式:
顺序存储,因为有序,所以即使元素值相同,但因为位置不同,所以能根据位置的不同而得到区分,所以元素可以重复。又因为索引位置固定,所以遍历元素,访问元素效率高。一般的都是添加元素的顺序。
链表储存,因为无序,所以元素值不能相同,不然下一次相同的几个元素可能互换位置,进而导致不能分清那个是哪个。所以说不同的存储方式的根本区别其实在于有序无序。
秩序决定了值能否相同。而且还还决定的方法的不同。
所以我觉得在集合里面秩序决定一切。
比如因为无序,所以没有固定位置,所以你无法区别两个相同元素的区别,所以元素不能相同,所以没有固定下标,所以无法通过下标获得删除某个元素,比如get(index) add(index)remove(index)等就无法使用。
这就是秩序决定值,秩序决定方法。
虽然因为无序导致了种种不便,但同样也是因为链表储存的位置不定,所以删除某个元素,就无所谓移动不移动其他的元素了,也就无需把后面的元素全部往前前移了。这就决定了链表储存的优点。
第三种储存方式。
哈希表储存。我理解的不透彻,所以不多说。大家也可以不用看这一段。免得误导大家。还是决定不写这段了。
Length与size的区别:
Length是数组长度,比如设定一个10长度的数组,不管向里面存了两个还是五个,length都是10.
而size是集合的元素个数,比如我们创建一个arrayslist实现类,其默认长度为10 ,其他的实现类默认的可能是16。但是如果你实际存了3个,那么size就是3。存了五个,长度就是5.
而且不止这一点区别,更重要的是,集合的长度是可以自由变化的,所以我们可以说arrayList是一个动态的数组。这样就可以将数组的概念转嫁过来去理解集合了。
删除的方法;
删除元素的时候一定要注意下标。没删除一个元素,实际长度必然改变,这个时候的长度不再是原来的长度,而是操作后的长度,所以下标必然改变,再次删除的时候下标就是删除后的新下标。而且如果删除的剩下一个元素了,再次删除的下标就不能是1了,因为下标越界了。
ArrayList list=new ArrayList();
LinkedList list=new LinkedList();
List list=new List();
大家看一下这三者的区别。
一般的情况下很多人喜欢,而且很多代码里面多用第三种。因为List是ArrayList和LinkedList的父类接口。在多态里面我们有学到向上转型以及向下转型。所以一般来说第三句比较实用,但是LinkedList里面的有些方法是他自己独有的,比如XX—first,XX_last这些对头部尾部的操作。如果这个时候你用第三句的话就无法使用这些子类独有的方法了,按照多态的办法只能向下强制转型了。这样就很麻烦。
所以开始的时候我们还是最好不用第三句的好。可是如果熟练之后我们会突然发现比如XX—first,XX_last基本上没啥大的用处,一般的我们用默认无参数列表的方法或者用指定下标的方法都能达到一样的效果。比如add(int index, E element) ,remove(int index) 。想要对头部,尾部操作,只需要把index设定为0或者size()-1就行。
特别是我都看不明白LinkedList里面的remove()(获取并移除此列表的头(不也是第一个元素吗?))。和removefirst()(移除并返回此列表的第一个元素)。、
这两个方法之间有什么区别,所以我们就会发现LinkedList里面的方法很多独有方法都是多余的,所以这个时候我们就会发现还是第三句比较好用,这就是为什么很多代码里面第三句用的比较多。
这样大家可以看看ArrayList和LinkedList的共有方法也就是List的方法是否够用,如果够用的话,就没必要区分这两者了。下图是List接口的方法的一部分。大家可以查一下api文档。看是否够用。
不过不管怎样,他们既然存在,就肯定有其不同之处,不然就没有存在的意义。可以通过他们的各自的api文档的方法的不同看到。
但是这些不是根本的不同,看下图。这个可能才是他们不同的根本所在。
再来说一下Set接口以及他的重要实现类hashSet。
前面说了,有序与否决定了元素值与对元素操作的方法,因为哈希表的无序性,所以就没有get(index)方法了。
至于其他的也是基本与List基本相同。
要注意一点的是List既是类,也是集合。不能这样说,应该算是重名了。有是类的List也有是接口的List。还是瞧万能的api吧。
最后说一下hashmap,他与前面的根本区别就是有一个映射的键。要注意map适合list同级别的。hashMap适合arrayList同一级别的。
其实前面三者也有这个关系,不过是被系统自动接管赋值了,也就是我们经常见到的下标0123……。而hashMap的键可以由我们程序员进行操作。而且更进一步,键不仅可以是数字123……,而且可以使字符abc,与字符串,比如中国这个值得键是china或者CN。因为他是objicet类型。
在这张图表中,红线的是最要的。但是说实话,Set接口很少用到。主要是用List接口以及Map接口。
而这两者就很好找到他们的区别了。那就是一个是单身,一个是成对。所以你选择的时候很容易。但是下面的接口与实现类的选择就困难很多,而且这个才是今后开发经常用到且要做出艰难选择的所在。下图:
Vector与ArrayList的方法一模一样,只是换了名字而已。同样的,Hashable也与HashMap的方法功能一模一样。只是换了个名字而已。
所不同的是,他们的安全性与效率不同,也就是如图所示。
而根本区别就在于线程同步与否。
举个简单的例子。wan网络游戏——cs。双方对战,特警开枪射杀了对方,可是这边的恐怖分子却还活着能进行反击。这就是线程异步。也就是所谓的时间不同步。而很多网络游戏,对即时要求非常高的软件来说线程同步是必须的,这个时候就要舍弃ArrayList和HashMap。
不过这却不能说明线程异步就没有优点,优点是效率高。而同步的代价是效率低。
其实在我看来有一点不是很明白。有时候线程异步并不代表安全低,同步就代表了安全高。比如说银行卡打款。我就觉得最好还是有个时间差的好,不然短信提示取款的时候,款项已经打到了对方的账户里面,可是这个不是我操作的啊。是非法操作,如果这个时候有个时间延迟,也就是线程异步,就能给我充足的时间阻止这份交易。增加安全性。就好比网上交易的第三方支付平台的时间延迟机制,就是为了增加双方的安全性。
所以说这个才是你选择用哪个的艰难所在。当然也有可能说的根本就不是一回事。实现原理都不在一个频道上。
前面说了因为无序,所以不会有get。但是hashMap有键确定顺序,所以算是无序中有序,所以他有自己独有的get方法,也就是通过key获得的顺序。也就是get(key)方法。这里的key不同于前面几个实现类必须输整数,这里可以是任意值。另外一个独有的方法就是添加方法是put()。而不是add()。
最后要注意的一点是keySet()和values()所得到的结果不是一个具体的元素,而是元素的集合。基本上相当于已经使用了遍历,所以不用再遍历了。
而集合有一个特殊的就是迭代器,专门为集合而诞生。其实就是另一种遍历的方法。但其实它是一个接口,因为是系统默认的,所以可以直接用集合调出来,然后用变量接受。关键是这个接口里面的而两个方法,一个是hasNext(),意思是是否还有元素,有的话,用next()继续遍历。没有的话就就终止,因为是循环,所以一般用while循环。
为何会有这种特有的遍历方法呢,其实说白了就是因为集合里面有很多元素的储存方式是无序的,没有办法通过索引下标来便利,只能一个个往外拿,直到拿完为止。
就好像从中奖箱里面那彩票,你不知道拿到的第一个,还是哪一个能中奖,直到全部拿完为止,所有的奖项才能全部变现。
不过后来for循环的增强版foreach循环其实已经能做到这一点了。所以有时这两者可以通用的。
多说最后一点,方法太多,关键是增删改查的功用,这一点和数组的增删改查,实用类的正删改查,文件的增删改查等合到一起共同构成了增删改查功能。这些方法零碎且多。只能多比较多用才能记住。不知道那位同学有没有这方面的心得,贡献出来让我能更好的记住他们。