集合——List

1.为什么使用集合

例如:存储数据
之前可以用数组存储数据,数组有很大的缺点,长度一旦确定,就没法修改,所以如果删除或者增加元素,需要大量的移动元素的位置。
数组:只能放一种数据类型,可以是基本数据类型也可以是引用数据类型

总行所述,可以使用集合来解决这种问题:

集合的优点:增加删除元素效率高
集合的特点:一个集合可以有多种数据类型(一般使用泛型,只存放一种数据类型),但是他只能存放引用数据类型

2. 集合的结构图

在这里插入图片描述

3.集合的应用场景

当需要将相同结构的个体整合到一起的时候,就考虑到用集合。

4.ArrayList

  • ArrayList的使用
public class Test {
    public static void main(String[] args) {
        Collection collection=new ArrayList();//接口new实现类
        System.out.println("集合是否为空:"+collection.isEmpty());
        //(1)添加元素
        collection.add("hello");
        collection.add(new Date());
        collection.add(123);
        collection.add("hello");
        collection.add(new Person("lisli",29, Gender.));
        System.out.println("集合是否为空:"+collection.isEmpty());
        System.out.println("集合中元素的个数:"+collection.size());
        System.out.println(collection);
        System.out.println(collection.remove("hello")); //将第一个hello删除
        //collection.clear();
        System.out.println(collection);
        System.out.println(collection.contains(123));
        Collection collection1=new ArrayList();
        collection1.add("world1");
        collection1.add("world2");
        collection1.add("world3");
        collection.addAll(collection1);
        System.out.println(collection);
        //遍历集合
        System.out.println("---------------------------------------");
        for(Object obj:collection){
            System.out.println(obj);//默认调用toString()方法
        }
        System.out.println("------------------自己使用迭代器遍历元素 hasNext() ,next()---------------------------");
        Iterator  ite= collection.iterator();
        while(ite.hasNext()){  //true就说明集合中有数据,为false,说明集合中没有数据,可以退出循环
           Object obj= ite.next();
            System.out.println(obj);
        }
    }
}

  • ArrayList的底层结构
    ArrayList:是对Object类型数组的封装
    (1)当调用ArrayList的无参构造方法创建ArrayList对象
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //初始长度为0,在堆里开空间了
         transient Object[] elementData; //声明一个Object类型的数组,并没有new对象
        public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; //指向 是长度为0的那个数组
     }

(2)当第一次调用add()方法添加元素时,给Object类型的数组初始化容量为10

private int size; //集合中元素的个数 ,默认值为0
      private static final int DEFAULT_CAPACITY = 10;
public boolean add(E e) {
        ensureCapacityInternal(size + 1);  //调用本类中的方法 
        elementData[size++] = e;
        return true;
    }
        
        private void ensureCapacityInternal(int minCapacity) {//minCapacity 的值为1
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
        
        private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//第一次调用add方法时执行
            return Math.max(DEFAULT_CAPACITY, minCapacity); //return Math.max(10,1);
        }
        return minCapacity;
    }
  private void ensureExplicitCapacity(int minCapacity) { //计算出来的最小容量为10
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0) //10-0>0?true 说明数组的长度已完全不够用
            grow(minCapacity);
    }
        
        private void grow(int minCapacity) {  //10
        // overflow-conscious code
        int oldCapacity = elementData.length;  //oldCapacity= 0
        int newCapacity = oldCapacity + (oldCapacity >> 1);  //0+0 结果为 newCapacity=0
        if (newCapacity - minCapacity < 0)   //0-10<0 true
            newCapacity = minCapacity;          //newCapacity=10;
        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);   //数组拷贝
    }

(3) 当第十一次调用add()方法时,数组扩容为15
int newCapacity = oldCapacity + (oldCapacity >> 1);
对ArrayList的增删改查就是对Object类型的数组的增删改查
ArrayList的优点:节省存储空间,按照索引查询效率高
ArrayList的缺点:删除、添加需要移动大量的元素,效率低,按照内容查找主要逐个比较判断,效率低下

  • LinkedList的使用
public class Test2 {
    public static void main(String[] args) {
        Collection collection=new LinkedList();//接口new实现类
        System.out.println("集合是否为空:"+collection.isEmpty());
        //(1)添加元素
        collection.add("hello");
        collection.add(new Date());
        collection.add(123);
        collection.add("hello");
        collection.add(new Person("lisli",29, Gender.));
        System.out.println("集合是否为空:"+collection.isEmpty());
        System.out.println("集合中元素的个数:"+collection.size());
        System.out.println(collection);
        System.out.println(collection.remove("hello")); //将第一个hello删除
        //collection.clear();
        System.out.println(collection);
        System.out.println(collection.contains(123));
        Collection collection1=new LinkedList();
        collection1.add("world1");
        collection1.add("world2");
        collection1.add("world3");
        collection.addAll(collection1);
        System.out.println(collection);
        //遍历集合
        System.out.println("---------------------------------------");
        for(Object obj:collection){
            System.out.println(obj);//默认调用toString()方法
        }
        System.out.println("------------------自己使用迭代器遍历元素 hasNext() ,next()---------------------------");
        Iterator  ite= collection.iterator();
        while(ite.hasNext()){  //true就说明集合中有数据,为false,说明集合中没有数据,可以退出循环
           Object obj= ite.next();
            System.out.println(obj);
        }
    }
}
  • LinkedList
    LinkedList的底层数据结构是链表,使用的是双向链表
transient Node<E> first;  //默认值均为null
 transient Node<E> last;
 
  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); //当调用add方法时,new Node创建节点对象
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }
        //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;
        }
    }

优点:删除,添加需要移动元素,效率高(但是需要先定位到元素上)
缺点:每个元素节点中,专门增加了空间来存储下个元素的地址,占用更多空间,每个节点地址不连续,无规律,导致按照索引查询效率低下

  • 使用迭代器对象遍历元素,添加元素
public class Test2 {
    public static void main(String[] args) {
        List list=new ArrayList();
        list.add("java");
        list.add("hello");
        list.add("world");
        list.add("sql");
        //想在遍历集合时添加元素
      // Iterator ite= list.iterator();
        ListIterator ite=list.listIterator();
       while(ite.hasNext()){
           Object obj=ite.next();
           if(obj.equals("hello")){
               ite.add("html");
           }
       }
        System.out.println(list);
    }
}

5.泛型

用于创建集合对象时对集合中所存储的元素的数据类型进行限制
泛型所用符号<数据类型>
泛型起作用的时间点:javac之前

public class Test {
    public static void main(String[] args) {
    	//创建集合对象时,规则了集合中存储的元素的数据类型只能是String类型
      List<String> list=new ArrayList<String>();
      
      list.add("hello");
      list.add("java");
      list.add("world");
    //  list.add(123);
      
    }
}

泛型的分类

(1)泛型接口

public interface Collection<E>{  //E代表一种数据类型,这个类型什么时候才能知道
}
 //在创建接口的实现类对象时,才能知道,在如下代码中,E所代表的数据类型是Integer
 Collection<Integer> coll=new ArrayList<Integer>();

(2)泛型类

public class ArrayList<E>{  //E的数据类型什么时候知道,创建ArrayList对象时知道

}
  ArrayList<String> al=new ArrayList<String>();

(3)泛型方法

public boolean add(E e) {}
  //E也代表一种数据类型 ,这个E的数据类型与ArrayList<E> 完全相同的,所以E的类型是在创建ArrayList的对象时确定


可变参数的泛型方法 :
(1)可变参数是JDK1.5
(2)泛型也是JDK1.5

public class Test {
    public static void main(String[] args) {
        Client<String> client=new Client<String>();//T的类型是String
        client.show("hello");
        //泛型方法,数据类型是在调用该方法时明确的,解决了同一个类中参数个数相同,类型不同的方法重载问题
        client.fun("hello");
        client.fun(123);
        client.fun(new Date());
         //可变参数的泛型方法,解决了数据类型不同,个数不同的方法重载问题
        client.method("hello");
        client.method("world1","world2","world3");
        client.method(123);
        client.method(8.7,9.8,7.6,9.9);
       //可变参数实际上就是一个数组
        Client<Integer> client2=new Client<Integer>();//T的类型时Integer
        client2.show(123);
    }
}
发布了37 篇原创文章 · 获赞 8 · 访问量 2232

猜你喜欢

转载自blog.csdn.net/penerx/article/details/104356607