不知你是否还记得高中我们学过的集合,映射,函数,数学确实很牛逼,拿它来研究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)的更多相关文章

  1. Java入门系列(七)Java 集合框架(JCF, Java Collections Framework)

    Java 集合概述 List.Set.Map可以看做集合的三大类 java集合就像一个容器,可以将多个对象的引用丢进该容器中. Collection和Map是java集合的根接口. List List ...

  2. (一)一起学 Java Collections Framework 源码之 概述

    . . . . . 目录 (一)一起学 Java Collections Framework 源码之 概述 JDK 中很多类 LZ 已经使用了无数次,但认认真真从源码级研究过其原理的还只占少数,虽然从 ...

  3. (二)一起学 Java Collections Framework 源码之 AbstractCollection

    . . . . . 目录 (一)一起学 Java Collections Framework 源码之 概述(未完成) (二)一起学 Java Collections Framework 源码之 Abs ...

  4. Java Collections Framework概览

    本文github地址 概览 容器,就是可以容纳其他Java对象的对象.Java Collections Framework(JCF)为Java开发者提供了通用的容器,其始于JDK 1.2,优点是: 降 ...

  5. Java Collections Framework Java集合框架概览

    Java SE documents -- The Collections Framework http://docs.oracle.com/javase/8/docs/technotes/guides ...

  6. Java Collections Framework 汇总

    1. Java Collections Framework Java集合框架概览 2. Java Collections Framework 之 RandomAccess接口 3. 关于ArrayLi ...

  7. Java Collections Framework知识结构目录

    The core collection interfaces are the foundation of the Java Collections Framework. The Java Collec ...

  8. 【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. ...

  9. Java Collections Framework

    集合OR 容器 通常我们会用数组去保存一些基本数据类型,数组是编译器支持的类型,但是数组的一个明显缺点就是具有固定尺寸,而在一般情况下,只有在程序运行的时候,我们才能知道要保存的具体数目. Java类 ...

随机推荐

  1. CPP-基础:C++中为什么需要一个头文件,一个cpp文件

    把文件分成头文件和源文件完全是为了方便扩展和组织程序 这么说吧 我们可能会自定义很多函数 而这些函数分别会在不同的地方被调用 甚至有些时候我们需要把一堆函数打包起来一起调用 比如#include &q ...

  2. js 两个数组对象根据账号比较去重,解决直接splice后数组索引改变

    目的获取Arr2中不包含在arr1中的对象 根据Account进行比较,如果相等则删除tempArr数组对象. 结果返回张三 var arr1=[{"account":" ...

  3. WinForm各种关闭

    Appication.Exit(); Environment.Exit(); System.Threading.Thread.CurrentThread.Abort(); Process.GetCur ...

  4. CF547D Mike and Fish 建图

    题意: 有点长→CF547DMike and Fish. 分析: 其实也没什么好分析的,我这也是看的题解. (不过,那篇题解好像文字的代码不太对劲) 这里直接说做法,正确性自证: 对输入的,将横.纵坐 ...

  5. (51)zabbix命令:zabbix_get获取item数据

    zabbix_get是什么?有什么作用? 总有人在群里提问,为什么zabbix获取不到数据,为什么zabbix提示Not Support,怎么办?别老问,用zabbix_get试着获取数据即可.在za ...

  6. mysql:having 用法

    顺序:where -> group by -> min -> order by -> limit 在select语句中使用having 子句来指定一组行或聚合的过滤条件 hav ...

  7. 离线web-ApplicationCache

    https://www.html5rocks.com/en/tutorials/appcache/beginner/ http://diveintohtml5.info/offline.html#fa ...

  8. 如何用纯 CSS 绘制一个充满动感的 Vue logo

    效果预览 在线演示 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/zaqKPx 可交互视频教 ...

  9. Python GUI界面开发环境配置:Pycharm+PyQt5

    通过DoS命令行执行如下命令,可能需要管理员权限. 检查Python版本:python 更新pip版本:python -m pip install --upgrade pip 安装PyQt5: pip ...

  10. Educational Codeforces Round 32:E. Maximum Subsequence(Meet-in-the-middle)

    题目链接:E. Maximum Subsequence 用了一个Meet-in-the-middle的技巧,还是第一次用到这个技巧,其实这个技巧和二分很像,主要是在dfs中,如果数量减小一半可以节约很 ...