Java知识点整理一

时间:2022-11-17 14:54:29

一、集合类:List 和 Set 比较,各自的子类比较(ArrayList, Vector, LinkedList, HashSet, TreeSet)。

        1)List: 元素是有序的,元素可以重复,因为每个元素都有自己的下标(索引)

            |-- ArrayList: 底层的数据结构是数组结构,特点是:查询很快,增、删稍微慢点,线程不同步;

            |-- LinkedList: 底层使用的是链表数据结构,特点是增、删很快,查询慢;

            |-- Vector: 底层是数组数据结构,线程同步,被 ArrayList 代替了,现在用的只有它的枚举。

        2)Set: 元素是无序的,且不可以重复(存入和取出的顺序不一定一致),线程不同步

            |-- HashSet: 底层是哈希表数据结构。根据 hashCode 和 equals 方法来确定元素的唯一性;

            |-- TreeSet: 可以对 Set 集合中的元素进行排序(自然排序),底层的数据结构是二叉树,也可以

                自己写个类实现 Comparable 接口,定义自己的比较器,将其作为参数传递给 TreeSet 的构造函数;

        3)Map: 这个集合是存储键值对的,一对一往里存,而且要确保键的唯一性,(01,张三)这样的形式打印

                出来就是 01=张三

            |-- HashTable: 底层是哈希表数据结构,不可以存入 null 键和 null 值,该集合是线程同步的,效率比较低。

                出现于 JDK1.0;

            |--HashMap: 底层是哈希表数据结构,可以存入 null 键和 null 值,线程不同步,效率较高,代替了 HashTable,

                出现于 JDK1.2;

            |--TreeMap: 底层是二叉树数据结构,线程不同步,可以用于各 map 集合中的键进行排序。


二、HashMap 的底层实现, ConcurrentHashMap 的底层实现。

                在 Java 编程语言中,最基本的结构就是两种, 一个是数组, 另一个是模拟指针(引用),所有的数据结构

        都可以用这两个基本结构来构造的, HashMap 也不例外。

                HashMap 实际上是一个“链表的数组”的数据结构,每个元素存放链表头结点的数组,即数组和链表的结合体。

        HashMap 底层就是一个数组,数组中的每一项又是一个链表。每当新建一个 HashMap 时就会初始化一个数组。

                ConcurrentHashMap 是由 Segment 数组结构和 HashEntry 数组结构组成。Segment 是一种可重入锁

        ReentrantLock, 在ConcurrentHashMap 里扮演锁的角色,HashEntry 则用于存储键值对数据。

                一个 ConcurrentHashMap 里包含一个 Segment 数组,Segment 的结构和 HashMap 类似,是一种数组和

        链表结构,一个 Segment 里包含一个HashEntry 数组。

                每个 HashEntry 是一个链表结构的元素,每个 Segment 守护着一个 HashEntry 数组里的元素,当对 HashEntry 

        数组的数据进行修改时,必须首先获得它对应的 Segment 锁。


三、如何实现 HashMap 顺序存储。

                方法一:维护一张表,存储数据插入的顺序,可以使用 Vector。但是如果需要删除数据,首先得在 Vector 里面找到

        那个数据,再进行删除操作,而删除又需要移动大量数据,性能效率很低。若使用 List,移动问题可以解决。但是查找数据

        的时间消耗为O(n),如果删除 m 次,那么查找数据的性能就是O(n*m),那总体性能就是O(n2)。性能还是无法

        接受。

                方法二:可以在 HashMap 里面维护插入顺序的 ID,在 value 建一个字段存储 ID 值,再维护一张表 Vector,,并且

        ID 对应 Vector 里面的值。

        插入的时候,ID+1, hashmap.insert, vector.push_back;

        删除的时候,先 hashmap.find(key),得到 value,并从 value 中得到 ID,通过 ID 把对应的 vector 值置为无效;

        更新:删除+插入。

        维护工作完成,输出的时候直接输出 vector 里面的值就可以了,无效的就 continue。

        算法复杂度为 O(n)。

                方法三:Java 里面有一个容器 LinkedHashMap,它能实现按照插入的顺序输出结果。它的原理也是维护一张表,

        但它是链表,并且 hashmap 中维护指向链表的指针,这样可以快速定位链表中的元素进行删除。它的时间复杂度也是

        O(n),但空间上要比上面方法二少些。


四、HashTable 和 ConcurrentHashMap 的区别。

                HashTable 容器使用 synchronized 来保证线程安全,但在线程竞争激烈的情况下,HashTable 的效率非常低下,因

        为当一个线程访问 HashTable 的同步方法时,其他线程访问此方法时,可能会进入阻塞或轮询状态。如线程 1 使用 put 进

        行添加元素,线程 2 不但不能使用 put 方法添加元素,并且也不能使用 get 方法来获取元素,所以竞争越激烈效率越低。

                ConcurrentHashMap 使用的锁分段技术,首先将数据分成一段一段的存储,然后给每一段数据分配一把锁,当一个

        线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。


五、String, StringBuffer, StringBuilder 的区别。

                1、三者在执行速度方面的比较:StringBuilder > StringBuffer > String

                2、String < (StringBuffer, StringBuilder) 的原因:

                    String:字符串常量, StringBuffer:字符串常量, StringBuilder:字符串常量

                    StringBuffer:线程安全的,StringBuilder:非线程安全的

                    当我们在字符串缓冲区被多个线程使用时,JVM 不能保证 StringBuilder 的操作是安全的,虽然它的速度最快,

            但是可以保证 StringBuffer 是可以正确操作的。

                    当然大多数情况下就是我们在单线程下进行的操作,所以大多数情况下是建议使用 StingBuilder 而不用

            StringBuffer 的,就是速度的原因。

                    3、对三者使用做下简单的总结:

                        a、如果要操作少量的数据,使用 String

                        b、单线程操作字符串缓冲区下大量数据,使用 StringBuilder

                        c、多线程操作字符串缓冲区下大量数据,使用 StringBuffer。