飞道的博客

Java填坑之List

307人阅读  评论(0)

在写代码中会经常用到Collection集合接口,最多用的就是ArrayList、HashMap等实现类,但是在一些面试题或者在看一些文献的时候,会用到Queue或者Set等,每次遇到这一块都是哪里不会查哪里,知识点是零散的,所以决定写一篇梳理一下Collection集合接口的各种知识点;思路是根据自己整理的,其中引用一些大佬的文章的话,链接这样贴出来:https://www.jianshu.com/p/63b01b6379fb
https://www.cnblogs.com/liulin1187740947/p/9164870.html
http://www.yiidian.com/java-collection/java-linkedlist.html

List

如图所见,List是一个接口,在它下面有ArrayList,LinkedList,Vector三个实现类,List有什么特点呢?

  • 它是一个元素存取有序的集合
  • 它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素
  • 集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。

我们都知道接口中定义的方法在实现类中是一定要实现的,List承诺可以将元素维护在特定的序列中。List接口在Collection的基础上添加了大量的方法,使得可以在List的中间插入和添加元素:

方法 描述
void add(int index, E element) 用于将指定的元素插入List中的指定位置。
boolean add(E e) 用于将指定的元素追加到List的末尾。
boolean addAll(Collection<? extends E> c) 用于将指定集合中的所有元素附加到List的末尾。
boolean addAll(int index, Collection<? extends E> c) 从List的指定位置开始,它用于附加指定集合中的所有元素
void clear() 用于从此List中删除所有元素。
boolean equals(Object o) 用于将指定的对象与List的元素进行比较。
int hashcode() 用于返回List的哈希码值。
E get(int index) 用于从List的特定位置获取元素。
boolean isEmpty() 如果List为空,则返回true,否则返回false。
int lastIndexOf(Object o) 用于返回指定元素最后一次出现在此List中的索引;如果List不包含此元素,则返回-1。
Object[] toArray() 用于以正确的顺序返回包含此List中所有元素的数组。
T[] toArray(T[] a) 用于以正确的顺序返回包含此List中所有元素的数组
boolean contains(Object o) 如果List包含指定的元素,则返回true
boolean containsAll(Collection<?> c) 如果List包含所有指定的元素,则返回true
int indexOf(Object o) 用于返回指定元素首次出现在此List中的索引,如果List不包含此元素,则返回-1。
E remove(int index) 用于删除List中指定位置上存在的元素。
boolean remove(Object o) 用于删除指定元素的第一次出现。
boolean removeAll(Collection<?> c) 用于从List中删除所有元素。
void replaceAll(UnaryOperator operator) 用于将List中的所有元素替换为指定的元素。
void retainAll(Collection<?> c) 用于保留List中指定集合中存在的所有元素。
E set(int index, E element) 用于替换List中位于指定位置的指定元素。
boolean remove(Object o) 用于删除指定元素的第一次出现。
void sort(Comparator<? super E> c) 用于根据指定的比较器对List中的元素进行排序。
Spliterator spliterator() 用于在List中的元素上创建分隔符。
List subList(int fromIndex, int toIndex) 用于获取位于给定范围内的所有元素。
int size() 用于返回List中存在的元素数。

其中有两种类型的List:

  • 基本的ArrayList:它擅长于随机访问元素,但是在List的中间插入和移除元素时较慢。
  • LinkedList:它通过代价较低的在List中间进行插入和删除操作,提供了比较优化的顺序访问;LinkedList在随机访问方面相对比较慢,但是它的特性集较ArrayList更大。

其中,ArrayList底层通过数组实现,随着元素的增加而动态扩容。而LinkedList底层通过链表来实现,随着元素的增加不断向链表的后端增加节点。

ArrayList

ArrayList是Java集合框架中使用最多的一个类,是一个数组队列,线程不安全集合,它继承于AbstractList,实现了List, RandomAccess, Cloneable, Serializable接口。

  • ArrayList实现List,得到了List集合框架基础功能;
  • ArrayList实现RandomAccess,获得了快速随机访问存储元素的功能,RandomAccess是一个标记接口,没有任何方法;
  • ArrayList实现Cloneable,得到了clone()方法,可以实现克隆功能;
  • ArrayList实现Serializable,表示可以被序列化,通过序列化去传输,典型的应用就是hessian协议。

ArrayList的特点

  • 容量不固定,随着容量的增加而动态扩容(阈值基本不会达到)
  • 有序集合(插入的顺序==输出的顺序)
  • 插入的元素可以为null
  • 增删改查效率更高(相对于LinkedList来说)
  • 线程不安全

思考

1. Java中已经有了Array数组,里面可以存储基本数据类型也能存储对象,那为什么我们还要使用ArrayList,它们有什么区别呢?

答:

  • ArrayList是Array的复杂版本
    可以将 ArrayList想象成一种“会自动扩增容量的Array”,ArrayList内部封装了一个Object类型的数组,从一般的意义来说,它和数组没有本质的差别,甚至于ArrayList的许多方法,如Index、IndexOf、Contains、Sort等都是在内部数组的基础上直接调用Array的对应方法。

  • 存储的数据类型
    ArrayList可以存储异构对象,而Array只能存储相同数据类型的数据;同构的对象是指类型相同的对象,若声明为int[]的数组就只能存放整形数据,string[]只能存放字符型数据,但声明为object[]的数组除外。 而ArrayList可以存放任何不同类型的数据(因为它里面存放的都是被装箱了的Object型对象,实际上ArrayList内部就是使用"object[] _items;"这样一个私有字段来封装对象的)

    class Apple {}
    class Orange {}
    //	没有使用泛型!!!
    ArrayList apples = new ArrayList();
    apples.add(new Apple());
    apples.add(new Orange());
    
  • 长度的可变
    数组具有固定的尺寸,而在一般的情况中,我们在写程序时并不知道将需要多少个对象,或者是否需要更复杂的方式来存储对象,因此数组尺寸固定会非常限制,而ArrayList的长度既可以指定(即使指定了长度,也会自动2倍扩容)也可以不指定,是变长的。

  • 存取和增删元素
    对于一般的引用类型来说,这部分的影响不是很大,但是对于值类型来说,往ArrayList里面添加和修改元素,都会引起装箱和拆箱的操作,频繁的操作可能会影响一部分效率。基于效率和类型检验,应尽可能使用Array,无法确定数组大小时才使用ArrayList,不过当你试着解决更一般化的问题时,Array的功能就可能过于受限。

2.在创建集合对象的时候,ArrayList arr = new ArrayList();与 List arr = new ArrayList();的区别

ArrayList arr = new ArrayList();
//推荐
List arr = new ArrayList();

这里涉及到Java基础多态的概念,我们都知道Java是一种面向对象语言,我们编写代码的时候也要秉承着面向对象编程的概念,List 是一个接口,定义了一些方法,但是方法没有被实现,ArrayList是一个类实现了内List这个接口,List里边定义的方法 在ArrayList里边都实现了,同时List也可以被别的类所实现例如Vector,Vector和ArrayList对List定义的方法的实现就有所区别;而且我们用实现接口的方法显得更加灵活:

import org.junit.Test;

import java.util.*;

public class Demo {

    List list;

    public List getArrayList(List list) {
        list = new ArrayList();
        return list;
    }
    
    public List getLinkedList(List list) {
        list = new LinkedList();
        return list;
    }
    
}

我们想拿ArrayList或者LinkedList直接调方法就好了,如果我们一开始就写死是ArrayList类的话,还得重新修改返回类型。但是特殊的一点是,比如我们如果要调用LinkedList具有而在List里面没有的方法(LinkedList特有方法),就不能将它向上转型成更通用的接口了,就只能老老实实用LinkedList list = new LinkedList();了。

3. 添加一组对象

我们怎么向集合里面一次性添加一组对象呢?Collection接口里面自带了一个方法:

boolean addAll(Collection<? extends E> c);

或者在Collection接口的实现类中自带的构造器:

除此之外,我们还可以用java.uitl包中的Arrays和Collections类的使用方法,给Collection中添加一组元素,Arrays.asList()方法接一个数组或者是一个逗号分割的元素列表,并将其转换为一个List对象。

	//逗号分割
	List list1 = Arrays.asList(1, 2, 3);
    Integer[] more = {6, 7, 8};
    //数组
    List list2 = Arrays.asList(more);

我们再看看怎么用Collections里面的addAll()方法:

public static <T> boolean addAll(Collection<? super T> c, T... elements) {
        boolean result = false;
        for (T element : elements)
            result |= c.add(element);
        return result;
    }

Collections的addAll()可以接受一个Collection对象,以及一个数组或是一个用逗号分割列表,将元素添加到Collection中;那用Collections的addAll()与Arrays.asList()有什么区别呢?Arrays.asList()方法的限制是它对所产生的List的类型做出了最理想的假设,而并没有注意到你会对她赋予什么样的类型,我们引用《Thinking in java》里面一个经典的例子:

class Snow{}
class Powder extends Snow{}
class Light extends Powder{}
class Heavy extends Powder{}
class Crustry extends Snow{}
class Slush extends Snow{}

public class AsListInference{
	public static void main(String[] args){
	List<Snow> snow1 = Arrays.asList(new Crustry(),new Slush(),new Powder());
	//Wont compile 因为编译器会以为是转为Powder类型:
	//List<Snow> snow2 = Arrays.asList(new Light(),new Heavy());
	//因为显示指定了是转为Snow类型
	List<Snow> snow3 = new Array<Snow>();
	Collections.addAll(snow3,new Light(),new Heavy());
	//我们也可以插入一条线索 使它识别到我们是想转为Snow类型的
	List<Snow> snow4 = Arrays.<Snow>asList(new Light(),new Heavy());
	
}

LinkedList


LinkedList是一个双向链表,每一个节点都拥有指向前后节点的引用。相比于ArrayList来说,LinkedList的随机访问效率更低。

它继承AbstractSequentialList,实现了List, Deque, Cloneable, Serializable接口。

  • LinkedList实现List,得到了List集合框架基础功能;
  • LinkedList实现Deque,Deque 是一个双向队列,也就是既可以先入先出,又可以先入后出,说简单些就是既可以在头部添加元素,也可以在尾部添加元素;
  • LinkedList实现Cloneable,得到了clone()方法,可以实现克隆功能;
  • LinkedList实现Serializable,表示可以被序列化,通过序列化去传输,典型的应用就是hessian协议。

LinkedList的特点是:

  • Java LinkedList类可以存储重复的元素。
  • Java LinkedList类是有序的。
  • Java LinkedList类是非同步的(线程不安全的)。
  • 在Java LinkedList类中,由于不需要进行任何转换,因此处理速度很快。
  • Java LinkedList类可以用作List,Stack或Queue。

LinkedList还添加了可以使其用作队列双端队列的方法。


思考

  1. 为什么Java Vector(和Stack)类被认为已过时或已弃用?

    Vector是1.0的一部分 - 原始实现有两个缺点:
    1.命名:vectors实际上只是可以作为数组访问的列表,因此它应该被称为ArrayList(它是Vector的Java 1.2 Collections替代品)。
    2.并发:所有get(),set()方法都是synchronized,所以你无法对同步进行细粒度控制。
    ArrayList和Vector之间没有太大区别,但你应该使用ArrayList。


转载:https://blog.csdn.net/weixin_37138915/article/details/105884866
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场