List集合(list集合排序)

  本篇文章为你整理了List集合(list集合排序)的详细内容,包含有list集合遍历 list集合排序 list集合的特点 list集合常用方法 List集合,希望能帮助你了解 List集合。

  一、什么是集合?

   顾名思义集合就相当于一个容器,容器就可以存储,只不过在java中存储的是对象,而对象本身是在堆内存中的,所以集合中存放的是一个个对象的引用。

  二、集合和数组的区别?

   问:我们都知道数组也可以存储元素,为什么还需要集合?

   答:首先数组是一个线性的序列(线性序列指线性结构中所有节点按其关系可以排成一个序列,例1、2、3........100),所以可以快速的访问其中的元素,而数组被创建的时候,容量是不变的。

   那么集合具体和数组有哪些区别?

   1、创建数组必须声明它容纳元素的类型,而集合不需要声明

  

 1 package collection;

 

   3 import java.util.ArrayList;

   5 /**

   6 * 创建数组和创建集合

   7 */

   8 public class Demo1 {

  10 public static void main(String[] args) {

  11 //第一种方式,数组长度为6

  12 int[] arr1 = new int[6];

  14 //第二种方式,数组长度为5

  15 int[] arr2 = {2,3,4,5,6};

  17 //第三种方式,数组长度为6

  18 int[] arr3 = new int[]{1,2,3,4,5,6};

  20 //创建集合

  21 ArrayList list = new ArrayList();

  25 }

 

   2、由上代码可以看出,一个数组的实例具有固定的大小,一旦创建就无法改变容量了,而集合可以动态的扩展容量,可以根据需要动态改变大小,非常灵活,能够满足我们更多的需求。

   集合动态扩展容量代码示例:

  

 1 package collection;

 

   3 import java.util.ArrayList;

   5 /**

   6 * 创建数组和创建集合

   7 */

   8 public class Demo1 {

  10 public static void main(String[] args) {

  11 //创建集合

  12 ArrayList list = new ArrayList();

  13 list.add("张三");

  14 System.out.println(list.size());

  15 list.add("李四");

  16 System.out.println(list.size());

  20 }

 

  代码输出结果:

  由此证明,集合是可以随着添加对象的多少,大小也随之改变。

   3、当我们创建一个数组,那么这个数组只能存放自定义的类型,但是集合存放的类型可以是多种(不加泛型时添加的类型是Object)

   集合代码示例:

  

 1 package collection;

 

   3 import java.util.ArrayList;

   5 public class Demo1 {

   7 public static void main(String[] args) {

   9 ArrayList list = new ArrayList();

  10 list.add("张三");

  11 list.add(1);

  12 System.out.println(list);

  16 }

 

   代码输出结果:

   数组代码示例:

  由此得出,当我们声明了数组是什么类型,那么就只能存放什么类型的元素。

   4、集合以类的形式存在,具有封装、继承、多态、等类的特性,通过简单的方法和属性调用即可实现各种复杂操作。

  三、集合的体系结构

   下图所示:

  

   1、集合Collection

   问:什么是Collection集合?

   答:Collection是集合的顶级接口,它没有具体的实现类,具有两个子接口List和Set

   2、Collection具有两个子接口,那么这两个子接口具体的区别是什么?

   首先list和set都继承自Collection这个顶级接口

   list特点是元素有放入顺序,可以重复

   set特点是元素无放入顺序,不可重复

   list支持for循环,可以通过下标来遍历,也可以用迭代器来遍历

   set只能用迭代器,因为无序,故无法使用下标

   3、ArrayList原理

   特点:查询快,增删慢,线程非安全

   原因:底层是数组,数组里的元素都有相对应的下标,根据下标去查询效率高

   原因:底层是数组,数组中的元素进行增加或者删除的时候,就需要对数组进行扩容或缩容的操作,又因数组创建出来时容量大小是固定的,要进行扩容或缩容的操作那么就要重新创建一个新

   的数组,然后把旧数组中的元素拷贝到新数组中,若原数组中的数据量非常大的时候那么效率就非常低下。

   原因:ArrayList默认数组大小为10,假设现在已经添加进去了9个元素,即size=9,现在有两个线程,线程A和线程B要往这个数组中添加元素,

   当线程A开始进入了add方法,它获取到的size值为9,在进行容量判断的时候,发现需求大小为10,而elementDate的大小也为10,可以容纳,于是不再扩容,

   同时,线程B也进入到了add方法,获取到的size值和线程A一样,判断容量的时候,发现也可以容纳,也无需扩容,

   这时线程A开始了设置值的操作,此时size的值变为了10,线程B也开始了设置值的操作,但是elementDate并没有进行扩容,下标最大为9,这时就会报出一个数组越界的异常。

   线程不安全代码实现:

  

 1 package collection;

 

   3 import java.util.ArrayList;

   5 public class Demo3 {

   6 public static void main(String[] args) {

   7 ArrayList Integer list = new ArrayList Integer ();

   8 //线程A将0-1000添加到list

   9 new Thread(new Runnable() {

  10 @Override

  11 public void run() {

  12 for (int i = 0; i 1000 ; i++) {

  13 list.add(i);

  14 try {

  15 Thread.sleep(1);

  16 } catch (InterruptedException e) {

  17 e.printStackTrace();

  21 }).start();

  22 //线程B将1000-2000添加到列表

  23 new Thread(new Runnable() {

  24 @Override

  25 public void run() {

  26 for (int i = 1000; i 2000 ; i++) {

  27 list.add(i);

  28 try {

  29 Thread.sleep(1);

  30 } catch (InterruptedException e) {

  31 e.printStackTrace();

  35 }).start();

  37 // 打印所有结果

  38 for (int i = 0; i list.size(); i++) {

  39 System.out.println("第" + (i + 1) + "个元素为:" + list.get(i));

  42 }

 

   结论:

   1、ArrayList中维护了一个Object类型的数组elementDate,类型是Object类型

   2、当创建ArrayList对象时,如果使用的是无参构造器,则初始容量为0,第一次添加,则扩容elementDate为10,如需再次扩容,则扩容elementDate为1.5倍

   3、如果使用有参构造器,则初始容量为指定大小,如果需要扩容,则直接扩容elementDate为1.5倍

   4、LinkedList原理

   底层是一个带头/尾指针的双向链表,可以快速的对头/尾节点进行操作,双向链表,头节点均指first,尾节点均指last

   特点:1、查询慢,增删快、非线程安全的

   linkedList本质是一个双向链表,由一个个的node对象组成

   原理:

   linkedlist由一个个node节点组成,每一个node持有前后节点的引用,也叫做指针,Node中有三个属性:元素对象、前一个节点指针、后一个节点指针、构造函数也由这三个属性去组成,

   在linkedlist的添加方法中,会创建一个Node对象,持有前后节点的信息,添加到最后节点之后。

   LinkedList添加实现:

  

 1 package collection;

 

   3 public class Demo4 {

   4 private static class Node E {

   5 E item;//当前元素对象

   6 Node E next;//下一个

   7 Node E prev;//前一个

   9 Node(Node E prev, E element, Node E next) {

  10 this.item = element;

  11 this.next = next;

  12 this.prev = prev;

  15 protected transient int modCount = 0;

  16 transient int size = 0;

  17 transient Node E first;

  18 transient Node E last;

  20 public boolean add(E e) {

  21 linkLast(e);

  22 return true;

  24 void linkLast(E e) {

  25 final Node E l = last;//把最后一个节点的last作为当前节点

  26 final Node E newNode = new Node (l, e, null);

  27 last = newNode;//把最后一个节点设置为last

  28 if (l == null)//如果最后一个节点为空,添加的这个节点即是最后一个节点也是第一个节点

  29 first = newNode;

  30 else

  31 l.next = newNode;

  32 size++;

  33 modCount++;

  37 }

 

   从上面源码可以看出,当linkedlist添加一个元素时,会默认往linkedlist最后一个节点添加,把最后一个节点作为当前节点,用当前节点添加参数e、null作为一个新的Node对象,将当前节点设置为last,如果最后一个节点为空,那么当前添加的这个节点即使last也是first,当前linkedlist的size+1,表示结构改变次数的对象也+1。

  

  

 1 E unlink(Node E x) {//unlink删除连接

 

   2 // assert x != null;

   3 final E element = x.item;

   4 final Node E next = x.next;

   5 final Node E prev = x.prev;

   7 if (prev == null) {//首先判断上一个节点的引用是否为null,如果为null,那么说明当前节点就是第一个节点

   8 first = next;

   9 } else {//如果不为null,说明当前节点不是第一个节点,则将当前的next赋值给prev.next,x.prew变为null

  10 prev.next = next;

  11 x.prev = null;

  14 if (next == null) {//如果下一个节点next为null,即为最后一个元素,则链表last就是x的前一个节点

  15 last = prev;

  16 } else {//如果不为null,即不为最后一个元素,则将当前节点的prew赋值给next.prew

  17 next.prev = prev;

  18 x.next = null;

  20 x.item = null;

  21 size--;

  22 modCount++;

  23 return element;

  24 }

 

   由上分析而来linkedlist的删除中,也是改变节点之间的引用关系去实现的。

   如果前一个节点为null,那么当前节点就为第一个元素,则first= x的下一个节点,

   如果前一个节点不为null,即当前节点就不是第一个元素,则将当前节点的next赋值给上一个节点的next,当前节点的前一个节点设置为null

   如果下一个节点为null,那么当前节点就是最后一个元素,则last=x的前一个节点

   如果下一个节点不为null,那么当前节点就不是最后一个元素,则将当前节点的prev赋值给下一个节点的prev

  

 1 public E get(int index) {

 

   2 checkElementIndex(index);

   3 return node(index).item;

   5 private void checkElementIndex(int index) {

   6 if (!isElementIndex(index))//如果当前传入的index不是linkedlist中的元素,那么抛出异常

   7 throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

   9 private boolean isElementIndex(int index) {

  10 return index = 0 index size;//判断下标是否在0~size之间

  12 Node E node(int index) {

  13 // assert isElementIndex(index);

  15 if (index (size 1)) {

  16 Node E x = first;

  17 for (int i = 0; i index; i++)

  18 x = x.next;

  19 return x;

  20 } else {

  21 Node E x = last;

  22 for (int i = size - 1; i index; i--)

  23 x = x.prev;

  24 return x;

  26 }

 

   查询是根据传入的index下标去判断是否为linkedList中的元素,

   调用node(index)方法,将size向右移一位,即size/2,判断size在linkedlist的前半部分还是后半部分

   如果在前半部分,即index下标小于size/2,从first节点开始遍历

   如果在后半部分,即index下标大于size/2,从last节点开始遍历

   由此得出,链表越长,遍历时间越长,对应查询时间也就越长

   5、使用场景:

   ArrayList使用在查询比较多,增加和删除比较少的情况下

   LinkedList使用在查询比较少,增加和删除比较多的情况下

   6、什么是Vector?

   Vector是矢量队列,是JDK1.0版本添加的类,

   Vector继承于AbstractList,实现了List,所以支持相关的添加、删除、更改、遍历等功能

   Vector实现了RandomAccess接口,提供了随机访问的功能

   Vector实现了Cloneable接口,它能被克隆。

   特点:线程安全,执行效率低,顺序存储,增删较慢。

   7、Vector和ArrayList的区别?

   1、Vector中的操作是线程安全的,因为Vector中所有的方法都是被synchronized关键字修饰的,ArrayList是线程不安全的

   2、Vector可以指定增长因子,如果不指定,每次扩容为原数组的2倍,如果指定,那就是原数组大小+增长因子

   3、ArrayList支持序列化,而Vecor不支持

   8、List遍历方式

  

 1 package collection;

 

   3 import java.util.ArrayList;

   4 import java.util.Iterator;

   6 public class Dem6 {

   7 public static void main(String[] args) {

   8 ArrayList Integer arrayList = new ArrayList ();

   9 for (int i = 0; i 100 ; i++) {

  10 arrayList.add(i);

  12 //第一种遍历方式,转换成数组

  13 Object[] arrays = arrayList.toArray();

  14 for (Object array: arrays) {

  15 System.out.print(array);

  17 //第二种遍历方式,使用foreach

  18 for (Integer array:arrayList) {

  19 System.out.println(array);

  21 //第三种遍历方式,使用for循环

  22 for (int i = 0; i arrayList.size() ; i++) {

  23 System.out.println(arrayList.get(i));

  25 //第四种,迭代器

  26 Iterator iterator = arrayList.iterator();

  27 while (iterator.hasNext()){//这里相当于一个标尺,开始时指向为0的地方,判断是否有下一个元素

  28 System.out.println(iterator.next());

  31 }

 

   9、CopyOnWriteArrayList简介

   简称COW,根据名字来看就是写入时复制,意思是大家共同去访问这个资源,如果有人想要去修改这个资源的时候,那么需要复制一个副本,去修改这个副本,对于其他人访问的资源还是原来的不影响。

   CopyOnWriteArrayList底层也是数组实现的,删除修改和添加元素操作时都需要加锁,只有读取的操作不会加锁。那么CopyOnWriteArrayList也就是线程安全的。

   问:为什么线程安全的List推荐用CopyOnWriteArrayList,而不是Vector?

   答:Vector和CopyOnWriteArrayList都是线程安全的list,底层都是数组实现的,

   Vector中的所有方法都被synchronized关键字修饰,而CopyWriteArrayList其中的读操作是不加锁的,因此CopyWriteArrayList的读取性能是远高于Vector的

   而Vector每次扩容,都是原数组的两倍,CopyWriteArrayList完全不需要扩容,通过复制副本去修改或者增加,新副本的长度正好是元素的长度,不会造成冗余。

   在开发中,读取操作会远超于其他操作,因此使用CopyWriteArrayList效率更高。

  

  以上就是List集合(list集合排序)的详细内容,想要了解更多 List集合的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: