一、图 (Graph)
图的基本概念:多对多关系。
图(graph)是一种网状数据结构,图是由非空顶点集合和一个描述顶点间关系的集合组成。图(grapn)是一种网状数据结构,图是田非空顶点集台和一个抽还顶点间天系的集合组成。
在无向图和有向图中V中的元素都称为顶点,而顶点之间的关系却有不同的称谓,即弧或边,为避免麻烦,在不影响理解的前提下,我们统一的将它们称为边(edge)。
无向图实际上也是有向图,是双向图
●加权图:
在实际应用中,图不但需要表示元素之间是否存在某种关系,而且图的边往往与具有一定实际意义的数有关,即每条边都有与它相关的实数,称为权。
这些权值可以表示从一个顶点到另一个顶点的距离或消耗等信息,在本章中假设边的权均为正数。这种边上具有权值的图称为带权图(weighted graph)
●图的存储结构:可以采用顺序存储结构和链式存储结构,更多采用链式存储结构。
●邻接表:链表 链式存储结构
二、集合
集合又称容器。Java中对数据结构(数据存储方式)的具体实现。
可以利用集合存放数据,也可以对集合中的数据进行新增、删除、修改、查看等操作。
集合中数据都是在内存中,当程序关闭或重启后集合中数据会丢失。集合也是一种临时存储数据的容器。
●Collection(集合)接口:
•Queue 接口:队列,先进先出
•List 接口:存储元素有序(插入顺序),可重复
①ArrayList:
底层实现为数组.数组具备的特性ArrayLsit也有,根据存储元素的个数完成数组的扩容。
线程不安全,效率高。
优点:遍历效率高,有随机访问能力,地址连续。
缺点:添加,删除 效率低,将删除元素的后边元素依次向前移动。
②LinkedList
底层实现为链表.链表中默认没有结点,添加数组时,创建结点,将数据存储在结点中。
线程不安全,效率高。
优点:添加,删除 效率高.不需要移动元素,只需要修改地址。
缺点:逼历效率低,丧失随访能力。
③Vector
底层实现为数组.和ArrayList相似,线程安全,效率低。
数组扩容,原数组2倍。
•Set 接口:
①HashSet:
底层实现为HashMap(散列表),HashSet添加时只有一个值,底层向HashMap添加时,将要添加的值放在key中,value的值自动创建(无需关注)。
存储元素,无序,元素不可以重复。
线程不安全,效率高。
②TreeSet
底层实现为TreeMap(红黑树),TreeSet添加时只有一个值,底层向TreeMap添加时,将要添加的值放在key中,value的值自动创建(无需关注)。
存储元素,有序(值大小的顺序),元素不可以重复。
●Map(映射)接口:Map集合
添加数据时为 key valye(键值对),添加时根据key计算出存储的位置,获取时key计算出存储的位置。
•HashMap:
底层实现为散列表
jdk1.8之前:数组+链表
jdk1.8及之后:数组+链表+红黑树
存储数据,无序,不可以重复
线程不安全,效率高
LinkedHashMap:HashMap子类
底层实现为散列表
jdk1.8之前:数组+链表+ 链表
jdk1.8及之后:数组+链表+红黑树 +链表
后添加的链表保证添加的顺序
•TreeMap:
底层实现为红黑树
存储数据,有序(值大小顺序),不可以重复。
•HashTable:
底层实现为散列表,和HashMap十分相似,
线程安全,效率低
Properties:HashTable子类
存储配置文件中的信息
三、Iterator
中文名称:迭代器。是一个接口,每个集合中实现类都对lterator提供了内部类的实现。
通过lterator可以实现遍历集合的效果。
隐藏集合实现细节,无论是哪种集合都是通过lterator进行操作,而不是直接操作集合。通过一套API实现所有集合的遍历。
可以在遍历时删除集合中的值。
每个实现类都提供了.iterator();返回值就是迭代器对象。
四、Collections
Collections是一个工具类型,一个专门操作集合的工具类。
五、不定项参数
不定项参数必须在方法定义时,最后一个方法参数。
调用包含不定项参数的方法时,不定项参数位置,可以传递0到任意个指定类型参数。方法内部,把不定项参数当做数组使用即可。
六、哈希表
●引入hash表
在无序数组中按照内容查找,效率低下,时间复杂度是O(n)
在有序数组中按照内容查找,可以使用折半查找,时间复杂度O(log2n)
●哈希表的结构和特点
hash表 也叫散列表;特点:快。
●哈希表是如何添加数据
①计算哈希码(调用hashCode(),结果是一个int值,整数的哈希码取自身即可)
②计算在哈希表中的存储位置 y=k(x)=x%11
x:哈希码 k(x) 函数y:在哈希表中的存储位置
●存入哈希表
情况1:一次添加成功
情况2:多次添加成功(出现了冲突,调用equals()和对应链表的元素进行比较,比较到最后,结果都是false,创建新节点,存储数据,并加入链表末尾)
情况3:不添加(出现了冲突,调用equals()和对应链表的元素进行比较,经过一次或者多次比较后,结果是true,表明重复,不添加)
结论1:哈希表添加数据快(3步即可,不考虑冲突)
结论2:唯一、无序。
●如何查询数据
添加数据的过程是相同的
情况1:一次找到23
情况2:多次找到 67
结论:哈希表查询数据快
●hashCode和equals
hashCode():计算哈希码,是一个整数,根据哈希码可以计算出数据在哈希表中的存储位置
equals():添加时出现了冲突,需要通过equals进行比较,判断是否相同;查询时也需要使用equals进行比较,判断是否相同
●各种类型数据的哈希码应该如何获取 hashCode()
•Integer中源码
•Arrays中源码
如果对象为null,hash码为0.
•String中源码
给定一个内容,对内容进行hash计算,得到一个hash值。只要内容不变,得到的结果一定是不变的。但是不能通过得到的值反向得到原内容。所以hash算法是单向不可逆的算法。
可能出现问题:原内容不一样,经过hash计算后得到的结果一样的,这种情况称为hash碰撞。
String类型中的hashcode()方法。
●解决哈希碰撞的方法
• 链地址法
把Hash表的每个单元作为链表的头节点。当发生冲突时放入到同一个hash值对应的链表。
•开放定址法
发生冲突后寻找下一个hash地址。
•再次哈希法
对hash值再次进行hash计算
•建立公共溢出区
把hash表分为基本表和溢出表。当溢出时放入到溢出表中。
•装填因子/加载因子/负载因子
哈希表的长度和表中的记录数的比例-装填因子:
在实际情况中,一般需要根据最终记录存储个数和关键字的分布特点来确定Hash表的大小。另一种情况是可能事先不知道最终需要存储的记录个数,则需要动态维护Hash表的容量,此时可能需要重新计算Hash地址。
负载因子=表中的记录数/哈希表的长度,
如果装填因子越小,表明表中还有很多的空单元,则添加发生冲突的可能性越小;而装填因子越大,则发生冲突的可能性就越大,在查找时所耗费的时间就越多。
一般情况下,装填因子取经验值0.5。
●类没有重写Object中的hashCode()方法,调用Object中的hashCode()方法,根据引用类型在内存中的地址计算hashCode
重写了hashCode()方法,根据存储的值进行计算
•equals()方法:返回boolean
没有重写,使用Object中的equals()方法,比较 地址。
重写,使用重写后的equals()方法,比较 值。
•hashCode()方法;返回hash码
没有重写,使用Object中的hashCode()方法,根据内存中的地址计算 hash码
重写,使用hashCode()方法,根据存储的值计算 hash码
•equals()方法执行过后相同,hash码是否相同?
equals()方法执行过后相同 值相同
值相同,hash码相同
七、HashMap底层源码分析(JDK1.7及以前)
DK1.7及其之前,HashMap底层是一个table数组+链表实现的哈希表存储结构,使用头插。
链表的每个节点就是一个Entry,其中包括:键key、值value、键的哈希码hash、执行下一个节点的引用next四部分。
●put()方法
调用put方法添加键值对。哈希奉三步添加数据原理的具体实现;是计算key的哈希码,和value无关。
注:
•计算哈希码时,不仅调用了key的hashCode(),还进行了更复杂处理,目的是尽量保证不同的key尽量得到不同的哈希码
•根据哈希码计算存储位置时,使用了位运算提高效率。同时也要求主数组长度必须是2的幕)
•添加Entry时添加到链表的第一个位置,而不是链表末尾
•添加Entry是发现了相同的key已经存在,就使用新的value替代旧的value,并且返回旧的value
●addEntry()方法
添加元素时如达到了國值,需扩容,每次扩容为原来主数组容量的2倍
●get()方法 java
调用get方法根据key获取value。
哈希表三步查询数据原理的具体实现
其实是根据key找Entry,再从Entry中获取value即可。
●HashMap底层原理
•从ava8开始HashMap底层由数组+链表+红黑树。
•使用HashMap时,当使用无参构造方法实例化时,设置扩容因子为默认扩容因子0.75。
•当向HashMap添加内容时,会对Key做Hash计算,把得到的Hash值和数组长度-1按位与,计算出存储的位置。如果数组中该没有内容,直接存入数组中(Node节点对象),该下标中有Node对象了,把内容添加到对应的链表或红黑树中。
•如果添加后链表长度大于等于8,会判断数组的长度是否大于等于64,如果小于64对数组扩容,扩容长度为原长度的2倍,扩容后把原Hash表内容重新放入到新的Hash表中。如果Hash长度大于等于64会把链表转换红黑树。
•最终判断HashMap中元素个数是否已经达到扩容值(threshold),如果达到扩容值,需要进行扩容,扩容一倍。反之,如果删除元素后,红黑树的元素个数小于等于6,由红黑树转换为链表。
●TreeMap:底层为红黑树
①根节点颜色为黑色
②红色结点下不能有红色结点
红色结点下可以有黑色结点
黑色结点下可以有红色结点
黑色结点下可以有黑色结点
③从一个结点触发,到其子节点经过的黑色结点数相同
④左边为父节点小,右边比父节点大
⑤没有追求绝对平衡,最多3次旋转可以完成平衡
八、File类
java.io.File类,这个类对应的是操作系统中的一个文件或一个文件夹(目录)。
通过File类可以实现操作系统中文件|文件夹的创建、删除、查看、重命名等操作。
●路径
File类操作资源时有两种路径写法:
①全路径。即:从磁盘名称开始的路径。
②项目路径。即:从项目跟目录开始的路径。
在windows中,文件路径都是通过右斜杠\表示子目录。
在Java中如果字符串的值中出现右斜线表示转义字符。会对后面的一个字符进行转义。所以在Java中如果想要表示上面的路径可以使用两个右斜线或一个左斜线。
对应windows中D:\soft\jdk的路径,在Java中路径的字符串写法为
String path ="D:\\soft\\jdk";
String path2 ="D:/soft/jdk";
●File类构造方法
•构造方法
①Constructor and Description
File(File parent, String child)
创建从一个家长的抽象路径名和一个孩子的路径字符串的新File实例。
②File(String pathname)
通过将给定的路径名的字符串转换成一个抽象路径名创建一个新的 File实例。
③File(String parent, String child)
创建从父路径名的字符串和一个孩子的一个新的 File实例文件。
④File(URI uri)
通过将给定的 file:URI到一个抽象路径名创建一个新的 File实例。
•File一共提供了四个构造方法,都是有参构造,并没有提供我们能使用的无参构造。其中我们比较常用的是File(String)和File(String,String)和File(File,String)
①File(String):参数表示文件或文件夹全路径。
②File(String,String):中第一个参数表示父文件夹路径,第二个参数表示资源路径。
③File(File,String):第一个参数表示父文件夹对象,第二个参数表示资源路径。
●创建文件夹
File中有两个方法能够创建文件夹。
mkDir():创建路径中最后一层文件夹。如果希望创建的文件夹已经存在或路径中前面文件夹不存在,返回false。
mkDirs():创建文件夹,如果路径中的文件夹不存在,先创建路径中的文件夹,然后再创建指定的文件夹。返回值表示是否创建成功。
●判断资源是否存在
exists():判断资源是否存在。存在返回true,不存在返回false。
●创建空文件
createNewFile()方法可以创建一个空文件,要求文件所在目录必须真实存在。
JDK在定义这个方法时抛出了IOException(Checked异常),文件路径不正确、无法操作文件等情况下抛出此异常。返回值表示是否创建成功。
●重命名文件
renameTo(File)剪切。利用剪切特性,把资源剪切后放入到原路径中,起新名字,实现重命名功能。
●删除文件
delete()可以实现删除文件。如果文件成功删除返回true。如果文件不存在或删除失败返回false。
●判断资源是否为文件
isFile()判断资源是否是文件。如果是文件返回true,如果是文件夹或不存在这个资源返回false。
●查看文件名和文件路径
getName()文件名。
getAbsolutePath()文件在磁盘中全路径。
getPath()创建文件时指定的路径。
●查看目录中内容
list()返回值为目录中资源的名称。返回值类型String,。数组长度和资源个数相同。如果是空文件夹范围一个长度为0的数组对象。如果文件夹不存在返回null。
listFiles()返回值为目录中资源对象。返回值类型File,。数组长度和资源个数相同。如果是空文件夹范围一个长度为0的数组对象。如果文件夹不存在返回null。
九、IO流
流就是将数据无结构化的传递。强调的是数据的传输过程。
流分为输入流(Input)和输出流(Output),简称为1/0流。输入流表示从一个源读取数据,输出流表示向一个目标写数据。
Java中1/0操作主要是指使用java.io包下的内容,进行输入、输出操作。输入也叫做读取数据,输出也叫做作写出数据。
•输入流:从磁盘读到内存中
硬盘 输入Input 内存
•输出流:从内存写到磁盘中
内存 输出output 硬盘
•流是一个抽象概念。流是看不见的。
•流有出发点和目的地。相对出发点来说就是输出流,相对目的地来说就是输入流。
•流是数据运输的载体。
●Java中IO流分类
在JDK中提供了10类的支持。这些类都在java.io包中。
①根据方向的划分:输入流和输出流
②根据数据的单位划分:字节流和字符流。
③根据功能的划分:节点流和处理流(缓冲流)
字节流就是流中数据以字节为单位(byte)。特别适用于音频、视频、图片等二进制文件的输入、输出操作。字符流就是流中数据以字符为单位。存在的意义:高效、方便的读取文本数据。