JDK方法全解

1 篇文章 0 订阅

目录

一.Object类

1.介绍

2.native关键字(JNI  java native interface)

3.equals方法

4.clone方法

5.finalize 方法

二.ArrayList类

1.ArryayList源码字段属性

2.无参构造

3.有参构造1

4.有参构造2

5.添加元素

6.删除元素

7.修改元素

8.查找元素

三.LinkedList类

1.LinkedList定义

2.LinkedList源码字段属性

3.构造函数

4.添加元素

5.修改元素

6.查找元素

7.删除元素

8.ArrayList和LinkedList的区别

四.HashMap类

1.定义

2.哈希表(Hash表)

3.JDK1.8以前HashMap数据结构

4.JDK1.8以后HashMap数据结构

 5.HashMap源码字段属性

6.添加元素

7.删除元素

8.查找元素

9.扩容机制

五.ConcurrentHashMap类

1. ConcurrentHashMap 1.7

2.ConcurrentHashMap 1.8

六.Synchronized类

1.作用

2.Synchronized用法


一.Object类

1.介绍

        Object 类是所有类的基类,当一个类没有直接继承某个类时,默认继承Object类

2.native关键字(JNI  java native interface)

问题:为什么要用 native 来修饰方法,这样做有什么用?

答:native 用来修饰方法,Java 调用非 Java 代码的接口。

3.equals方法

        在Object类中,== 运算符和 equals 方法是等价的,都是比较两个对象的引用是否相等。

String类重写了Object的equals方法,String中的equals方法是判断字符串的内容是否相同。

Object中equals方法源码如下

public boolean equals(Object obj) {
    return (this == obj);
}

4.clone方法

源码

protected native Object clone() throws CloneNotSupportedException;

保护方法,实现对象的浅拷贝,只有实现了Cloneable接口才可以调用该方法,否则抛出 CloneNotSupportedException异常。

5.finalize 方法

源码

protected void finalize() throws Throwable { }

当 GC 确定不再有对该对象的引用时,GC 会调用对象的 finalize() 方法来清除回收。

二.ArrayList类

1.ArryayList源码字段属性

//集合的默认大小
private static final int DEFAULT_CAPACITY = 10;
//空的数组实例
private static final Object[] EMPTY_ELEMENTDATA = {};
//这也是一个空的数组实例,和EMPTY_ELEMENTDATA空数组相比是用于了解添加元素时数组膨胀多少
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//存储 ArrayList集合的元素,集合的长度即这个数组的长度
//1、当 elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 时将会清空ArrayList
//2、当添加第一个元素时,elementData 长度会扩展为 DEFAULT_CAPACITY=10
transient Object[] elementData;
//表示集合的长度
private int size;

2.无参构造

        此无参构造函数将创建一个 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 声明的数组,注意此时初始容量是0,而不是大家以为的 10。根据默认构造函数创建的集合,ArrayList list = new ArrayList();此时集合长度是0,在添加一个元素时,数组才会扩容为10

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

3.有参构造1

        初始化集合大小创建 ArrayList 集合。当大于0时,给定多少那就创建多大的数组;当等于0时,创建一个空数组;当小于0时,抛出异常。

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
    }
}

4.有参构造2

        将已有的集合复制到 ArrayList 集合中

    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

5.添加元素

add(E e);

源码

public boolean add(E e) {
    //添加元素之前,首先要确定集合的大小(是否需要扩容)
    ensureCapacityInternal(size + 1); 
    elementData[size++] = e;
    return true;
}

6.删除元素

remove(int index);

7.修改元素

set(int index, E e);

8.查找元素

get(int index)

三.LinkedList类

1.LinkedList定义

        LinkedList是一种可以在任何位置进行高效地插入和移除操作的有序序列,它是基于双向链表实现的

2.LinkedList源码字段属性

//链表元素(节点)的个数
transient int size = 0;
/**
*指向第一个节点的指针
*/
transient Node<E> first;
/**
*指向最后一个节点的指针
*/
transient Node<E> last;

 Node 类,这是 LinkedList 类中的一个内部类,其中每一个元素就代表一个 Node 类对象,LinkedList 集合就是由许多个 Node 对象类似于手拉着手构成。

private static class Node<E> {
    E item;//实际存储的元素
    Node<E> next;//指向上一个节点的引用
    Node<E> prev;//指向下一个节点的引用
    //构造函数
    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

3.构造函数

public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);
}

        LinkedList 有两个构造函数,第一个是默认的空的构造函数,第二个是将已有元素的集合Collection 的 实例添加到 LinkedList 中,调用的是 addAll() 方法

         注意:LinkedList 是没有初始化链表大小的构造函数,因为链表不像数组,一个定义好的数组是必 须要有确定的大小,然后去分配内存空间,而链表不一样,它没有确定的大小,通过指针的移动来指向 下一个内存地址的分配。

4.添加元素

(1)将指定元素添加到链表头

addFirst(E e);

(2)将指定元素添加到链表尾,无返回值

addLast(E e)

(3)将指定元素添加到链表尾,返回boolean

add(E e)

(4)将指定的元素插入此列表中的指定位置

add(int index, E element)

(5)按照指定集合的迭代器返回的顺序,将指定集合中的所有元素追加到此列表的末尾

addAll(Collection<? extends E> c)

5.修改元素

(1)用指定的元素替换此列表中指定位置的元素

 set(int index, E element)

6.查找元素

(1)返回此列表中的第一个元素

getFirst()

(2)返回此列表中的最后一个元素

getLast()

(3)返回指定索引处的元素

get(int index)

(4)返回此列表中指定元素第一次出现的索引,如果此列表不包含元素,则返回-1

indexOf(Object o)

7.删除元素

(1)移除并返回第一个元素

remove()和removeFirst()

(2)中删除并返回最后一个元素

removeLast()

(3)删除指定位置的元素,并返回该元素

remove(int index)

8.ArrayList和LinkedList的区别

(1)ArrayList是数组实现的集合操作,而LinkedList是链表实现的集合操作
(2)对于大数据量的查询使用get(index)方法,ArrayList复杂度为O(1),LinkedList为O(n)

四.HashMap类

* HashMap底层数据结构(为什么引入红黑树、存储数据的过程、哈希碰撞相关问题)
* HashMap成员变量(初始化容量是多少、负载因子、数组长度为什么是2的n次幂)
* HashMap扩容机制(什么时候需要扩容? 怎么进行扩容?)
* JDK7 与 Jdk8比较,JDK8进行了什么优化

1.定义

(1)HashMap基于哈希表的Map接口实现以key-value存储形式存在。
(2)HashMap的实现不是同步的,它不是线程安全的。它的key、value都可以为null。
(3)HashMap中的映射不是有序的
        JDK1.7 HashMap数据结构:数组 + 链表
        JDK1.8 HashMap数据结构:数组 + 链表 / 红黑树

2.哈希表(Hash表)

(1)定义       

         Hash表是一种根据关键字值(key - value)而直接进行访问的数据结构,在链表、数组等数据结构中,查找某个关键字,通常要遍历整个数据结构,也就是O(N)的复杂度,但是对于哈希表来说,只是O(1)复杂度。

(2)散列函数

        哈希表是通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表,只需要O(1)的时间级。

(3)多个 key 通过散列函数会得到相同的值,这时候怎么办?
        链地址法:相同的值挂到链表下
        如果数据量大,会造成子链表很长,那么我们查找所需遍历的时间也会很长。

3.JDK1.8以前HashMap数据结构

         JDK 8 以前 HashMap 的实现是 数组+链表,即使哈希函数取得再好,也很难达到元素百分百均匀分布。

        当 HashMap 中有大量的元素都存放到同一个桶中时,这个桶下有一条长长的链表,极端情况HashMap 就相当于一个单链表,假如单链表有 n 个元素,遍历的时间复杂度就是 O(n),完全失去了它的优势。

4.JDK1.8以后HashMap数据结构

        JDK 8 后 HashMap 的实现是 数组+链表+红黑树

        桶中的结构可能是链表,也可能是红黑树,当链表长度大于阈值(默认为8)并且当前数组的长度大于64时,此时此索引位置上的所有数据改为使用红黑树存储。

 5.HashMap源码字段属性

//序列化和反序列化时,通过该字段进行版本一致性验证
private static final long serialVersionUID = 362498820763181265L;
//默认 HashMap 集合初始容量为16(必须是 2 的倍数)
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//集合的最大容量,如果通过带参构造指定的最大容量超过此数,默认还是使用此数
static final int MAXIMUM_CAPACITY = 1 << 30;
//默认的填充因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//当桶(bucket)上的结点数大于这个值时会转成红黑树(JDK1.8新增)
static final int TREEIFY_THRESHOLD = 8;
//当桶(bucket)上的节点数小于这个值时会转成链表(JDK1.8新增)
static final int UNTREEIFY_THRESHOLD = 6;
/**(JDK1.8新增)
* 当集合中的容量大于这个值时,表中的桶才能进行树形化 ,否则桶内元素太多时会扩容,
* 而不是树形化 为了避免进行扩容、树形化选择的冲突,这个值不能小于 4 *
TREEIFY_THRESHOLD
*/
static final int MIN_TREEIFY_CAPACITY = 64;
/**
* 初始化使用,长度总是 2的幂
*/
transient Node<K,V>[] table;
/**
* 保存缓存的entrySet()
*/
transient Set<Map.Entry<K,V>> entrySet;
/**
* 此映射中包含的键值映射的数量。(集合存储键值对的数量)
*/
transient int size;
/**
* 跟前面ArrayList和LinkedList集合中的字段modCount一样,记录集合被修改的次数
* 主要用于迭代器中的快速失败
*/
transient int modCount;
/**
* 调整大小的下一个大小值(容量*加载因子)。capacity * load factor
*/
int threshold;
/**
* 散列表的加载因子。
*/
final float loadFactor;

6.添加元素

put(key,value);

7.删除元素

remove(key);

8.查找元素

get(key);

9.扩容机制

        我们知道集合是由数组+链表+红黑树构成,向 HashMap 中插入元素时,如果HashMap 集合的元素已经大于了最大承载容量,那么必须扩大数组的长度,Java中数组是无法自动扩容的,我们采用的方法是用一个更大的数组代替这个小的数组,就好比以前是用小桶装水,现在小桶装不下了,我们使用一个更大的桶。1.8使用的是2次幂的扩展(指长度扩为原来2倍)。

五.ConcurrentHashMap类

1. ConcurrentHashMap 1.7

(1)存储结构

        在JDK1.7中ConcurrentHashMap采用了数组+分段锁的方式实现, Segment 的个数一旦初始化就不能改变,Segment 的个数是 16 个,所以可以认为 ConcurrentHashMap 默认支持最多 16 个线程并发。

Segment(分段锁):是为了减少锁的粒度。

2.ConcurrentHashMap 1.8

(1)存储结构

        在JDK1.8中ConcurrentHashMap采用了 Node 数组 + 链表 / 红黑树 。当冲突链表达到一定长度时,链表会转换成红黑树。

        使用的 Synchronized 锁加 CAS 的机制保证线程安全。

六.Synchronized类

1.作用

        synchronized的作用是保证在同一时刻, 被修饰的代码块或方法只会有一个线程执行,以达到保证并发安全的效果。

2.Synchronized用法

        从语法上讲,Synchronized可以把任何一个非null对象作为"锁",在HotSpot JVM实现中,锁有个专门 的名字:对象监视器(Object Monitor)。可以修饰普通方法,静态方法,

(1)修饰普通方法(锁对象是this,使用同一个类的两个对象去访问不是同一把锁)

public synchronized void run()  {
    System.out.println(1);
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(2);
}

使用同一对象访问,结果是同步的

Demo demo = new Demo();
new Thread(() -> demo.run()).start();
new Thread(() -> demo.run()).start();

//结果如下
1
2
1
2

使用不同对象访问,结果是异步的

Demo demo = new Demo();
new Thread(() -> demo.run()).start();
Demo demo2 = new Demo();
new Thread(() -> demo2.run()).start();

//结果如下
1
1
2
2

(2)修饰静态方法(锁对象是当前类的class字节码文件,使用同一个类的两个对象去访问是同一把锁)

public static synchronized void run()  {
   System.out.println(1);
   try {
       Thread.sleep(1000);
   } catch (InterruptedException e) {
       e.printStackTrace();
   }
   System.out.println(2);
}

使用不同的对象访问,结果是同步的

Demo demo = new Demo();
new Thread(() -> demo.run()).start();
Demo demo2 = new Demo();
new Thread(() -> demo2.run()).start();

//结果如下
1
2
1
2
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr Tang

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值