Java | List集合
List接口
java.util.List
接口继承Collection
接口,是集合的一个重要的分支,我们平常所使用最多的就是这个集合,我们用实现类所实现这个接口的时候,习惯把这个接口的对象叫做List集合
List接口
List集合的特点:
1、它是一个元素存取有序的集合。
2、它是一个有索引的集合,可以通过索引精确的操作集合中的元素。
3、集合中允许有重复的元素。
List接口中自带的常用的方法:
public void add(int index, E element)
将指定的元素,添加到该集合中的指定位置上。
public E get(int index)
返回集合中指定位置的元素。
public E remove(int index)
移除列表中指定位置的元素,返回被移除的元素。
public E set(int index, E emelent)
用指定元素替换集合中指定位置的元素,返回是更新前的元素。
List接口中的常用方法,可以在它的所有的实现类中使用
List接口中常用的方法的使用
准备数据:
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
System.out.println(list); //[a, b, c, d, e]
public void add(int index, E element)
将指定的元素,添加到该集合中的指定位置
//在bc之间加上一个*
list.add(2, "*");
System.out.println(list); //[a, b, *, c, d, e]
public E get(int index)
返回集合中指定位置的元素。
//取出下标为2的元素
String str = list.get(2);
//String n = list.get(5); java.lang.IndexOutOfBoundsException 数组下标越界异常
System.out.println(str); //c java中数组下标是从0开始的
public E remove(int index)
移除列表中指定位置的元素,返回被移除的元素。
//移除下标为3的元素
String remove = list.remove(3);
System.out.println(remove); //d
System.out.println(list); //[a, b, c, e]
public E set(int index, E emelent)
用指定元素替换集合中指定位置的元素,返回是更
//移除第一个元素,返回值是被移除的元素
String set = list.set(0, "*");
System.out.println(set); //a
System.out.println(list); //[*, b, c, d, e]
ArrayList集合
java.util.ArrayList
集合,在之前就使用过,但是一直没有介绍过,一直到现在再介绍,因为这个集合是以后我的在工作中用的最多的集合,并且它的大部分方法,在之前使用的时候已经用过了,所以在这里就不在多说他的使用方法了。
在这里主要说一下,他的数据结构,可以从名子中就可以看出来,他是数组实现的,是List接口的一个重要的实现类。
底层的实现:
List里面的数组:
transient Object[] elementData; // non-private to simplify nested class access
这个数组就是List集合里面的底层操作的数组。
List里面的长义:
private int size;
我们通常获得长度就是获得的这个就量的数据。
Arraylist里面的add方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!(增量)
elementData[size++] = e;
return true;
}
ensureCapacityInternal(size + 1)
这个方法的作用,就是查询集合中底层的数组的长度够不够,如果够,就继承执行下面的代码,如果不够,就得数组扩容,数组扩容后面再说。
elementData[size++] = e;
这个就是把长度加一之后,存到数组的这个位置。
return true;
如果没有发生异常,都是返回的true
ensureCapacityInternal(size + 1)
方法:
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
这个方法是完成判断的主要的方法,它调用了calculateCapacity()
方法来确定数组的长度。
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
DEFAULTCAPACITY_EMPTY_ELEMENTDATA
默认容器
DEFAULT_CAPACITY
默认容量为10
当数组中没有元素的时候,会取默认长度和传进来的长度之中的最大值,致于为什么会这样,是因为这个方法不光add会调用,addAll也会调用这个方法,如果用的是Arraylist的默认方法创建的数组,那么说不定数组的长度会不够的,所以要判断一下。然后返回新数组的长度。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
modCount
这个变量的作用就是记录一下集合中元素操作的次数,每一次增加或删除的时候,都会加一。
当新数组长度,大于旧的数组长度的时候,就会通过grow()
来拷贝数组。
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
这个方法就是拷贝数组的,这个方法就算到调用的是Arrays.copyOf(elementData, newCapacity)
,而这个方法的最底层是调用的System.arraycopy()
,就是把创建一个新的数组,然后把原来数组中所有的内容都拷贝到新数组中去!
删除的方法,也是和增加的方法大同小异,所以就不说了。
所以说,ArrayList集合查询快,增删慢,就是因为他的底层都是数组操作,数组操作增加和删除的时候,都会创建新的数组,所以速度就会慢很多。
在我们使用List集合的时候,如果初始化数据过大,那么可以在new
的时候,直接把长度传过去,这样可以提升一点效率。
LinkedList集合
java.util.LinkedList
集合数据存储的结构是链表结构,方便元素的添加,删除。
链表结构,因为是链表结构,所以数据的存储不是在一个边续的内存里面,所以链表结构添加和删除元素的时候,是效率是非常高的。
简单说一下,LinkedList中的链表实现:
在集合中有一个内部娄:
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;
}
}
链表其实就是这个Node
表,所有的数组都是存到Node
里面,item
代表的就是当前元素,next
就代表着下一个元素的地址,prev
就代表着前一个元素的地址。
所以说,LinkedList集合添加和删除容易,就是因为在添加或删除元素的时候,只用new
一个对象,然后修一个或两个对象里面的参数就行了。
具体的实现就不说了,但是很简单的。有兴趣的可以去网上找一下。
LinkedList的简单使用
因为是链表结构,所以第一个数据和最后一个数据是最特殊的,所以集合中有在量操作第一个和最后一个数据的方法,所以在这里,我就不说了,这些方法都可以查看API文档,我在这里说两个特殊一点的方法:
public void push(E e)
在列表的头部插入一个元素。
public E pop();
移除列表里面的第一个元素。
准备数据:
LinkedList<String> link = new LinkedList<>();
link.add("a");
link.add("b");
link.add("c");
link.add("d");
link.add("e");
System.out.println(link); //[a, b, c, d, e]
public void push(E e)
在列表的头部插入一个元素。
link.push("*");
System.out.println(link); //[*, a, b, c, d, e]
public E pop();
移除列表里面的第一个元素。
String pop = link.pop();
System.out.println(pop); //a
System.out.println(link); //[b, c, d, e]
上面所说的两个LIst集合的实现类,都是在日常开发中经常使用到的,但是这两个实现类都不是同步,这两个实现类都是线程不安全的,所以涉及多线程的时候,要小心使用。
关注公众号,随时获取最新资讯
细节决定成败!
个人愚见,如有不对,恳请斧正!