java基础知识 一

一.String 

1.1string源码中,几个变量 :

  String类有final修饰,所以String类不能被继承

@Stable
private final byte[] value;  final 是不可变的;因为private是私有的,并且没有提供修改value数组的方法,所以一旦确定值是不可变的;

构造函数:

public String() {
    this.value = "".value;
    this.coder = "".coder;
}
public String(String original) {
    this.value = original.value;
    this.coder = original.coder;
    this.hash = original.hash;
}

1.2 方法:

1.2.1 equals 方法

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String aString = (String)anObject;
        if (coder() == aString.coder()) {
            return isLatin1() ? StringLatin1.equals(value, aString.value)
                              : StringUTF16.equals(value, aString.value);
        }
    }
    return false;
}
latin1码下的比较
public static boolean equals(byte[] value, byte[] other) {
    if (value.length == other.length) {
        for (int i = 0; i < value.length; i++) {
            if (value[i] != other[i]) {
                return false;
            }
        }

2.StringBuffer 继承自abstractStringBuffer

变量:

byte[] value;

不是final的可以改变,并且权限是默认的;

继承自abstractStringBuilder

append方法:

扩容的新容量为当前value的容量2倍加2,如果扩容后的容量还是比需要的最小容量小,则直接扩容为需要的最小容量,再将当前value内容复制给一个新的长度为newCapacity的字符数组,再将value指向这个扩容后的新数组。即扩容是通过开辟新数组完成的,返回的也是新创建的新数组。

先扩容,将原先的数组复制到新数组中;

 */
public AbstractStringBuilder append(String str) {
    if (str == null) {
        return appendNull();
    }
    int len = str.length();
    ensureCapacityInternal(count + len);
    putStringAt(count, str);
    count += len;
    return this;
}
 */
private void ensureCapacityInternal(int minimumCapacity) {
    // overflow-conscious code
    int oldCapacity = value.length >> coder;
    if (minimumCapacity - oldCapacity > 0) {
        value = Arrays.copyOf(value,
                newCapacity(minimumCapacity) << coder);
    }
}
private int newCapacity(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = value.length >> coder;
    int newCapacity = (oldCapacity << 1) + 2;
    if (newCapacity - minCapacity < 0) {
        newCapacity = minCapacity;
    }
    int SAFE_BOUND = MAX_ARRAY_SIZE >> coder;
    return (newCapacity <= 0 || SAFE_BOUND - newCapacity < 0)
        ? hugeCapacity(minCapacity)
        : newCapacity;
}

3.String与StringBuffer与StringBuilder三者介绍和三者的区别:

string 是不可变的,每次对string的操作其实是生成一个新的string对象;

tringBuffer与StringBuilder,是对自己本身操作的,并不产生新对象;

因为在源码中,string的值存放在value的char[] 数组中,这个value变量值final的并且是private,并且源码中并没有可以改变value数组的方法,同时stringbuffer的value数组权限是默认的且非final的;

StringBuilder是线程非安全的,stringbuffer是线程安全方法加锁的;

二.list

1.ArrayList

底层由数组实现,

最主要的方法:

add(): 初始 默认是10; 先查询是否进行扩容,如果扩容Array.copyof()方法,扩到1.5倍,;

remove() 其实底层用的是system.arraycopy; 

 public int size() {
        return size;
    }

返回的size是变量,是list的长度,而不是数组的长度;

2. ArrayList 的安全问题 

ArrayList 采用fail-fast 机制,是java集合的一中失败检测机制,在迭代过程中报错;

具体场景是:

    List<String> list = new ArrayList<>();
           for (int i = 0 ; i < 10 ; i++ ) {
                list.add(i + "");
           }
           Iterator<String> iterator = list.iterator();
           int i = 0 ;
           while(iterator.hasNext()) {
                if (i == 3) {
                     list.remove(3);  //改成iterator.remove(3)方法代替就可以解决
                }
                System.out.println(iterator.next());
                i ++;
           }

 //这个时候就会报错,因为当ArraList在添加或者删除操作时,会有个专门的记录修改次数的变量+1,而在执行iterator.next()操作时候,会 进行一个判读 expectedModCount和门的记录修改次数的变量的判断,这个时候就会报错;

解决方法 :     list.remove(3);  //改成iterator.remove(3)方法代替就可以解决;

2.linkedList

双向链表

变量:

 Node : first last;

方法 :

get(int index) //获取指定位置的节点:

会把index 与size/2比,小于,从头开始遍历查找,大于从尾部查找开始;

也同时实现了普通队列和栈的功能:

//出队(从前端),如果不存在会返回null,存在的话会返回值并移除这个元素(节点)
    public E poll() {
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }
    //出队(从前端),如果不存在会抛出异常而不是返回null,存在的话会返回值并移除这个元素(节点)
    public E remove() {
        return removeFirst();
    }
 构造函数:

其中有个构造函数

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

addAll方法把c转换成数组,然后遍历数组一个个添加,

三. map

1.hashMap

hashMap 是数组+链表的形式,根据key的hashcode 来确定放到哪个桶,当两个key的hashcode相同时候,需要放在一个桶中,这个时候就用链表,如果链表长度超过(默认),就转成红黑树;

如果数组越长 那么hash冲突的概率就越小,而查找效率越快,但空间的占用就越多,所以这个时候,出现了扩容机制;

扩容机制是 根据默认负载因子0.75,threshold=负载因子 * length; //判断是否需要扩容,每次扩容*2;HashMap是先插入数据再进行扩容的,但是如果是刚刚初始化容器的时候是先扩容再插入数据。

扩容之后的hash计算   :只需要看看原来的hash值新增的那个bit是1还是0就好了,是0的话索引没变,是1的话索引变成“原索引+oldCap”,

put()方法

检测table[]是否为空,

检测table[i]是否为插入过,选择直接插入,或者遍历

然后看是否链表超过8,编程红黑树;

如果key相同覆盖,如果不是插入,

2.hashtable

hashtable 相对来说是线程安全的,加了锁; 初始容量11 每次扩容2n+1;ashMap的迭代器(Iterator)是fail-fast迭代器;

Map m = Collections.synchronizeMap(hashMap);(让hashmap变线程安全);

3.ConcurrentHashMap

1.7中采用的是分段锁,解决了hashmap中的线程安全问题,可以多个线程同时访问不同的segement。

ConCurrentHashMap1.7中的实现是

segement[]数组,里面是enrty[]数组,而entry又是链表;

segement是充当锁,实现的是ReentrantLock锁。

put方法

先根据hash找到对应的segement,

然后尝试获取锁,如果获取锁失败就说明有竞争存在,然后就自旋尝试获取,当自旋锁获取到了一定次数,改为独占锁,阻塞,确保一定可以获取到锁。

在获取到锁之后,在通过hash找到对应的entry,如果存在hash,则进行比较key是否相等,如果相等则进行覆盖,如果不相等则插入。如果不存在则插入一个新的entry,并且判断是否需要扩容。

get()方法,不加锁的

直接两侧hash。

jdk1.8

取消了分段锁,直接cas+synchronize 保证线程安全。

基本单位换成了node,变量仍然是用volatile修饰。

put方法()

判断是否进行初始化

根据hash定位到Node,如果为空,则用cas进行尝试写入,写入失败则进行自旋。

如果hashcode =-1 则进行扩容操作;

如果不为空,则加synchronize锁进行写操作。

如果链表数量大于阙值则进行转换成红黑树。

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值