1.链表
链表的特性:
1.在Java中所有的链表其实都是双向链表,也就是每一个节点都保存了前驱节点和后继节点的索引。
2.高效的插入和删除
3.低效的随机访问
4.在Java中LinkedList是一个有序的集合
ListIterator:
集合类库中专门提供的一个List的迭代器叫做ListIterator,这个迭代器中的add方法没有返回值,这是因为List下的两个实现类:ArrayList 和 LinkedList 都可以动态增加长度,不存在添加不进去的情况;
ListIterator中对于LinkedList有一个previous方法可以反向遍历链表。
链表的add、set、remove方法
在Java中的链表,add方法需要依赖于迭代器的位置,即如果想要在指定的位置插入元素,就必须先遍历到这个位置,然后才可以使用add进行插入操作。
remove方法则更依赖于迭代器的状态,比如:如果你之前使用的是next,那么他会删除左边的元素,也就是这次遍历到的元素(为什么说是左边的元素,因为迭代器真正指向的其实应该是两个元素之间的位置,每越过一个元素,就把这个元素返回给调用者);而如果你之前使用的是previous,那么他会删除右边的元素,并且无论是那种情况,都不可以连续使用remove方法;
set方法就常规多了,因为set方法其实就是用作修改的,但也得依赖于迭代器的位置,调用set方法,他会用一个新元素取代调用next或者previous方法返回的那个元素;
链表中的get方法
在Java中最好不要使用get方法去获取链表某个位置的值,由于链表的缘故需要遍历过去才能获得,而这样的获取方式无疑是非常低效的(尽管Java中的get方法对于索引大于size/2的时候会从链表的尾部搜索元素,依然无法改变效率低下的问题);
使用链表的理由:
作为一个存储结构来看,链表在使用的时候其实并不友好,因为大多数时候我们都需要频繁的随机访问来支持我们查阅数据;哪怕存储结构里面只有几个元素,数组带来便利性和性能会更好,所以只有为了减少频繁的中间插入和删除元素的低效工作才应该使用链表;
2.数组列表
概述:
在Java中ArrayList被称为数组列表,他拥有和数组一样的功能,可以动态扩容和缩减,并且还封装了一些操作,从某种程度上来说他比数组要好用;
特点:
1.高效的访问
2.动态扩容和缩减
ArrayList和Vector
ArrayList和Vector两者都可以当作数组来用,那么他们有什么区别呢?原因就在于多线程的访问,由于ArrayList不支持同步,也就是方法不同步,在多线程情况下会经常导致数据出错;而Vertor类的所有方法都是同步的,多线程下可以保证数据安全;
所以在多线程情况下Vector会更高效,但是单线程情况下,频繁的同步会损耗大量时间,所以单线程下使用ArrayList会更好;
3.散列集
概述:
在Java中HashSet被称为散列集,Hash也就是散列算法,既然被称作是集而不是集合,那么存储次序其实就是通过hash散列算法来存储的一种数据结构;
特点:
1.无序
2.可以根据散列码快速查找
3.存入元素的散列码必须可以平均散列
为什么要使用散列集?
在使用数组和链表的时候,如果你忘记了要查找元素的次序,那么就只能遍历查找,如果数据量很大的话,那么查找起来将会很慢;但是如果你不在乎数据的次序,可以使用hashSet来提高查找效率;
散列集的实现原理:
Java中的散列表使用链表数组实现,每个列表成为做桶,然后根据每个元素的散列码来决定存储在那个桶中(也就是根据散列码对桶的总数取余),如果发现桶被占满了(也被称为冲突),那么就需要使用新元素和桶中的元素比较,查询这个元素是否已经存在;在Java SE8中,桶满之后会从链表变成平衡二叉树(所以储存元素的散列码和散列函数一定要合理并且恰当,不然就会造成很多冲突降低性能)。
如果大致知道最后可能会有多少个元素插入进去,那么就可以设置桶的个数了;一般情况下,桶的个数应该为预计元素个数的75%到150%会比较合理;Java标准库使用桶的个数是2的幂,默认值是16,为表大小提供的任何值都会被自动转换为2的下一个幂;
如果要存储的数据量被低估了很多,表就需要再散列,一个散列表再散列,会得到一个桶数更多的表,将所有元素添加到新表中,旧表就会被丢弃掉,装填因子决定了什么时候对表扩充,而在大多数情况下0.75比较合理;