java知识

(一)java集合类(done)
1、在java集合类中最常用的是Collection和Map的接口实现类。Collection又分为List和Set两类接口,List的实现类有ArrayList、LinkedList、Vector、Stack,Set接口的实现类有HashSet、TreeSet,而Map的实现类主要有HashMap、ConcurrentHashMap、TreeMap。

2、ArrayList是容量可以改变的非线程安全集合(可以使用Collections.synchronizedList方法实现线程安全),内部使用数组进行存储。ArrayList支持对元素的快速随机访问,但是插入和删除时速度通常会很慢,因为这个过程需要移动其他元素。ArrayList的默认大小为10,在不传入指定的列表大小时,默认使用空列表。ArrayList在每次添加数据时都会检查空间是否足够,若不足就会按原数组空间的1.5倍扩容(向下取整),最后会将新的数组空间大小和插入数据后需要的空间大小作比较,取两者之前的更大者。之后就会将旧数组整体拷贝进新的数组空间。
3、HashMap的默认容量值为16,默认负载因子为0.75,默认阈值为12。HashMap的容量不会在new的时候就分配,而是在put的时候才进行分配。HashMap的实际容量为比传入容量参数大的2的幂,而ConcurrentHashMap实际容量为比(传入容量参数除4/3后)大的2的幂,此后每次扩容都是增加2倍。HashMap的容量为2的幂的主要是因为HashMap在计算槽位的时候使用的算法是(n-1)& hash,当n为2的幂时元素可以更加均匀的散列。HashTable是HashMap的线程安全版本,两者主要区别在于HashTable在一些关键的函数中增加了synchronized关键字,由于性能不佳目前已经被ConcurrentHashMap淘汰了。HashMap是线程不安全的,在java8以前甚至可能出现由于多线程resize导致的循环链表,线程不安全的两个小例子:两个线程同时对同一桶位插入数据可能导致某个线程的数据被覆盖、某个线程在执行resize时另一线程可能读不到数据。
4、HashMap的key和value都可以为null,key为null的元素会被散列到数组的第一个元素,但在使用stream的Collectors.toMap方法时HashMap的value不可以为null,且key不可以重复,否则会抛出异常。另外不同于HashMap,ConcurrentHashMap的key和value都不能为null。
5、java8对HashMap的改进?
主要有两大改动
(1)在数组桶的链表长度超过8时,HashMap通过红黑树替换链表。
(2)在HashMap扩容时不再重新对每个节点计算桶位,而是通过(节点的hash值&旧数组容量 == 0)来形成两条新的链表,分别插在原桶位和旧数组容量+原桶位。
6、树的基础知识:
高度:从某节点出发到叶子节点的简单路径上边的数量被称为该节点的“高度”
重要的二叉树:平衡二叉树、二叉查找树、红黑树
平衡二叉树:树及所有子树的左右高度差不超过1
二叉查找树:对于树的任意节点而言它的左子树的所有节点值都小于它,而他的右子树的所有节点值都大于它。它主要有前序遍历、中序遍历、后序遍历三种遍历方式。随着数据不停的增加或删除,二叉查找树容易失衡
红黑树:红黑树是一种可以自平衡的二叉查找树。红黑树在每个节点上增加了颜色属性,可以为红色或黑色,红黑树通过按规则着色和特定的旋转来保持自身的平衡,它新增、删除、查找的最坏时间复杂度均为O(logN)。相对于其他的自平衡树例如AVL树,红黑树并不严格保证时左右子树的高度差超过1,这使得红黑树在删除时能够更快的恢复平衡,成本比较低,所以面对频繁的插入删除时,红黑树更适合,而面对低频修改,大量查询时AVL树更合适
7、Comparable接口和Comparator接口的区别?
如果要使用Comparable接口,就必须实现该接口并重写compareTo方法。而Comparator接口可以在类外实现,并可以将其实现对象传入到Collections.sort或Arrays.sort方法中以实现排序。Comparator接口的使用体现了基于开闭原则的设计。
8、集合中的hashCode和equals?
hashCode和equals用来标识对象,两者协同工作来判断对象是否相等,当hashCode的值相同时,还需要调用equals进行一次值比较。任何时候在覆写equals时一定要同时覆写hashCode,因为Map、Set等集合都是同时使用两者来判断对象是否相同的。hashCode是根据对象地址进行相关计算得到的int类型数值。在做对象间的比较时,尽量使用Objects.equals方法来避免空指针。
9、集合的fail-fast机制?
fail-fast是一种在集合遍历时的错误检测机制,如果在遍历的过程中出现了意料之外的修改就会抛出ConcurrentModificationException异常,这种机制经常出现在多线程环境下。线程会维护一一个modCount用来记录集合已经修改的次数,在遍历集合时会时时检查modCount的数值是否发生变化,若发生变化则抛出异常。
10、ConcurrentHashMap知识:
在java8以前ConcurrentHashMap通过锁分段的思想将整个hashMap分为16个segment,每个segment负责一部分数组元素,并通过reentrantLock锁来保证每个segment的数据安全。而在java8后ConcurrentHashMap取消了segment,大量的使用了volatile、cas等技术进一步减少了锁竞争造成的性能影响。java8后ConcurrentHashMap有三点主要的改动:(1)取消分段锁机制,进一步降低冲突概率、(2)引入红黑树、(3)使用了更加优秀的方式统计元素的数量。
get操作逻辑:计算出key的hash值并计算出槽位,然后通过getObjectVolatile获取数该槽位的元素,并比较hash值和key值,若相同则返回,如果槽位内节点的hash值小于0则说明正在进行扩容,则通过ForwardingNode的find函数去新的数组nextTable中进行查找。否则就遍历单链表查找相应节点。
put操作逻辑:首先检查核心的Node<K,V>[] table是否已经初始化,如果没有初始化,则利用CAS将sizeCtl的值置为-1进行初始化。查询key相应的槽位是否为 null,若为null直接通过CAS将键值对放入槽位。如果相应的槽位已经有节点,并且其hash值为-1,则表示正在进行扩容,则当前线程帮忙进行扩容。否则通过synchronized锁住槽位内的节点即链表的头结点,然后遍历链表,寻找是否有hash值及key值相同的节点,若有则将value设置进去,否者创建新的节点加入链表。通过addCount函数更新ConcurrentHashMap键值对的数量,并检查是否需要进行扩容。
扩容操作逻辑:首先新建一个两倍长度的数组nextTable。初始化ForwardingNode节点,其中保存了新数组nextTable的引用,在处理完每个槽位节点后当做占位节点,表示该槽位已经处理过了。通过倒序的方式为线程分配需要处理数组元素个数(默认每个线程16个元素)。每个线程处理自己负责的数组元素,具体逻辑和HashMap基本相应,处理完的元素的位置上会放入ForwardingNode节点。
计数方法:代码里的变量baseCount用于在无竞争环境下记录元素的个数,每当插入元素或删除元素时都会利用CAS更新键值对个数。当有线程竞争时,会使用CounterCell数组来计数,每个ConuterCell都是一个独立的计数单元。线程可以通过ThreadLocalRandom.getProbe() & m找到属于它的CounterCell进行计数。这种方法能够降低线程的竞争,相比所有线程对一个共享变量不停进行CAS操作性能上要好很多。这里的CounterCell数组初始容量为2,最大容量是机器的CPU数。


其他知识

1、http和下https的区别?https如何保证数据安全?
HTTPS全称HTTP over SSL,简单的说就是在之前的HTTP传输上增加了SSL协议的加密能力,SSL全称安全套接字层,工作于传输层和应用层之间,为应用提供数据的加密传输。在普通的数据传输中,我们通常会在客户端和服务器端使用对称协议进行加密,即两端使用一个相同的秘钥来对传输的数据进行加密和解密。但是由于秘钥同样需要在网络上进行传输,我们仍然需要对秘钥进行保护,为了解决这个问题,我们可以使用非对称加密,就是我们常见的RSA算法,该算法能够生成公钥和私钥,公钥任何人都可见,用来对信息加密,而私钥用来对加密信息进行解密,这种非对称加密算法,加密和解密的耗时都比较长,所以只适合对少量的数据进行加密传输,就刚好可以用来进行对称加密算法的秘钥传输。有了这种加密方式后,整个传输流程看起来安全多了,但是如果在端对端的传输过程中,某一端的传输数据被黑客劫持,那么黑客同样可以自己生成公钥,并和另一端进行数据传输,为了解决这个问题,在使用HTTPS进行数据传输时,需要提供CA办法的数字证书来证明传输者的身份。

2、TCP与UDP的区别?
UDP只是在IP数据包上增加了端口等部分信息,是面向无连接的,是不可靠传输,多用于视频通信,电话会议等。与之相反,TCP是面向连接的,是一种端到端间通过失败重试机制建立的可靠数据传输方式,就像是一条一条固定的道路承载着数据的可靠传输。
3、Java泛型原理?
泛型的本质是类型参数化,可以解决不确定具体对象类型的问题,如果不使用泛型而使用Object,则在使用不当的情况下可能出现类型转换异常,泛型实际上主要是为了在编译器进行类型检查,保证程序员使用泛型时能够安全的存储数据和使用数据,在完成编译泛型类型就会被擦除,使用泛型的好处:保证类型安全、提升代码的可读性。泛型使用时,约定俗成的符号包括:E代表Element,用于集合中的元素;T代表the Type of object,表示某个类;K代表Key,V代表Value,用于键值对元素。当List和泛型相结合时,可以把泛型的功能发挥到极致。List完全没有类型限制和赋值限制,如果随意使用,可能会造成类型转换异常。List<Object>并不完全等同于List,List<Object>不能接受其他泛型赋值,比如List<Object>不能接受List<Integer>类型变量的赋值,而List却可以。类型List<?>表示接受任何类型的集合赋值,但是赋值之后就不能在随意添加元素了,但仍可以执行remove和clear操作。<? extends T>表示T及T的子类集合,<? super T>表示T及T的父类集合。<? extends T>除了null外不能add任何元素,这主要是因为T及其子类类型很多,无法匹配添加元素的类型,<? extends T>可以进行get操作,但是get的元素类型都是T及其父类。<? super T>可以添加元素,但是只能添加T及T子类的对象,这主要是因为满足了上转型,<? super T>在进行get操作时,虽然能够返回对象,但是只能返回Object类型对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值