Java ArrayList与LinkedList基础与底层部分源码

ArrayList数据结构

元素的数据类型相同,所占内存大小一样,由于是顺序存储的,可以直接计算每个元素所在的内存地址,根据索引查找时速度快。

  • 在内存中分配连续的空间,实现了长度可变的数组
  • 优点:遍历元素和随机访问元素的效率比较高
  • 缺点:添加和删除需大量移动元素效率低(需要覆盖前移),按照内容查询效率低在这里插入图片描述

ArrayList特点:不唯一、 有顺序(索引的顺序)。
集合中只能放对象,不能放基本数据类型,jdk1.5之后会自动装箱。

ArrayList常用方法

添加

index为可选参数,list下标

list. add([index,]object) ;
list. addAll([index,]list2) ;

获取元素个数

 list.size()

获取第i个元素

list.get(i)

list数据交给迭代器

Iterator it=list.iterator()

实例

import java.util.ArrayList;
import java.util.Iterator;

public class TestArrayList {
    public static void main(String[] args) {
//             创建ArrayList
        ArrayList<Integer> list=new ArrayList<Integer>();
//        向ArrayList添加数据
        list.add(1);//add参数需要是对象,基本数据类型时会自动装箱
        list.add(1,2);
        ArrayList<Integer> list1=new ArrayList<Integer>();
        list1.add(3);
        list1.add(4);
        list.addAll(0,list1);//将一个ArrayList添加另一个ArrayList的指定index位置
//        遍历ArrayList
//        增强型foreach循环,不会遍历下标
        for (int elem:
             list) {
//            int elem1=(int)elem;
            System.out.println(elem);
        }
//        使用Iterator迭代器遍历
        Iterator<Integer> iterator=list.iterator();
        while (iterator.hasNext()){//当前遍历的集合是否还有元素
            System.out.println(iterator.next());//next返回iterator指针当前元素的值
        }
    }
}

删除

参数传入为整数,默认按照索引所以删除,不按照值删除,如果要删除值为int的需要包装为Integer,非整数则直接按值删除

list.remove(index)
list.remove(object)
list.remove(new Integer(int))

删除一个集合中所包含的元素

list.removeAll(collection)

清除整个list

list.clear()

修改

list.set(index,value)

其他方法

list.isEmpty()
list.toArray()
list.retainAll(collection)//仅保留此列表中指定集合中包含的元素

是否包含某个元素

list.contains(value)

查找元素的第一个或最后一个index

list.indexOf(value)
list.lastIndexOf(value)

ArrayList源码

底层能动态改变长度的数组

构造方法

无参构造底层数组直接指向空数组,elementData 为引用,{}为实际地址内容。

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
private static final Object[] EMPTY_ELEMENTDATA = {};

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

带指定内存大小参数的构造,传入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);
    }
}

add方法

add方法会先调用扩容方法,将当前的size+1作为最小内存minCapacity(如果elementData 为空,即首次使用list,会为elementData 自动分配10个空间大小作为minCapacity),当minCapacity大于底层数组elementData 的长度(一般是自动分配的10个空间),就要考虑将elementData 扩容。扩容规则:在elementData .length()增加50%的容量,如果增加50%后的容量仍无法满足需求minCapacity,就用minCapacity的大小,数组扩容,创建新数组,将原数组的元素复制到新数组,elementData 引用新数组地址。(size+1能用到这么大的内存?addAll重用会有这么大)

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 扩容方法
    elementData[size++] = e;//添加到list中
    return true;
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
	//elementData 为空,即首次使用list
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);//分配10个内存空间作为最小内存,DEFAULT_CAPACITY=10
    }
    return minCapacity;
}
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // minCapacity大于底层数组elementData 的长度,扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
//扩容规则
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    //向左移一位增加50%的容量
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    //如果增加50%后的容量仍无法满足需求minCapacity,就用minCapacity的大小
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 数组扩容,创建新数组,将原数组的元素复制到新数组,elementData 引用新数组地址。
    elementData = Arrays.copyOf(elementData, newCapacity);
}

toString

通过Iterator接口实现遍历。重用父类实现[elment1,elment2…]

public String toString() {
    Iterator<E> it = iterator();
    if (! it.hasNext())
        return "[]";

    StringBuilder sb = new StringBuilder();
    sb.append('[');
    for (;;) {
        E e = it.next();
        sb.append(e == this ? "(this Collection)" : e);
        if (! it.hasNext())
            return sb.append(']').toString();
        sb.append(',').append(' ');
    }
}

泛型

使用场景1繁琐

取出元素时需要强制转换,否则是0bject类型。使用泛型将返回的为指定类型,而不是默认的Object

ArrayList<Integer> list=new ArrayList<Integer>();
//int elem = (int)list.get(i);
list.get(i)       //返回的为Integer类型,而不是默认的Object

使用场景2不安全

添加元素的时候可以加入不同的数据类型,带来安全问题。使用泛型Integer就不能添加String。

list. add("abc");

LinkedList

与ArrayList的区别

增加了操作首尾节点的方法

List<> list=new LinkedList<>();
list.addFirst();
list.addLast();
list.removeFirst();
list.removeLast();
list.getFirst();
list.getLast();

添加、删除操作多使用LinkedList
随机访问多用ArrayList
E为泛型,在创建对象时会确定泛型E的数据类型,并将数据类型作为参数传递,赋值给泛型E

add方法

添加流程

  1. 首先l保存last最后一个节点
  2. 创建新节点,pre指向最后一个节点l,e为值,由于新节点会成为最后一个节点next为null
  3. 将新节点变成最后一个节点
  4. l如果为空,则新节点既是第一个节点又是最后一个节点,如果l不为空,l的next指向新节点。
    first为头结点的next指向,last为最后一个节点。
void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

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;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 答:hashmap、hashtable、arraylistlinkedlist都是Java源码中常用的数据结构。hashmap是一种散列表,它使用键值对来存储数据;hashtable是一种哈希表,它使用散列函数来存储数据;arraylist是一种动态数组,它可以动态增加和删除数据;linkedlist是一种链表,它使用节点来存储数据。 ### 回答2: 1. HashMap(哈希映射):是Java中最常用的集合类之一。它是基于哈希表实现的,用于存储键值对。HashMap可以根据键快速地查找对应的值,内部使用哈希函数来将键计算为哈希码,并将其存储在数组中。当需要根据键查找值时,HashMap会将键的哈希码通过哈希函数再次计算得到数组索引,然后在该位置的链表中查找对应的值。 2. Hashtable(哈希表):也是一种存储键值对的集合类,在Java中已被HashMap取代,但仍然保留供向后兼容使用。Hashtable与HashMap的原理相似,都是基于哈希表实现的。与HashMap不同的是,Hashtable是线程安全的,即在多线程环境下,不需要额外的同步操作就可以安全地使用Hashtable。然而,由于线程安全的机制增加了额外开销,推荐在单线程环境下使用HashMap。 3. ArrayList(数组列表):是Java中最常用的动态数组实现。底层使用数组来存储元素,具有自动扩容的功能。ArrayList可以按索引访问元素,并支持快速插入和删除元素,通过扩容和拷贝数组的方式实现。ArrayList允许存储任意类型的对象,可以动态地增加和减少集合的大小。 4. LinkedList(链表):也是Java中的一种集合类,LinkedList底层实现了双向链表的数据结构。相比于ArrayListLinkedList在插入和删除元素时效率更高,因为它不需要像ArrayList一样进行数组的扩容和拷贝操作。LinkedList可以在链表的任意位置进行插入和删除操作,但访问指定位置的元素需要遍历整个链表,效率较低。 总结:以上四种集合类都是Java中用于存储和操作数据的类库。HashMap和Hashtable都是基于哈希表实现的键值对集合,其中Hashtable是线程安全的,HashMap性能更好。ArrayList是基于数组实现的动态数组,支持快速访问和增删操作。LinkedList是基于链表实现的集合,插入和删除元素效率较高,但访问元素的效率较低。 ### 回答3: HashMap是Java中的一种数据结构,它使用了哈希表来存储键值对。其中的键是唯一的,通过哈希函数计算得到对应的存储位置,提高了查找和插入的效率。HashMap允许键和值都为空,但是不保证存储顺序。 Hashtable也是一种键值对的映射结构,它与HashMap类似,但是Hashtable是线程安全的,即在多线程环境下,多个线程同时访问Hashtable的读写操作,不会导致数据不一致的情况发生。Hashtable的键和值都不允许为空。 ArrayList是一种动态数组,它使用数组来存储一组元素。与普通的数组相比,ArrayList的大小可以动态调整,可以根据实际需求进行增删操作。ArrayList适用于频繁访问、查询和插入元素,但不适用于频繁删除元素的场景。 LinkedList是一种双向链表,它通过节点之间的引用关系连接了一组元素。与ArrayList相比,LinkedList的插入和删除操作效率更高,但是随机访问元素的效率较低。LinkedList适用于频繁插入和删除元素的场景。 综上所述,HashMap和Hashtable都是用于存储键值对的数据结构,其中HashMap在非多线程环境下性能更好,Hashtable在多线程环境下安全可靠;ArrayList是动态数组,适用于频繁访问和查询元素;LinkedList是双向链表,适用于频繁插入和删除元素。根据实际需求选择合适的数据结构可以提高程序的效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值