数学知识巧学JCF(Java Collections framework)
不知你是否还记得高中我们学过的集合,映射,函数,数学确实很牛逼,拿它来研究java集合类,轻而易举的就把知识理解了。本篇文章适合初学java集合类的小白,也适合补充知识漏缺的学习者,同时也是面试者可以参考的一份资料。
数学知识
回顾一下之前所学的知识,结合我多年的高中数学教学经验,相信你会对某些知识有一些新的感悟。
集合:一般地,我们把研究对象统称为元素(element),把一些元素组成的总体叫做集合(set)。
对于一个给定的集合,其具有的特征:
确定性:集合中的元素都是确定的。
互异性:集合中的元素都是不同的。
无序性:集合中的元素的顺序是无序的。
映射:一般地,我们有:
设A,B是两个非空的集合,如果按照某一个确定的对应关系f.是对应集合A中的任意一个元素x,在集合B中都有唯一确定的元素y与之对应,那么就称对应f:A—>B为集合A到集合B的一个映射(mapping)。
其实简单的来讲,何谓映射,就是函数上将的关系对应,例如:
函数 f(x)=x^2 那么每一个x都有唯一的y与之对应,这就是映射关系的一个模型。
而方程 x^2+y^2=1,这个很明显是圆心为(0,0)的半径为1的圆,任取一个x可能会有一个或者两个y与之对应,这就不能称为映射,进而不能称为函数。(1,0)或者(-1,0)这时候的x只有唯一的确定的y和它对应。
集合类的学习
集合类产生的原因:在一般的情况下,我们在写程序时并不知道将需要多少个对象,或者是否需要更加复杂的方式存储对象,显然使用具有固定长度的数组已经不能解决这个问题了。所以java 实用类库提供了一套相当完整的容器类来解决这个问题。
基本概念
java容器类类库的用途是“保存对象”,可将其划分为两个不同的概念:
1)collection.独立元素的序列。主要包含List(序列),Set(集合),Queue(队列)
List:按照插入的顺序保存元素;
Set:不能有重复的元素;
Queue:按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同);
2)Map:一组成对的“键值对”对象,允许我们使用键来查找值。
针对经常使用的类库,我们只列出List,Set,Map之间的继承关系:
List
List接口在Collection的基础上添加了大量的方法,使得可以在List的中间插入和删除元素。
继承自List的子类有ArrayList, LinkedList ,Vector三类。
list的特征:
有序的Collection
允许重复的元素,允许空的元素。
插入类似的数据:{1,2,4,{5,2},1,3};
ArrayList(类似于顺序表)
其主要用于查找,对于删除和插入,耗时巨大。ArrayList是以数组实现的列表,不支持同步。
优点:利用索引位置可以快速的定位访问
适合变动不大,主要用于查询的数据
和java的数组相比较,其容量是可以动态调整的。
缺点:不适合指定位置的插入,删除操作。
--ArrayList在元素填满容器是会自动扩充容器大小的50%
ArrayListTest 代码分析:
add()方法,添加元素,默认是在后面添加。
add(index,value),在指定索引处添加元素。会进行元素的移动。源码如下:
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
remove(index)删除指定位置上的元素。源码如下:
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
从源码可以分析出,在ArrayList进行插入和删除的时候,会进行类似顺序表的操作,移动元素,空出位置,然后插入元素。删除:依次移动后面的元素覆盖指定位置的元素。这就会大大减慢ArrayList插入和删除的效率。
举一个应用的例子,更好的理解ArrayList:
public class ArrayListTest {
public static void main(String[] args) {
//泛型的用法,只允许Integer类型的元素插入。
ArrayList<Integer> arrayList =new ArrayList<Integer>();
//增加元素
arrayList.add(2);
arrayList.add(3);
arrayList.add(4);
arrayList.add(5);
arrayList.add(4);
arrayList.add(null);//ArrayList允许空值插入,
arrayList.add(new Integer(3));
System.out.println(arrayList);// [2, 3, 4, 5, 4, null, 3]
//查看元素的个数
System.out.println(arrayList.size());//
arrayList.remove(0);
System.out.println(arrayList);// [3, 4, 5, 4, null, 3]
arrayList.add(1, new Integer(9));
System.out.println(arrayList);// [3, 9, 4, 5, 4, null, 3]
System.out.println("-----------遍历方法-------");
ArrayList<Integer> as=new ArrayList<Integer>(100000);
for(int i=0;i<100000;i++){
as.add(i);
}
traverseByIterator(as);
traverseByFor(as);
traverseByForEach(as);
}
public static void traverseByIterator(ArrayList<Integer>al){
System.out.println("---------迭代器遍历-------------");
long startTime=System.nanoTime();//开始时间
Iterator it=al.iterator();
while(it.hasNext()){//
it.next();
}
long endTime=System.nanoTime();//结束时间
System.out.println((endTime-startTime)+"纳秒");
}
public static void traverseByFor(ArrayList<Integer>al){
System.out.println("---------索引遍历-------------");
long startTime=System.nanoTime();//开始时间
for(int i=0;i<al.size();i++) al.get(i);
long endTime=System.nanoTime();//结束时间
System.out.println((endTime-startTime)+"纳秒");
}
public static void traverseByForEach(ArrayList<Integer>al){
System.out.println("---------Foreach遍历-------------");
long startTime=System.nanoTime();//开始时间
for(Integer temp:al);
long endTime=System.nanoTime();//结束时间
System.out.println((endTime-startTime)+"纳秒");
}
}
-----------遍历方法-------
---------迭代器遍历-------------
10407039纳秒
---------索引遍历-------------
7094470纳秒
---------Foreach遍历-------------
9063813纳秒
可以看到利用索引遍历,相对来说是快一些。
迭代器 Iterator
hasNext() 判断是否有下一个元素
next() 获取下一个元素
remove () 删除某个元素
LinkedList:(主要用于增加和修改!)
--以双向链表实现的列表,不支持同步。
--可以被当做堆栈、队列和双端队列进行操作
--顺序访问高效,随机访问较差,中间插入和删除高效
--适合经常变化的数据
addFirst()在头部添加元素
add(3,10);将10插入到第四个位置上
remove(3)删除第四个位置的元素
代码详解:
public class LinkedListTest {
public static void main(String[] args) {
LinkedList<Integer> linkedList=new LinkedList<Integer>();
linkedList.add(2);
linkedList.add(3);
linkedList.add(9);
linkedList.add(6);
linkedList.add(7);
System.out.println(linkedList);
//linkedList.addFirst(1);
//linkedList.addLast(10);
//System.out.println(linkedList);
linkedList.add(3, 4);
System.out.println(linkedList);
System.out.println(linkedList.get(4));
LinkedList<Integer> as=new LinkedList<Integer>();
for(int i=0;i<100000;i++){
as.add(i);
}
traverseByIterator(as);
traverseByFor(as);
traverseByForEach(as);
}
public static void traverseByIterator(LinkedList<Integer>al){
System.out.println("---------迭代器遍历-------------");
long startTime=System.nanoTime();//开始时间
Iterator it=al.iterator();
while(it.hasNext()){
it.next();
}
long endTime=System.nanoTime();//结束时间
System.out.println((endTime-startTime)+"纳秒");
}
public static void traverseByFor(LinkedList<Integer>al){
System.out.println("---------索引遍历-------------");
long startTime=System.nanoTime();//开始时间
for(int i=0;i<al.size();i++) al.get(i);
long endTime=System.nanoTime();//结束时间
System.out.println((endTime-startTime)+"纳秒");
}
public static void traverseByForEach(LinkedList<Integer>al){
System.out.println("---------Foreach遍历-------------");
long startTime=System.nanoTime();//开始时间
for(Integer temp:al);
long endTime=System.nanoTime();//结束时间
System.out.println((endTime-startTime)+"纳秒");
}
}
---------迭代器遍历-------------
6562423纳秒
---------索引遍历-------------
4565606240纳秒
---------Foreach遍历-------------
4594622纳秒
可以看出使用索引遍历,对于linkedList真的很费时间!
add(index,value)源码分析:我们可以看到,这就是双引用(双指针)的赋值操作。
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
remove(index)源码分析:同样,这也是对引用的更改操作,方面多了!
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev; if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
} if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
} x.item = null;
size--;
modCount++;
return element;
}
get(index)源码分析:利用指针挨个往后查找,直到找到位置为index的元素。当然了,找的时候也是要注意方法的,比如说利用二分查找。
Node<E> node(int index) {
// assert isElementIndex(index); if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
Vector
-和ArrayList类似,可变数组实现的列表
-Vector同步,适合在多线程下使用
-原先不属于JCF框架,属于java最早的数据结构,性能较差
-从JDK1.2开始,Vector被重写,并纳入JCF中
-官方文档建议在非同步的情况下,优先采用ArrayList
其实vector类似于ArrayList,所以在一般情况下,我们能优先使用ArrayList,在同步的情况下,是可以考虑使用Vector
代码例子:
public class VectorTest {
public static void main(String[] args) {
Vector<Integer> vs=new Vector<Integer>();
vs.add(1);
vs.add(4);
vs.add(3);
vs.add(5);
vs.add(2);
vs.add(6);
vs.add(9);
System.out.println(vs);
System.out.println(vs.get(0));
vs.remove(5);
System.out.println(vs);
/*Integer []a=new Integer[vs.size()];
vs.toArray(a);
for(Integer m:a){
System.out.print(m+" ");
}*/
Vector <Integer> as=new Vector <Integer>(100000);
for(int i=0;i<1000000;i++){
as.add(i);
}
traverseByIterator(as);
traverseByFor(as);
traverseByForEach(as);
traverseEm(as);
}
public static void traverseByIterator(Vector<Integer>al){
System.out.println("---------迭代器遍历-------------");
long startTime=System.nanoTime();//开始时间
Iterator it=al.iterator();
while(it.hasNext()){
it.next();
}
long endTime=System.nanoTime();//结束时间
System.out.println((endTime-startTime)+"纳秒");
}
public static void traverseByFor(Vector<Integer>al){
System.out.println("---------索引遍历-------------");
long startTime=System.nanoTime();//开始时间
for(int i=0;i<al.size();i++) al.get(i);
long endTime=System.nanoTime();//结束时间
System.out.println((endTime-startTime)+"纳秒");
}
public static void traverseByForEach(Vector<Integer>al){
System.out.println("---------Foreach遍历-------------");
long startTime=System.nanoTime();//开始时间
for(Integer temp:al){
temp.intValue();
}
long endTime=System.nanoTime();//结束时间
System.out.println((endTime-startTime)+"纳秒");
}
public static void traverseEm(Vector<Integer>al){
System.out.println("---------Enumeration遍历-------------");
long startTime=System.nanoTime();//开始时间
for(Enumeration <Integer> ei=al.elements();ei.hasMoreElements();){
ei.nextElement();
}
long endTime=System.nanoTime();//结束时间
System.out.println((endTime-startTime)+"纳秒");
}
}
---------迭代器遍历-------------
28927404纳秒
---------索引遍历-------------
32122768纳秒
---------Foreach遍历-------------
25191768纳秒
---------Enumeration遍历-------------
26901515纳秒
可以看到Foreach遍历要快于其他的遍历方法。
add(index,value)源码剖析:这个和ArrayList类似,需要进行元素的复制,所以很慢
public synchronized void insertElementAt(E obj, int index) {
modCount++;
if (index > elementCount) {
throw new ArrayIndexOutOfBoundsException(index
+ " > " + elementCount);
}
ensureCapacityHelper(elementCount + 1);
System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
elementData[index] = obj;
elementCount++;
}
get(index)源码剖析:可以看到,直接根据元素的下表返回数组元素。非常快!
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index); return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}
其实List这部分内容用的数学知识不是很多,但是set和Map确实是类似于数学模型的概念。期待后续Set,Map的学习。
个人微信公众号
数学知识巧学JCF(Java Collections framework)的更多相关文章
- Java入门系列(七)Java 集合框架(JCF, Java Collections Framework)
Java 集合概述 List.Set.Map可以看做集合的三大类 java集合就像一个容器,可以将多个对象的引用丢进该容器中. Collection和Map是java集合的根接口. List List ...
- (一)一起学 Java Collections Framework 源码之 概述
. . . . . 目录 (一)一起学 Java Collections Framework 源码之 概述 JDK 中很多类 LZ 已经使用了无数次,但认认真真从源码级研究过其原理的还只占少数,虽然从 ...
- (二)一起学 Java Collections Framework 源码之 AbstractCollection
. . . . . 目录 (一)一起学 Java Collections Framework 源码之 概述(未完成) (二)一起学 Java Collections Framework 源码之 Abs ...
- Java Collections Framework概览
本文github地址 概览 容器,就是可以容纳其他Java对象的对象.Java Collections Framework(JCF)为Java开发者提供了通用的容器,其始于JDK 1.2,优点是: 降 ...
- Java Collections Framework Java集合框架概览
Java SE documents -- The Collections Framework http://docs.oracle.com/javase/8/docs/technotes/guides ...
- Java Collections Framework 汇总
1. Java Collections Framework Java集合框架概览 2. Java Collections Framework 之 RandomAccess接口 3. 关于ArrayLi ...
- Java Collections Framework知识结构目录
The core collection interfaces are the foundation of the Java Collections Framework. The Java Collec ...
- 【DataStructure】The description of Java Collections Framework
The Java Connections FrameWork is a group of class or method and interfacs in the java.util package. ...
- Java Collections Framework
集合OR 容器 通常我们会用数组去保存一些基本数据类型,数组是编译器支持的类型,但是数组的一个明显缺点就是具有固定尺寸,而在一般情况下,只有在程序运行的时候,我们才能知道要保存的具体数目. Java类 ...
随机推荐
- CPP-基础:C++中为什么需要一个头文件,一个cpp文件
把文件分成头文件和源文件完全是为了方便扩展和组织程序 这么说吧 我们可能会自定义很多函数 而这些函数分别会在不同的地方被调用 甚至有些时候我们需要把一堆函数打包起来一起调用 比如#include &q ...
- js 两个数组对象根据账号比较去重,解决直接splice后数组索引改变
目的获取Arr2中不包含在arr1中的对象 根据Account进行比较,如果相等则删除tempArr数组对象. 结果返回张三 var arr1=[{"account":" ...
- WinForm各种关闭
Appication.Exit(); Environment.Exit(); System.Threading.Thread.CurrentThread.Abort(); Process.GetCur ...
- CF547D Mike and Fish 建图
题意: 有点长→CF547DMike and Fish. 分析: 其实也没什么好分析的,我这也是看的题解. (不过,那篇题解好像文字的代码不太对劲) 这里直接说做法,正确性自证: 对输入的,将横.纵坐 ...
- (51)zabbix命令:zabbix_get获取item数据
zabbix_get是什么?有什么作用? 总有人在群里提问,为什么zabbix获取不到数据,为什么zabbix提示Not Support,怎么办?别老问,用zabbix_get试着获取数据即可.在za ...
- mysql:having 用法
顺序:where -> group by -> min -> order by -> limit 在select语句中使用having 子句来指定一组行或聚合的过滤条件 hav ...
- 离线web-ApplicationCache
https://www.html5rocks.com/en/tutorials/appcache/beginner/ http://diveintohtml5.info/offline.html#fa ...
- 如何用纯 CSS 绘制一个充满动感的 Vue logo
效果预览 在线演示 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/zaqKPx 可交互视频教 ...
- Python GUI界面开发环境配置:Pycharm+PyQt5
通过DoS命令行执行如下命令,可能需要管理员权限. 检查Python版本:python 更新pip版本:python -m pip install --upgrade pip 安装PyQt5: pip ...
- Educational Codeforces Round 32:E. Maximum Subsequence(Meet-in-the-middle)
题目链接:E. Maximum Subsequence 用了一个Meet-in-the-middle的技巧,还是第一次用到这个技巧,其实这个技巧和二分很像,主要是在dfs中,如果数量减小一半可以节约很 ...