链表LinkedList和HashMap源码分析

单链表HashMap

  1. 新增结点
    p结点后面增加一个s结点,实现代码如下:
s.next = p.next;
p.next = s;

注意操作步骤,首先是将插入结点snext指针s.next指向p结点的下一个结点p.next,再将p结点的next指针p.next指向s结点;如果按照我们正常的思维方式,是将p结点的next指针p.next指向s结点,再将s结点的next指针s.next指向p结点的下一个结点p.next
(最后这一句话中的p.nexts结点,结果就是s结点的next指针s.next指向s自己,显然是不正确的)

  1. 删除结点
    删除p结点后面那个结点,实现代码如下:
p.next = p.next.next;

p结点的next指针p.next指向p结点的下下个结点p.next.next

  1. 更改结点的值
p.data = new Object();

p结点的数据域data重新赋值

  1. 查询结点
while(p.next != l){
    p = p.next;
}

从头结点开始遍历,遍历每一个结点失败,则结点后移,直至遍历出我们的l结点为止.


双链表LinkedList

  1. 新增结点
    p结点后面增加一个s结点,实现代码如下:
s.next = p.next;
p.next.prev = s;
s.prev = p;
p.next = s;

这里的操作步骤同单链表的新增结点,只是要注意每个结点增加了一个prev指针,用于指向前一个结点;所以在处理s结点与p结点下一个结点关系的同时也要处理prev指针,处理p结点与s结点的关系的同时也要处理prev指针

LinkedList在中间新增一个结点的源码:

void linkBefore(E e, Node<E> succ) {
    //得到插入位置的前一个结点
    final Node<E> pred = succ.prev;
    //构建待插入结点的对象,里面包含了插入结点prev指针指向的结点,和next指针指向的结点
    final Node<E> newNode = new Node<>(pred, e, 
    succ);
    //将后面一个结点的prev指针指向待插入的结点
    succ.prev = newNode;
    //插入到表头,则插入的结点就是头结点;不是表头,则将前一个结点的next指针指向待插入的结点
    if (pred == null)
        first = newNode;
    else
        pred.next = newNode;
    //链表长度加1
    size++;
    //更改次数加1
    modCount++;
}
  1. 删除结点
    删除p结点,实现代码如下:
p.next.prev = p.prev;
p.prev.next = p.next;

这两行代码执行顺序不影响

LinkedList在删除一个结点的源码,首先调用node(int index)方法从链表的头部/尾部开始遍历,找到我们要删除的结点,然后执行删除操作:

E unlink(Node<E> x) {
    //找到删除结点中的数据域,最后会将其作为方法的返回值
    final E element = x.item;
    //找到删除结点的后一个结点
    final Node<E> next = x.next;
    //找到删除结点的前一个结点
    final Node<E> prev = x.prev;
    //判断是不是删除的链表中第一个结点
    if (prev == null) {
        //是则将当前结点赋值给fisrt
        first = next;
    } else {
        //不是则将前一个结点的next指针指向删除结点下一个结点
        prev.next = next;
        //将删除结点中指向前一个结点的指针置空
        x.prev = null;
    }
    //判断是不是删除的链表中最后一个结点
    if (next == null) {
        //是则将当前结点赋值给last
        last = prev;
    } else {
        //不是则将下一个结点的prev指针指向删除结点的上一个结点
        next.prev = prev;
        //将删除结点中指向后一个结点的指针置空
        x.next = null;
    }
    //结点的指针域数据置空
    x.item = null;
    //链表长度减1
    size--;
    //操作次数加1
    modCount++;
    //返回删除结点的指针域赋值出来的对象;(上面都将x.item = null,那么element是不是也为空了,难道是因为element前面的final修饰符起到了作用???测试了element非空,应该是element是重新创建的一个对象吧)
    return element;
}

补充:ArrayListLinkedListVectorStackList的4个实现类

面试问题
  1. ArrayList和LinkedList的区别?
  • ArrayList存储空间是连续的,而LinkedList存储空间是不连续的;
  • ArrayList允许随机的访问,尾部插入和删除方便,插入和删除效率低,长度固定;而LinkedList允许随机的进行增删改,插入和删除效率高,长度可以随意的修改;
  1. 接口之间可以继承吗?接口和抽象类的区别?
  • 可以的,比如List接口就继承自Collection接口,而Collection接口继承自Iterable接口;
  • 接口是特殊的抽象类,接口中所有的方法都是抽象的(public abstract隐式声明),都需要重写,并且接口成员变量必须用public static final修饰,接口不能有静态代码块和静态方法.

面试题:HashMap和HashTable区别,HashMap和HashSet的区别?

  1. HashMap和HashTable区别
  • HashTable几乎等价于HashMap只不过HashTable中的方法是synchronized修饰的,所以HashTable是线程安全的,而HashMap不是线程安全的,从效率上比较HashMap更快.
  • HashMap中存储数据时keyvalue都可以为null,而HashTable不能存储keyvaluenull的值.
  1. 实现HashMap线程安全的两种方式
  • 使用ConcurrentHashMap;
  • 使用Collections.synchronizeMap(hashMap)得到的HashMap对象是线程安全的;
  1. HashMap和HashSet区别
  • HashMap存储keyvalue键值对,而HashSet存储对象.
  • HashSet中不能存储重复的对象.
  • 扩展:HashSet继承自HashMap,所以底层还是用到了map存储;但是HashSet存储的集合元素是无序的哦.

代码举例:

//比较HashMap、HashTable和HashSet中存储是否可以为null,是否可以重复
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put(null, null);
hashMap.put(null, null);
hashMap.put("name", "汤坤");
hashMap.put("name", "汤坤");
hashMap.put("name", "罗丹");
Hashtable<String, String> hashtable = new Hashtable<>();
hashtable.put("name", "王强");
hashtable.put("name", "檀庭兵");
hashtable.put("name", "朱海建");
hashtable.put("name", "汤坤");
HashSet<String> hashSet = new HashSet<>();
hashSet.add("王强");
hashSet.add("汤坤");
hashSet.add("罗丹");
hashSet.add("汤坤");
hashSet.add(null);
hashSet.add("檀庭兵");
System.out.println("hashMap是否可以存储重复,key和value都为null的值:" + hashMap.toString() + "\n" +
        "hashtable是否可以存储重复,key和value都为null的值:" + hashtable.toString() + "\n" +
        "hashSet是否可以存储重复,对象为null的值:" + hashSet.toString()+ "\n");
        
打印结果:
hashMap是否可以存储重复,key和value都为null的值:{null=null, name=罗丹}
hashtable是否可以存储重复,key和value都为null的值:{name=汤坤}
hashSet是否可以存储重复,对象为null的值:[null, 王强, 檀庭兵, 汤坤, 罗丹]

扩展知识:Java中>>和>>>的区别?

  • >>是是带符号右移,转换成2进制右移时候,正数左边补0,负数左边补1
  • >>>是无符号右移,转换成2进制右移时,左边补0
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值