集合List接口

基本概念:

1)可以动态保存多个对象

2)提供了 一系列方法便的操作对象的方法;

3)使用集合添加删除元素的代码非常简洁。

一、集合框架图

1、集合主要分为单列机和和双列集合

2、Collection接口有两个重要的子接口,List和set,他们实现的子类都是单列集合

 

 3、Map实现的子类是双列集合

二、Collection接口的常用方法

1、Collection接口常用方法:

1)add:添加元素

2)remove:删除指定元素

3)contains:查找元素是否存在

4)size:获取元素个数

5)isEmpty:判断是否为空

6)clear:清空

7)addAll:添加多个元素

8)containAll:查找多个元素是否都存在

9)removell:删除多个元素

2、Collection接口遍历元素的方式

使用Iterator迭代器

1)Iterator对象称之为迭代器,主要用于遍历Collection集合中的所有元素。

2)所有实现了Collection的接口的集合类都有一个iterator()方法,用于返回一个实现了Iterator接口的对象,即可以返回一个跌迭代器。

3)iterator的结构图

4)iterator仅用来遍历元素,本身并不是用来存储对象;

 

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

public class Teat13{
	public static void main(String[] args) {
		List list=new ArrayList();
		list.add(new Book("Jack",12));
		list.add(new Book("Tom",22));
		list.add(new Book("Lala",32));
		
		Iterator it=list.iterator();
		while(it.hasNext()) {//判断集合内还有无数据
			Object tmp=it.next();//将提取出的元素赋给tmp
			System.out.println(tmp);
		}
	}
}
class Book{
	private String name;
	private int price;
	
	public Book(String name, int price) {
		super();
		this.name = name;
		this.price = price;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
	@Override
	public String toString() {
		return "Book [name=" + name + ", price=" + price + "]";
	}
	
}

3、List接口

List接口常用方法

 List接口中的sort方法

public class Test2 {
	public static void main(String[] args) {
		List list = new ArrayList();
		list.add(new A2(99L, "zhangsan"));
		list.add(new A2(77L, "lisi"));
		list.add(new A2(99L, "wangwu"));
		list.sort(new Comparator<A2>() {
			public int compare(A2 o1, A2 o2) {
				// 自定义比较规则,按照id从小到大,如果id相等则按照name从大到小
				int res = o1.getId().compareTo(o2.getId());
				if (res == 0)
					res = (o1.getName().compareTo(o2.getName())) * -1;
				return res;
			}
		});
		list.forEach(System.out::println);
	}
}

class A2 {
	private Long id;
	private String name;

	public A2(Long id, String name) {
		this.id = id;
		this.name = name;
	}

	// ===================================
	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "A2 [id=" + id + ", name=" + name + "]";
	}

}

4、ArrayList源码分析

/*
 * ### ArrayList的使用和实现
 * List list=new ArrayList();
 * 
 * public class ArrayList<E> 
 * 		extends AbstractList<E> 通过继承抽象类可以共享所有公共方法
        implements List<E>, 实现List接口 
        RandomAccess,  实现随机访问接口
        Cloneable, 实现克隆接口
        java.io.Serializable  实现序列化接口
    
        
 *  transient Object[] elementData;   Arraylist真实存储数据的地方在elementData中;
 *  
 *  private int size; 当前集合中存储的元素个数 
 *  
 *  构造器
 *  	 public ArrayList() { 
 *  		//针对存储数据的数组进行初始化操作
        	this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;先定义一个空数组
        	//常量定义为DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};空数组
        	//如果使用无参构造器,则不会直接创建数组,而是采用空数组,当第一次添加元素时才创建数组
        	//使用无参构造器时ArrayList会构建一个空数组用于未来存放数据,这里是一种对内存消耗的优化处理
    	}
    	
    	
    	带参构造器   List list=new ArrayList(18) 
    	    18就是初始化容积,这里的初始化参数值必须为[0,int的最大值)
    	
    public ArrayList(int initialCapacity) {  //参数为初始化容积
        if (initialCapacity > 0) {  如果初始化容积大于0,则按照指定的容积创建数组
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;  空数组,长度为0的数组
        } else {  //初始化容积值小于0则报异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

	add方法的定义[重点]
	
	protected transient int modCount = 0;
	
	public boolean add(E e) {
        modCount++; 用于统计当前集合对象的修改次数   
        add(e, elementData, size);  参数1:新增元素;参数2:存储数据的数组;参数3:当前集合中存储的元素个数
        return true;   返回true表示添加成功
    }
    
    private void add(E e, Object[] elementData, int s) {
    	//如果数组已经存满了,则首先进行扩容处理
        if (s == elementData.length)  判断当前集合中存储的元素个数是否和数组长度相等
            elementData = grow();   如果相等则进行扩容处理
        elementData[s] = e; 在数组的指定位置存储元素
        size = s + 1;  集合中所存储的元素个数
    }
    
    private Object[] grow() {  //扩容处理方法
        return grow(size + 1);  //继续调用其它扩容方法,参数为 当前存储个元素个数+1---最小容积
    }

	private Object[] grow(int minCapacity) {
         //调用Arrays工具类中的方法进行数组的拷贝,同时调用newCapacity方法计算所需要的新容积值
        return elementData = Arrays.copyOf(elementData,newCapacity(minCapacity));  
    }
    
    
    private static final int DEFAULT_CAPACITY = 10;
    
    private int newCapacity(int minCapacity) {  计算所需要的新容积值
        int oldCapacity = elementData.length;  计算原来数组的长度
        //计算新容积值
        int newCapacity = oldCapacity + (oldCapacity >> 1); 相当于是老容积增加50%,这就是扩容比
        //计算出的新容积值是否满足所需要的最小容积值
        if (newCapacity - minCapacity <= 0) {
        	//如果存储数据的数组为初始化时的空数组,则计算默认初始化容积10和所需要最小容积值则最大值
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            if (minCapacity < 0) // OOM内存溢出
                throw new OutOfMemoryError();
            return minCapacity;
        }
        //如果计算出的新容积值大于所需要的最小容积值
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
            //如果计算出的新容积值小于最大允许的容积值,则返回计算出的新容积
             		(minCapacity > MAX_ARRAY_SIZE)
            			? Integer.MAX_VALUE
            			: MAX_ARRAY_SIZE;
           //如果计算出的新容积值大于最大允许的容积值并且minCapacity大于MAX_ARRAY_SIZE,则返回最大整数值,否则返回最大允许的容积值
    }
	
	//数组长度的上限值
	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;//减8是为了不爆内存
 * 
 *  
 *  数组的拷贝操纵定义在Arrays工具类中,copyof会新建一个指定长度newLength的数组,并且将原始数组中的所有元素拷贝到新数组中,同时返回新创建的数组
 *  参数1是原始数组,参数2为所需要的新长度
    public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }
    结论:如果直接初始化Arraylist,则采用延迟化处理【初始化数组长度为0】的方式节约内存,当添加数据时数据的数组长度不足时候,数组会自动变长,变长的长度为原来的50%;
     private void fastRemove(Object[] es, int i) {
        modCount++;   修改次数+1
        final int newSize;  
        if ((newSize = size - 1) > i) //size - 1就是可以删除元素的最大下标
            System.arraycopy(es, i + 1, es, i, newSize - i); 通过数组拷贝的方式覆盖掉指定位置的元素
        es[size = newSize] = null; //最后位置赋null值
        
        {1,2,3,4,5} 执行arrayCopy方法需要覆盖下标为2的元素{1,2,4,5,5}
    }
    
    结论:
    - 使用数组元素移动的方式实现元素的删除。注意:这里没有变小容积
    - 修改元素个数时会有modCount的修改--快速失败
    
    
    get方法的实现
      public E get(int index) {
        Objects.checkIndex(index, size);针对索引下标进行合法性验证
        return elementData(index);
  }
	
	private void checkForComodification(final int expectedModCount) {
        if (modCount != expectedModCount) {//期待的修改次数和实际修改次数是否相同
            throw new ConcurrentModificationException(); 并发修改异常
        }
    }
	

结论:首先要求index应该在[0,size-1]的范围内,否则异常

​            如果index正确则按照下标从数组中获取元素

	
 */

5、vector底层源码分析

/*
 * Vector
 * 
 * 类定义
 *  @since   1.0 从JDK1.0开始提供的实现类
 *  public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
 * 
 *属性:
 * protected Object[] elementData; 内部的数据存储采用的是Object[],数据存储在elementData数组之中;
 *
 * protected int elementCount; 存储的元素个数
 * 
 * protected int capacityIncrement;容积增长的步长值
 * 
 *构造器
 * public Vector() {
        this(10);
    }
   
    public Vector(int initialCapacity) { 参数为初始化容积
        this(initialCapacity, 0);
    }
    
    public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0) 初始化容积不能小于0
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity]; 初始化数组,不是ArrayList中的延迟初始化
        this.capacityIncrement = capacityIncrement;
    }
    
   成员方法:
   public synchronized boolean add(E e) {   方法上有synchronized,基本上所有的修改方法上都有synchronized关键字,所以线程安全的
        modCount++;  修改次数+1
        add(e, elementData, elementCount); 参数1为需要添加的元素,参数2为具体存储数据的数组,参数3为元素个数
        return true;
    }
    
    private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)   如果当前存储的元素个数和数据相等则先进行扩容处理
            elementData = grow();
        elementData[s] = e;
        elementCount = s + 1;
    }
    
    private Object[] grow() {
        return grow(elementCount + 1);
    }

	//具体的扩容处理,调用newCapacity方法获取新容积
	private Object[] grow(int minCapacity) {
        return elementData = Arrays.copyOf(elementData,
                                           newCapacity(minCapacity));
    }
    
    private int newCapacity(int minCapacity) {
        int oldCapacity = elementData.length;  获取原始容积值,也就是存储数据的数组长度
        //新容积值的算法:
         * 如果设置了容积增长的步长值,则新容积为原始容积+步长值
         * 如果没有设置容积增长的步长值,则扩容增长100%
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity <= 0) {
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return minCapacity;
        }
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
    }
    
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
 */

6、linkedList源代码分析

/*
	 * LinkedList:双向链表实现,增删快,查询慢 (线程不安全)
	 * 
	 * 属性:
	 * transient int size = 0; 存储的元素个数
	 * transient Node<E> first; 头节点first
	 * transient Node<E> last; 尾节点last
	 * 
	 * 节点的定义
	 * private static class Node<E> {  静态内部类
	        E item;  节点上存储的具体数据
	        Node<E> next;  指向下一个节点
	        Node<E> prev;  指向上一个节点

			//创建节点对象的参数
			 * prev前一个节点的引用  element具体存储的数据   next下一个节点引用
	        Node(Node<E> prev, E element, Node<E> next) {
	            this.item = element;
	            this.next = next;
	            this.prev = prev;
	        }
	    }
	    
	   构造器方法
	   
	    public LinkedList() {
	    }
	    
	    没有指定容积的构造器,理论上来说,链表实际上没有长度限制,但是int size属性要求元素个数必须在[0,Integer.max-value]范围内
	    
	    
	  add方法的定义:在双向链表的末尾新增元素(增)
	   public boolean add(E e) {
	        linkLast(e);
	        return true;
	    }
	    
	     void linkLast(E e) {
	        final Node<E> l = last;  缓存尾节点
	        final Node<E> newNode = new Node<>(l, e, null);  创建新的节点,新节点的下一个节点为null
	        last = newNode;  将尾指针执行新创建的节点
	        if (l == null)   如果原来的尾节点为null,则新增的节点应该是链表的第一个节点
	            first = newNode;  将头指针指向新创建的节点
	        else  
	            l.next = newNode;  如果原来的尾节点不为null,则表示已经有元素了,将原来的尾节点指向新创建的节点,这样就将新节点加入到链表中
	        size++; 链表中元素个数加1
	        modCount++;  修改次数加1
	    }
	    
	   --------------------------------------------------------------------------------
	   
	   add(int,Object) 在指定位置新增元素,原来位置上的元素后移
	   
	    public void add(int index, E element) {
	        checkPositionIndex(index); 检查index的合法性,要求index应该是在[0,size]的范围内
	        	index >= 0 && index <= size;如果不合法则抛出异常IndexOutOfBoundsException
	        if (index == size)  如果索引值index等于size,则在链表默认添加元素
	            linkLast(element);
	        else  在指定下标位置之前添加元素
	            linkBefore(element, node(index));
	    }
	    
	    //获取指定位置上的节点对象
	    Node<E> node(int index) {
	        if (index < (size >> 1)) { 如果序号值小于size/2则从头指针开始遍历链表,查找指定索引值的元素Node
	            Node<E> x = first;
	            for (int i = 0; i < index; i++)
	                x = x.next;  从前向后遍历
	            return x;
	        } else { 如果序号值大于等于size/2则从尾指针开始遍历链表,查找指定索引值的元素Node
	            Node<E> x = last;
	            for (int i = size - 1; i > index; i--)
	                x = x.prev;  从后向前遍历
	            return x;
	        }
	    }
	    
	    //在指定节点对象之前添加e数据
	     void linkBefore(E e, Node<E> succ) {
	        final Node<E> pred = succ.prev;  获取指定位置的前一个节点
	        final Node<E> newNode = new Node<>(pred, e, succ);  创建节点对象,参数1是原始节点的前一个节点,参数2是具体存储的数据,参数3是原始位置上的节点
	        succ.prev = newNode;  设置原始位置上的node对象的前一个节点为新创建的节点【双向链表】
	        if (pred == null)  如果指定位置的节点的前一个节点为null,则插入的新节点应该就是头节点
	            first = newNode;  使头指针指向新创建的节点
	        else   设置前一个节点的下一个节点指针指向新创建的节点
	            pred.next = newNode;
	        size++;  元素个数加1
	        modCount++;  修改次数加1
	    }
	    
	   --------------------------------------------------------------------------------
	   
	   删除和参数相等的第一个元素,删除成功返回true,否则返回false
	    public boolean remove(Object o) {
	        if (o == null) {  删除值为null的元素,使用==判断
	            for (Node<E> x = first; x != null; x = x.next) {从头指针开始执行遍历,查找第一个值为null的节点
	                if (x.item == null) {  //判断当前节点的值是否为null
	                    unlink(x);  删除指定的Node节点,并且返回Node中存储的数据
	                    return true;  
	                }
	            }
	        } else {  删除值非null的元素,判断使用equals方法
	            for (Node<E> x = first; x != null; x = x.next) {
	                if (o.equals(x.item)) { 判断当前需要删除的数据和节点中的数据是否相等
	                    unlink(x);  删除指定的Node节点,并且返回Node中存储的数据
	                    return true;
	                }
	            }
	        }
	        return false;
	    }

	   删除指定的Node节点,并且返回Node中存储的数据
	   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) { 如果当前节点的前一个节点为null,则表示当前节点为头节点
	            first = next;  使头指针指向当前节点的下一个节点
	        } else { 如果不是头节点
	            prev.next = next;   上一个节点的下一个节点为当前节点的下一个节点,将当前节点从链表中剔除出来
	            x.prev = null;  当前节点的上一个节点赋null值
	        }
	        if (next == null) {如果当前节点的下一个节点为null,则表示当前节点为尾节点
	            last = prev; 使尾指针指向当前节点的上一个节点
	        } else {  如果不是尾节点
	            next.prev = prev; 下一个节点的上一个节点引用值为当前节点的上一个节点,将当前节点从链表中剔除出来
	            x.next = null; 当前节点的下一个节点赋null值
	        }
	        x.item = null; 将当前节点的数据赋null值
	        size--; 链表中的元素个数-1
	        modCount++; 修改次数+1
	        return element; 返回当前节点原来的数据
	    }
	    
	    --------------------------------------------------------------------------------
	    
	    按照指定的索引序号删除对应的元素,同时返回删除元素的具体数据值
	    public E remove(int index) {
	        checkElementIndex(index);  检查序号值是否合法,不合法抛出异常IndexOutOfBoundsException  index >= 0 && index < size
	        return unlink(node(index));  首先查找指定索引序号对应的节点对象,然后删除对应的节点,返回原来节点上存储的具体数据
	    }
	    
	    修改操作
	    public E set(int index, E element) {
	        checkElementIndex(index);
	        Node<E> x = node(index);
	        E oldVal = x.item;  //获取原始节点存储的数据
	        x.item = element;  //修改节点上的数据为新值
	        return oldVal;  //返回原始存储的数据
	    }
	    
	    查找操作,如果找到则返回索引序号,如果没有找到则返回-1
	    public int indexOf(Object o) {
	        int index = 0;
	        if (o == null) {
	            for (Node<E> x = first; x != null; x = x.next) {
	                if (x.item == null)
	                    return index;
	                index++;
	            }
	        } else {
	            for (Node<E> x = first; x != null; x = x.next) {
	                if (o.equals(x.item))
	                    return index;
	                index++;
	            }
	        }
	        return -1;
	    }
	    
	 */

7、关于LinkedList、Arraylist、vector的对比

 ## 常见的List接口的实现类
 * - ArrayList:数组实现,查询快,增删慢,轻量级;(线程不安全)
 * - LinkedList:双向链表实现,增删快,查询慢 (线程不安全)
 * - Vector:数组实现,重量级 (线程安全、使用少)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340