Java中的数据结构

一 八大基础数据类型

在java中一般对象是储存在堆内存中的,栈内存中只是储存了其引用。而基本数据类型是储存在栈内存中的,调用更加方便。而在java中是面向对象的,很多地方必须要使用对象,所以又为各大基本类型提供了包装类型。而为了基本数据类型和对应包装类型转换,java提供了自动拆装箱功能(即基本类型和对应包装类自动转换)。但是自动拆箱的时候需要注意,值为null的情况。
1.char 字符型
2.boolean 布尔型
3.byte short int long 整形
byte:一个字节占8位,所以取值范围是-128(-2^7) 到127。
short:两个字节,所以取值范围是-32,768 (-2^15)到32,767。
int:四个字节,所以取值范围是-2,147,483,648 (-2^31)到2,147,483,647。
long:八个字节,所以取值范围是-9,223,372,036,854,775,808 (-2^63)到9,223,372,036, 854,775,807。
计算机中数是通过其补码来储存的,正数的补码还是其本身,而负数的补码需要
由原码取反加一,所以负数取值范围会大1。
4.float double 浮点型
float:四个字节 单精度。
double:八个字节 双精度。
计算机中的小数其实是十进制小数的近似值,所以不能用浮点数表示金额。一般使用BigDecimal来表示金额。

二 字符串

字符串不属于基本数据类型。一旦一个String对象被创建出来,他就无法被修改。所有对该对象的操作都会返回一个新的字符串,只是引用没变。只有StringBuffer 或者 StringBuilder对象才是可以被修改的。如果你需要一个经常被改变的字符串建议使用后两者。否则会有大量时间浪费在垃圾回收上,因为每次试图修改都有新的string对象被创建出来。同时字符串也是有长度限制的,在编译期,要求字符串常量池中的常量不能超过65535,并且在javac执行过程中控制了最大值为65534。在运行期,长度不能超过Int的范围,否则会抛异常。

StringBuffer字符串扩长的时候效率低下,但是线程安全,StringBuilder字符串扩长的时候效率高,但是线程不安全。扩长时长度为 length*2+2 。速度上来说 StringBuilder > StringBuffer > String。底层都是存放在char[] 数组中。

字符串常量池:在JVM中,为了减少相同的字符串的重复创建,为了达到节省内存的目的。会单独开辟一块内存,用于保存字符串常量,这个内存区域被叫做字符串常量池。当代码中创建字符串对象时,JVM 会先对这个字符串进行检查,如果字符串常量池中存在相同内容的字符串对象的引用,则将这个引用返回;否则,创建新的字符串对象,然后将这个引用放入字符串常量池,并返回该引用。

三 集合

java集合主要分为两大类分别继承两大顶级接口:Collection和Map。其中Collection接口下主要包含了List,Set,Queue三大接口。Map接口下主要包含了HashMap,Hashtable,TreeMap,WeakHashMap,IdentityHashMap几大实现类。

List接口

list接口下主要有三大实现类,分别是ArrayList,LinkedList,Vercotr。三者都可以保存有序且可重复的数据。其中Vercotr是线程安全的,一般不用所以不多介绍,主要介绍下ArrayList以及LinkedList。

ArrayList是一个Object数组。适合保存有序,可重复数据。利于遍历查询,但是增删慢。因为ArrayList底层是一个一维数组,可以通过下标快速遍历。但是如果要增加或者删除其中某个元素,需要将其后的元素依次后移或者前移耗费时间较多。且当ArrayList增加元素的时候实际上有两步操作,首先要判断当前数组的长度是否足够,不够会自增1.5背后将新元素添加到数组末尾。这两步操作都是没加锁的,所以会产生原子性和可见性问题。多线程操作同一数组对象时会导致线程不安全。

LinkedList是一个双向链表,它将数据一个个储存在Node节点中,每个Node节点包含上一节点和下一节点的引用。适合保存有序可重复数据。利于增删,但是查询慢,遍历适合使用迭代器。同时因为LinkedList实现了Deque接口,而Depue接口又继承了Queue接口,所以实际上LinkedList也可以当作队列来使用。因为LinkedList底层是一个双向链表,每次增加或者删除元素只需更改前后节点的信息即可所以增删快,但是因为没有下标查询一个元素需要重第一个节点开始往下找,所以不利于查询。

Set接口

Set接口下主要有两大实现类,分别是HashSet和TreeSet。两者都能保存不可重复的数据。且都是线程不安全的。

HashSet实际上就是对HashMap的封装,通过HashMap中value值不可重复实现存放不可重复数据,它每添加一个元素实际是存放在HashMap中的Key值中,而Value值则存放一个空的Object对象,所以它是无序的。一般我们使的比较多的实际上是HashSet的一个子类LinkedHashSet,LinkedHashSet中每个元素由LinkedHashMap储存,所以也算是有序的,在需要遍历所有元素时优于HashSet。适合保存不重复的数据,具有较好的存取和查找性能。因为每个值都会有一个独特的散列码,所以查找和存取都比较方便。

TreeSet是一个树集,TreeSet中每个元素由TreeMap储存。通过TreeMap中value值不可重复实现存放不可重复数据,它每添加一个元素实际是存放在TreeMap中的Key值中,而Value值则存放一个空的Object对象。

Queue接口

队列主要有三种队列,第一是链表队列也就是前面已经介绍的LinkedList,第二就是数组队列ArrayDeque,一般不用就不多介绍。第三就是接下来要讲的PriorityQueue优先队列。

PriorityQueue优先队列也是一个Object数组,数组里面是一个二叉堆(完全二叉树)。适合保存有序,可重复数据,排序性能有极大提升。可以自定义比较算法。默认是二叉小顶堆,当你需要插入数据时,会先将数据插入数组末尾,找到其父节点位置 = floor((k - 1) / 2)后,比较大小并交换位置。当你需要删除数据时,会先将堆顶元素取出,并将最后一位元素取出放入堆顶,并根据其子节点位置 = 2k+1 和 2k+2找到子节点值后进行比较交换位置。这样堆顶元素仍然是最小的元素。需注意的是,只需比较到倒数第二层即可,又因完全二叉树最后一层最多存一半数据,所以当子节点值大于size/2时即可结束比较。

java中的树结构

介绍Map接口前,需要先理解下树的相关知识。树主要分为多叉树和二叉树。
多叉树:平时java程序员涉及到的就是储存索引的数据结构 B+树
二叉树:主要分为满二叉树和二叉查找树。平时java程序员涉及到的就是优先队列中的完全二叉树(完全二叉树<满二叉树,具体还可分为小顶堆和大顶堆)以及Map结构中的红黑树(红黑树<二叉查找树,注意:红黑树不是平衡二叉查找树,它遵循大致平衡)

红黑树的五大性质:

1.根节点必须是黑色
2.节点必须是红色或者黑色
3.叶子节点都是为空的黑色节点
4.不能连续有两个红色节点
5.任意一个节点到其所有叶子节点路径上黑色节点个数相同

平衡二叉树相较与二叉查找树效率更高,但是做插入操作时需要做多次旋转比较复杂。而红黑树相比于平衡二叉树插入操作更为简单(最多一次旋转),查找效率略低但可忽略不计。

红黑树的插入:

红黑树也是一个二叉查找树,所以每次插入操作与二叉查找树一致。只有两点不同。第一:默认插入节点为红色(更容易满足五大性质) 第二:如果插入节点后不满足五大性质,需做对应的变色和旋转操作,使其满足红黑树的五大性质。

1.如果插入节点的父节点是黑色节点,无需任何处理都能满足
2.如果插入节点的父节点是红色节点且叔父节点是红色节点,则需修改父亲节点、叔父节点颜色为黑色并将祖父节点的颜色改为红色,并将祖父节点当成新插入节点继续判断,直到满足红黑树特性。
3.如果插入节点的父亲节点是红色节点且不存在叔父节点(此时叔父节点不可能为黑色节点),则需修改父亲节点颜色为黑色,同时根据插入的位置进行左旋或者右旋操作,以此满足红黑树特性。所以说红黑树的插入只需做一次旋转即可。

红黑树的删除:

红黑树的删除操作需首先判断删除节点是否是叶子节点(不包含最后一层空的黑色节点),如果不是则需先中序遍历红黑树,找到删除节点的前驱节点或者后继节点,值替换后再删除前驱或者后继节点。这样保证删除操作时,必定删除的是叶子节点,或者是拥有一个红色子节点的黑色节点(按红黑树特性只有这一种情况)

1.如果叶子节点是红色节点,则可以直接删除
2.拥有一个红色叶子节点的黑色节点:使用红色节点代替黑色节点,并将红色节点染红即可
3.如果叶子节点是黑色节点(非根节点不可能没有兄弟节点):
①如果是根节点直接删除即可。
②兄弟节点是黑色节点,且至少有一个红色子节点:以父节点进行右旋,新父节点继承原父节点的颜色,并将新子节点颜色变成黑色
③兄弟节点是黑色节点,且子节点没有红色节点
如果父亲节点是红色:将父亲节点染为黑色,兄弟节点染为红色。
如果父亲节点是黑色:将兄弟节点染为红色,并将父亲节点当作删除节点继续处理
④兄弟节点是红色
将父亲节点染为红色,兄弟节点染为黑色,并将父亲节点右旋。这样就如同兄弟节点是黑色节点一样,继续按照同样的方法处理

Map接口

Map接口主要有五个实现类分别是:HashMap,HashTable,TreeMap,WeakHashMap,WeakHashMap。其中HashTable是线程安全的,现在一般不用所以略过。WeakHashMap和WeakHashMap用的比较少,主要还是讲解HashMap以及TreeMap两大实现类。

HashMap实际储存数据是使用数组+链表的形式。每次新增数据时,HashMap会先计算其hash值,并与数组长度取模得到插入数组位置(如果实际存储数据量大于负载因子(默认0.75) * 数组长度,则数组会进行扩容,变为原来的两倍),如果该位置已经有值,则以链表的形式继续添加。如果链表长度大于8,链表则会转为红黑树。如果红黑树数据量小于等于6则会退化为链表。所以HashMap是无序的,而LinkedHashMap则是在HashMap的每个节点上添加了befor和after两个参数,使其变得有序,但增加了开销。

TreeMap当前使用的是红黑树。每次添加元素都会按顺序添加,所以他是有序的。TreeSet有两种排序方法自然排序和定制排序。自然排序:TreeSet会调用集合元素的compareTo()方法来比较元素的大小关系,然后将元素按照升序排列,适合保存有序的不可重复数据。

注意:TreeMap的有序是保证了键值按指定顺序排序,而LinkedHashMap的有序是保证了插入的顺序。且TreeMap的Key值不能为null。以上三种map都是线程不安全的

Concurrent线程安全的集合

BiMap双向map

HashBiMap,EnumBiMap,ImmutableBiMap了解即可

Immutable不可变集合

主要包括ImmutableSet,ImmutableList,ImmutableMap了解即可

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值