首页 > 其他分享 >集合

集合

时间:2022-11-04 17:23:51浏览次数:72  
标签:list System println add 集合 public out

集合

1. 集合的理解和好处

  1. 数组

  • 长度开始时必须指定,而且一旦指定,不能更改;

  • 保存的必须为同一类型的元素

  • 使用数组进行增加元素的示意代码-----比较麻烦

  1. 集合

  • 可以动态保存任意多个对象,使用比较方便

  • 提供了一系列方便的操作对象:add,remove,set,get等

  • 使用集合添加,删除新元素的示意代码----简洁了

2. 集合的框架体系

java的集合类很多,主要分为两大类,如图:

image-20221101201923339

image-20221101201851297

 

  1. 集合主要是两组(单列集合,双列集合);

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

  3. Map接口的实现子类 是双列集合,存放的K-V

3. Collection接口和常用方法

3.1 Collection接口实现类的特点

 public interface Collection<E> extends Iterable<E>
  • collection实现子类可以存放多个元素,每个元素可以是object;

  • 有些Collection的实现类,可以存放重复的元素,有些不可以;

  • 有些Collection的实现类,有些是有序的(List),有些不是有序的(Set);

  • Collection接口没有直接的实现类,是通过它的子接口Set和List来实现的;

3.2 Collection接口和常用方法

Collection接口常用方法,以实现子类ArrayList来演示.

  • add:添加单个元素

  • remove:删除指定元素

  • contains:查找元素是否存在

  • size:获取元素个数

  • isEmpty:判断是否为空

  • clear:清空

  • addAll:添加多个元素

  • containsAll:查找多个元素是否都存在

  • removeAll:删除多个元素

 public class CollectionMethod {
     public static void main(String[] args) {
         //add:添加单个元素
         List list = new ArrayList();
         list.add("jack");
         list.add(10);//list.add(new Integer(10)) //自动装箱拆箱
         list.add(true);
         System.out.println("list="+list);
 ​
         //remove:删除单个元素
 //       list.remove(0);//删除第一个元素
         list.remove(true);//自动装箱拆箱
 //       System.out.println("list="+list);
 ​
         //contains:查找摸个元素是否存在
         System.out.println(list.contains("jack"));
 ​
         //size:获取元素个数
         System.out.println(list.size());
 ​
         //isEmpty:判断是否为空
         System.out.println(list.isEmpty());
 ​
         //clear:清空
         list.clear();
         System.out.println("list="+list);
 ​
         //addAll:添加多个元素
         ArrayList list2 = new ArrayList();
         list2.add("红楼梦");
         list2.add("西游记");
         list2.add("三国演义");
         list2.add("水浒传");
         list.addAll(list2);
         System.out.println("list="+list);
 ​
         //containsAll:查找多个元素是否存在
         System.out.println(list.containsAll(list2));
 ​
         //removeALL:删除多个元素
         ArrayList list3 = new ArrayList();
         list3.add("水浒传");
         list3.add("三国演义");
         list.removeAll(list3);
         System.out.println("list="+list);
 ​
    }
 }

 

3.3 Colletion接口遍历方式1-使用Iterator(迭代器)

1. 基本介绍

  • Iterator对象称为迭代器,主要用于遍历Collection集合中的元素

  • 所有实现了Collection接口的集合类都有一个Iterator()方法,用于返回一个实现了Iterator接口的对象,即可以

返回一个迭代器

  • Iterator的结构

  • Iterator仅用于遍历集合,Iterator本身并不存放对象;

2. 迭代器的执行原理

  1. 当创建完成指向某个集合或者容器的Iterator对象是,这是的指针其实指向的是第一个元素的上方,即指向一个 空

  2. 当调用hasNext方法的时候,只是判断下一个元素的有无,并不移动指针

  3. 当调用next方法的时候,向下移动指针,并且返回指针指向的元素,如果指针指向的内存中没有元素,会报异常。

  4. remove方法删除的元素是指针指向的元素。如果当前指针指向的内存中没有元素,那么会抛出异常。

 Iterator iterator = coll.iterator();//得到一个集合的迭代器
 //快捷键itit:快速遍历迭代器
 while (iterator.hasNext()){//判断是否还有下一个元素
     Object obj = iterator.next();//1.指针下移 2.将下移以后集合位置上的元素返回
     System.out.println(obj);
 }

3. Iterator接口的方法

hasNext:没有指针下移操作,只是判断是否存在下一个元素 next:指针下移,返回该指针所指向的元素 remove:删除当前指针所指向的元素,一般和next方法一起用,这时候的作用就是删除next方法返回的元素

注意:在调用iterator.next()方法之前必须要调用iterator.hasNext()进行检测.若不调用,且下一条记录无效,直接调用iterator.next()会抛出NoSuchElementException异常;

 public class CollectionIterator {
     public static void main(String[] args) {
         Collection coll = new ArrayList();
         coll.add(new Book("小李飞刀", "古龙", 5.1));
         coll.add(new Book("红楼梦", "曹雪芹", 34.6));
         coll.add(new Book("三国演义", "罗贯中", 10.1));
         //System.out.println(collection);
 ​
         //先得到coll对应的迭代器
         Iterator iterator = coll.iterator();
         while (iterator.hasNext()){//判断是否还有下一个元素
             //返回下一个元素,类型是Object
             Object obj = iterator.next();
             System.out.println(obj);
        }
         //当退出while循环后,这时Iterator迭代器,指向最后的元素
         //iterator.next();//NoSuchElementException
         //如果希望再次遍历,需要重置我们的迭代器
         iterator = coll.iterator();
         while (iterator.hasNext()) {
             Object next =  iterator.next();
             System.out.println(next);
        }
    }
 }
 ​
 class Book {
     private String name;
     private String author;
     private double price;
 ​
     public Book(String name, String author, double price) {
         this.name = name;
         this.author = author;
         this.price = price;
    }
 ​
     @Override
     public String toString() {
         return "Book{" +
                 "name='" + name + '\'' +
                 ", author='" + author + '\'' +
                 ", price=" + price +
                 '}';
    }
 ​
     public String getName() {
         return name;
    }
 ​
     public void setName(String name) {
         this.name = name;
    }
 ​
     public String getAuthor() {
         return author;
    }
 ​
     public void setAuthor(String author) {
         this.author = author;
    }
 ​
     public double getPrice() {
         return price;
    }
 ​
     public void setPrice(double price) {
         this.price = price;
    }
 }

3.4 Collection接口遍历对象方式2-for循环增强

增强for循环,可以代替Iterator迭代器,特点:增强for就是简化版的Iterator,本质一样,只能用于遍历集合或数组;

1. 基本语法

for(元素类型 元素名 : 集合名或数组名){

访问元素

}

2.底层原理

  1. 使用增强For,在Collection集合

  2. 增强for,底层仍然是迭代器

  3. 增强for可以理解成就是简化版本的迭代器遍历

  4. 快捷方式 I 或者 集合对象.for

  5. 增强for也可以直接在数组中使用

 public class CollectionFor {
     public static void main(String[] args) {
         Collection coll = new ArrayList();
         coll.add(new Book("小李飞刀", "古龙", 5.1));
         coll.add(new Book("红楼梦", "曹雪芹", 34.6));
         coll.add(new Book("三国演义", "罗贯中", 10.1));
 ​
         //1.使用增强For,在Collection集合
         //2.增强for,底层仍然是迭代器
         //3.增强for可以理解成就是简化版本的迭代器遍历
         //4.快捷方式 I 或者 集合对象.for
 ​
         for(Object book:coll){
             System.out.println("book"+book);
        }
 ​
         //增强for也可以直接在数组中使用
         int[] nums = {1,8,10,20};
         for(int i:nums){
             System.out.println(i);
        }
    }
 }

4. List接口和常用方法

4.1 List接口基本介绍

List接口是Collection接口的子接口

  1. List集合中元素有序(即添加顺序和取出顺序一致),且可重复;

  2. List集合中的每个元素都有器对应的顺序索引,即支持索引;

  3. List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素

  4. JDK API中List接口的实现类有很多,常用的有:ArrayList,LinkedList,Vector;

image-20221103234128407

4.2 List接口常用方法

  1. void add(int index,Object ele):在index位置插入ele元素;

  2. boolean addAll(int index,Collection eles):从index位置开始将eles中的所有元素添加进来;

  3. Object get(int index):获取指定index位置的元素;

  4. int indexOf(Object obj):返回obj在集合中首次出现的位置;

  5. int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置;

  6. Object remove(int index):移除指定index位置的元素,并返回此元素;

  7. Object set(int index,Object ele):设置指定index位置的元素为ele,相当于是替换;

  8. List subList(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集合; //注意:返回的子集合 包左不包右 fromIndex <= subList < toIndex

 public class ListMethod {
     @SuppressWarnings("all")
     public static void main(String[] args) {
         List list = new ArrayList();
         list.add("张三丰");
         list.add("贾宝玉");
         list.add("张三");
         //1.void add(int index,Object ele):在index位置插入ele元素
         //在index = 1的位置插入一个对象
         list.add(1,"张三");
         System.out.println("list="+list);
         System.out.println("----------------");
         //2.boolean addAll(int index,Collection eles):从index位置开始将eles中的所有元素添加进来
         List list2 = new ArrayList();
         list2.add("jack");
         list2.add("tom");
         list.addAll(1,list2);
         System.out.println("list="+list);
         System.out.println("----------------");
         //3.Object get(int index):获取指定index位置的元素
         //4.int indexOf(Object obj):返回obj在集合中首次出现的位置
         System.out.println(list.indexOf("tom"));//2
         System.out.println("----------------");
         //5.int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
         System.out.println(list);
         System.out.println(list.lastIndexOf("张三"));//5
         System.out.println("----------------");
         //6. Object remove(int index):移除指定index位置的元素,并返回此元素
         list.remove(0);
         System.out.println("remove:"+list);
         System.out.println("----------------");
         //7.Object set(int index,Object ele):设置指定index位置的元素为ele,相当于是替换
         list.set(1,"阿狸");
         System.out.println(list);
         System.out.println("----------------");
         //8.List subList(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集合
         //注意:返回的子集合 包左不包右 fromIndex <= subList < toIndex
         System.out.println(list);
         List resList = list.subList(1,list.size());
         System.out.println(resList);
    }
 }

4.3 List的三种遍历方式

List接口实现子类[ArrayList,LinkedList,Vector],遍历方式相同

  1. 方式一:使用Iterator

 Iterator iter  =  list.Iterator();
 ​
 while(iter.hasNext()){
 ​
 • Object 0 = iter.next();
 ​
 }

 

  1. 方式二:使用增强for

 for(Object o : list){
 ​
 }

 

  1. 方式三:使用普通for

 for (int i = 0; i < list.size(); i++) {
     Object obj = list.get(i);
     System.out.println(obj);
 }

说明:使用LinkedList完成使用方式和ArrayList一样

5. ArrayList底层结构和源码分析

5.1 ArrayList的注意事项

  • permits all elements,including null;(允许所有元素,包括null),ArrayList可以加入null,并且多个;

  • ArrayList是由数组来实现数据存储的

  • ArrayList基本等同于Vector,除了ArrayList是线程不安全(执行效率高)看源码,没有synchronized关键字修饰

在多线程情况下,不建议使用ArrayList;

5.2 ArrayList的底层操作机制源码分析(重点,难点)

  • ArrayList中维护了一个Object类型的elementData数组

     transient Object[] elementData;//transient 表示瞬间,短暂的,表示该属性不会被序列化

(Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过程)

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

  • image-20221103200951833

  • 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍;

6. Vector底层结构和源码剖析

6.1 Vector的基本介绍

  1. Vector类的定义说明;

 public class Vector<E>
     extends AbstractList<E>
     implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  1. Vector底层也是一个对象数组;

 protected Object[] elementData;
  1. Vector是线程同步,即线程安全,Vector类的操作方法带有synchronized;

 public synchronized E get(int index) {
     if (index >= elementCount)
         throw new ArrayIndexOutOfBoundsException(index);
 ​
     return elementData(index);
 }
  1. 在开发中,需要线程同步安全时,考虑使用Vector;

6.2 Vector和ArrayList的比较

image-20221103190940467

6.3 Vector底层结构

 public class Vector_ {
     public static void main(String[] args) {
         //无参构造器
         //Vector vector = new Vector();
         Vector vector = new Vector(10);
         for (int i = 0; i < 10; i++) {
             vector.add(i);
        }
         vector.add(100);
         System.out.println("vector="+vector);
 ​
         //解读源码
         /*
         //1. new Vector() 底层
             public Vector() {
                 this(10);
              }
         补充:如果是有参数构造 Vector vector = new Vector(8);
         走的方法是:
             public Vector(int initialCapacity) {
                 this(initialCapacity, 0);
             }
         2.vector.add(i)
         2.1 //下面这个方法就添加数据到vector集合
             public synchronized boolean add(E e) {
                 modCount++;
                 ensureCapacityHelper(elementCount + 1);
                 elementData[elementCount++] = e;
                 return true;
             }
         2.2 //确定是否需要扩容 条件 :   minCapacity - elementData.length > 0
                 private void ensureCapacityHelper(int minCapacity) {
                     // overflow-conscious code
                     if (minCapacity - elementData.length > 0)
                         grow(minCapacity);
                 }
         2.3 //如果需要的数组大小 不够用,就扩容 ,扩容的算法
             //newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                            capacityIncrement : oldCapacity);
             //就是扩容两倍
                  private void grow(int minCapacity) {
                     // overflow-conscious code
                     int oldCapacity = elementData.length;
                     int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                                      capacityIncrement : oldCapacity);
                     if (newCapacity - minCapacity < 0)
                         newCapacity = minCapacity;
                     if (newCapacity - MAX_ARRAY_SIZE > 0)
                         newCapacity = hugeCapacity(minCapacity);
                     elementData = Arrays.copyOf(elementData, newCapacity);
                 }
         */
    }
 }

7. LinkedList底层结构

7.1 LinkedList的全面说明

  1. LinkedList底层实现了双向链表和双端队列特点;

  2. 可以添加任意元素(元素可以重复,包括null);

  3. 线程不安全,没有实现同步;

7.2LinkedList的底层操作机制

  1. LinkedList底层维护了一个双向链表;

  2. LinkedList中维护了两个属性first和last分别指向首节点和尾结点;

  3. 每个节点(Node对象),里面又维护了prev,next,item三个属性,其中通过prev指向前一个,通过next指向后一个节点,最终实现双向链表;

  4. 所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高;

  5. 模拟一个简单的双向链表;

image-20221103213238150

 public class LinkedList01 {
     public static void main(String[] args) {
         //模拟一个简单的双向链表
         Node jack = new Node("jack");
         Node tom = new Node("tom");
         Node mary = new Node("mary");
         //连接三个节点,形成双向链表
         //jack -> tom -> mary
         jack.next = tom;
         tom.next = mary;
         //mary -> tom -> jack
         mary.pre = tom;
         tom.pre = jack;
 ​
         Node first = jack;//让first引用指向jack,就是双向链表的头结点
         Node last = mary;//让last引用指向mary,就是双向链表的尾结点
 ​
         //演示 , 从头到尾遍历
         System.out.println("从头到尾遍历===================");
         while (true) {
             if (first == null) {
                 break;
            }
             System.out.println(first);
             first = first.next;
        }
 ​
         //演示 , 从尾到头遍历
         System.out.println("从尾到头遍历===================");
         while (true) {
             if (last == null) {
                 break;
            }
             System.out.println(last);
             last = last.pre;
        }
 ​
         //演示链表的添加对象/数据,是多么方便
         //要求,在tom   和 mary之间 插入一个对象 smith
         //1.先创建一个Node结点,name就是smith
         Node smith = new Node("smith");
         //下面就把smith加入双向链表了
         tom.next = smith;
         smith.pre = tom;
         smith.next = mary;
         mary.pre=smith;
         System.out.println("===========================");
         //让fist再次指向jack
         first = jack;//重置一下first
         while (true) {
             if (first == null) {
                 break;
            }
             System.out.println(first);
             first = first.next;
        }
    }
 }
 ​
 //定义一个Node类,Node对象 表示双向链表的一个节点
 class Node {
     public Object item;//真正存放数据
     public Node next;//指向后一个节点
     public Node pre;//指向前一个节点
 ​
     public Node(Object item) {
         this.item = item;
    }
 ​
     @Override
     public String toString() {
         return "Node name=" + item;
    }
 }

7.3 LinkedList底层结构

 public class LinkedListCRUD {
     public static void main(String[] args) {
 ​
         LinkedList linkedList = new LinkedList();
         linkedList.add(1);
         linkedList.add(2);
         linkedList.add(3);
         System.out.println("linkedList="+linkedList);
 ​
         //演示一个删除结点的
         linkedList.remove();//这里默认删除的是第一个结点
         //linkedList.remove(2);
         System.out.println("LinkedList="+linkedList);
 ​
         //修改某个结点
         linkedList.set(1,999);
         System.out.println("LinkedList="+linkedList);
 ​
         //得到某个结点对象
         //get(1)是得到双向链表的第二个对象
         Object o = linkedList.get(1);
         System.out.println(o);//999
 ​
         //因为LinkedList是实现了List接口 ,遍历方式
         System.out.println("====LinkedList遍历迭代器====");
         Iterator iterator = linkedList.iterator();
         while (iterator.hasNext()) {
             Object next =  iterator.next();
             System.out.println(next);
        }
         System.out.println("====LinkedList遍历增强For====");
         for (Object oo :linkedList) {
             System.out.println(oo);
        }
         System.out.println("====LinkedList遍历普通for====");
         for (int i = 0; i < linkedList.size(); i++) {
             System.out.println(linkedList.get(i));
        }
 ​
 ​
         //源码阅读
         /*
             1. LinkedList linkedList = new LinkedList();
                  public LinkedList(){}
             2.这时LinkedList的属性:first = null last=null
             3.执行添加方法
                 public boolean add(E e) {
                     linkLast(e);
                     return true;
                  }
             4.将新的结点,加入到双向链表的最后;
                 void linkLast(E e) {
                     final Node<E> l = last;
                     final Node<E> newNode = new Node<>(l, e, null);
                     last = newNode;
                     if (l == null)
                         first = newNode;
                     else
                         l.next = newNode;
                     size++;
                     modCount++;
                 }
          */
 ​
         /*
             linkedList.remove();//这里默认删除的是第一个结点
             1.执行removeFirst
               public E remove() {
                  return removeFirst();
               }
             2.执行
                public E removeFirst() {
                    final Node<E> f = first;
                    if (f == null)
                        throw new NoSuchElementException();
                    return unlinkFirst(f);
                }
             3. 执行unlinkFirst,将f指向的双向链表的第一个结点拿掉
                private E unlinkFirst(Node<E> f) {
                     // assert f == first && f != null;
                     final E element = f.item;
                     final Node<E> next = f.next;
                     f.item = null;
                     f.next = null; // help GC
                     first = next;
                     if (next == null)
                         last = null;
                     else
                         next.prev = null;
                     size--;
                     modCount++;
                     return element;
                 }
          */
    }
 }

7.4 ArrayList和LinkedList的比较

  1. ArrayList和LinkedList的比较

image-20221103233256770

ArrayList改查的效率较高,因为可以通过索引直接定位;LinkedList改查效率较低,会从头开始,一个一个遍历;

  1. 如何选择ArrayList和LinkedList:

  • 如果我们改查的操作多,选择ArrayList;

  • 如果我们增删的操作多,选择LinkedList;

  • 一般来说,在程序中,80%-90%都是查询,因此大多数情况下会选择ArrayList;

  • 在一个项目中,根据业务灵活选择,也可以这样,一个模块下使用的是ArrayList,另一个模块下选择的是LinkedList,也就是说,要根据业务来进行选择;

8. Set接口和常用方法

8.1 Set接口基本介绍

  1. 无序(添加和取出顺序不一致),没有索引;

  2. 不允许重复元素,所以最多包含一个null;

  3. JDKAPI中Set接口的实现类有:

image-20221103234103670

8.2 Set接口的常用方法

和List接口一样,Set接口也是Collection的子接口,因此,常用方法和Collection接口一样;

8.3 Set接口的遍历方式

同Collection的遍历方式一样,因为Set接口是Collection接口的子接口;

  1. 可以使用迭代器

  2. 增强for

  3. 不能使用索引的方式获取

 public class Setmethod {
     public static void main(String[] args) {
 ​
         //1. 以Set接口的实现类,HashSet来讲解Set接口的方法
         //2. set接口的实现类的对象(Set接口对象),不能存放重复的元素,可以添加一个null
         //3. set接口对象存放数据是无序的(即添加的顺序和取出的顺序不一致)
         //4. 注意:取出的顺序虽然不是添加的顺序,但是他是固定的;
         Set set = new HashSet();
         Set set2 = new HashSet();
         set.add("john");
         set.add("lucy");
         set.add("mary");
         set.add("jack");
         set.add(null);
         set.add(null);
         for (int i = 0; i < 3; i++) {
             System.out.println("set=" + set);
        }
 ​
         //删除
         set.remove(null);
         boolean flag = set.equals(5);
         System.out.println(flag);
         //遍历
         //方式1: 使用迭代器
         System.out.println("=========使用迭代器=========");
         Iterator iterator = set.iterator();
         while (iterator.hasNext()) {
             Object obj = iterator.next();
             System.out.println("obj=" + obj);
        }
 ​
         //方式2: 使用增强For
         System.out.println("=========使用增强for=========");
         for (Object o : set) {
             System.out.println("o=" + o);
        }
         //set接口对象,不能通过索引获取
    }
 }

9. Set接口实现类-HashSet

9.1HashSet的全面说明

  1. HashSet实现了Set接口;

  2. HashSet实际上是HashMap,看下源码;

 public HashSet() {
     map = new HashMap<>();
 }
  1. 可以存放null值,但是只能有一个null;

  2. HashSet不保证元素是有序,取决于hash后,在确定索引的结果;(不保证存放元素的顺序和取出顺序一致);

  3. 不能有重复元素/对象,在Set接口使用已经讲过;

9.2 HashSet底层机制说明

分析HashSet底层是HashMap,HashMap底层是(数组+链表+红黑树);

为了方便理解,模拟一个简单的数组+链表结构;

集合

1. 集合的理解和好处

  1. 数组

  • 长度开始时必须指定,而且一旦指定,不能更改;

  • 保存的必须为同一类型的元素

  • 使用数组进行增加元素的示意代码-----比较麻烦

  1. 集合

  • 可以动态保存任意多个对象,使用比较方便

  • 提供了一系列方便的操作对象:add,remove,set,get等

  • 使用集合添加,删除新元素的示意代码----简洁了

2. 集合的框架体系

java的集合类很多,主要分为两大类,如图:

image-20221101201923339

image-20221101201851297

 

  1. 集合主要是两组(单列集合,双列集合);

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

  3. Map接口的实现子类 是双列集合,存放的K-V

3. Collection接口和常用方法

3.1 Collection接口实现类的特点

 public interface Collection<E> extends Iterable<E>
  • collection实现子类可以存放多个元素,每个元素可以是object;

  • 有些Collection的实现类,可以存放重复的元素,有些不可以;

  • 有些Collection的实现类,有些是有序的(List),有些不是有序的(Set);

  • Collection接口没有直接的实现类,是通过它的子接口Set和List来实现的;

3.2 Collection接口和常用方法

Collection接口常用方法,以实现子类ArrayList来演示.

  • add:添加单个元素

  • remove:删除指定元素

  • contains:查找元素是否存在

  • size:获取元素个数

  • isEmpty:判断是否为空

  • clear:清空

  • addAll:添加多个元素

  • containsAll:查找多个元素是否都存在

  • removeAll:删除多个元素

 public class CollectionMethod {
     public static void main(String[] args) {
         //add:添加单个元素
         List list = new ArrayList();
         list.add("jack");
         list.add(10);//list.add(new Integer(10)) //自动装箱拆箱
         list.add(true);
         System.out.println("list="+list);
 ​
         //remove:删除单个元素
 //       list.remove(0);//删除第一个元素
         list.remove(true);//自动装箱拆箱
 //       System.out.println("list="+list);
 ​
         //contains:查找摸个元素是否存在
         System.out.println(list.contains("jack"));
 ​
         //size:获取元素个数
         System.out.println(list.size());
 ​
         //isEmpty:判断是否为空
         System.out.println(list.isEmpty());
 ​
         //clear:清空
         list.clear();
         System.out.println("list="+list);
 ​
         //addAll:添加多个元素
         ArrayList list2 = new ArrayList();
         list2.add("红楼梦");
         list2.add("西游记");
         list2.add("三国演义");
         list2.add("水浒传");
         list.addAll(list2);
         System.out.println("list="+list);
 ​
         //containsAll:查找多个元素是否存在
         System.out.println(list.containsAll(list2));
 ​
         //removeALL:删除多个元素
         ArrayList list3 = new ArrayList();
         list3.add("水浒传");
         list3.add("三国演义");
         list.removeAll(list3);
         System.out.println("list="+list);
 ​
    }
 }

 

3.3 Colletion接口遍历方式1-使用Iterator(迭代器)

1. 基本介绍

  • Iterator对象称为迭代器,主要用于遍历Collection集合中的元素

  • 所有实现了Collection接口的集合类都有一个Iterator()方法,用于返回一个实现了Iterator接口的对象,即可以

返回一个迭代器

  • Iterator的结构

  • Iterator仅用于遍历集合,Iterator本身并不存放对象;

2. 迭代器的执行原理

  1. 当创建完成指向某个集合或者容器的Iterator对象是,这是的指针其实指向的是第一个元素的上方,即指向一个 空

  2. 当调用hasNext方法的时候,只是判断下一个元素的有无,并不移动指针

  3. 当调用next方法的时候,向下移动指针,并且返回指针指向的元素,如果指针指向的内存中没有元素,会报异常。

  4. remove方法删除的元素是指针指向的元素。如果当前指针指向的内存中没有元素,那么会抛出异常。

 Iterator iterator = coll.iterator();//得到一个集合的迭代器
 //快捷键itit:快速遍历迭代器
 while (iterator.hasNext()){//判断是否还有下一个元素
     Object obj = iterator.next();//1.指针下移 2.将下移以后集合位置上的元素返回
     System.out.println(obj);
 }

3. Iterator接口的方法

hasNext:没有指针下移操作,只是判断是否存在下一个元素 next:指针下移,返回该指针所指向的元素 remove:删除当前指针所指向的元素,一般和next方法一起用,这时候的作用就是删除next方法返回的元素

注意:在调用iterator.next()方法之前必须要调用iterator.hasNext()进行检测.若不调用,且下一条记录无效,直接调用iterator.next()会抛出NoSuchElementException异常;

 public class CollectionIterator {
     public static void main(String[] args) {
         Collection coll = new ArrayList();
         coll.add(new Book("小李飞刀", "古龙", 5.1));
         coll.add(new Book("红楼梦", "曹雪芹", 34.6));
         coll.add(new Book("三国演义", "罗贯中", 10.1));
         //System.out.println(collection);
 ​
         //先得到coll对应的迭代器
         Iterator iterator = coll.iterator();
         while (iterator.hasNext()){//判断是否还有下一个元素
             //返回下一个元素,类型是Object
             Object obj = iterator.next();
             System.out.println(obj);
        }
         //当退出while循环后,这时Iterator迭代器,指向最后的元素
         //iterator.next();//NoSuchElementException
         //如果希望再次遍历,需要重置我们的迭代器
         iterator = coll.iterator();
         while (iterator.hasNext()) {
             Object next =  iterator.next();
             System.out.println(next);
        }
    }
 }
 ​
 class Book {
     private String name;
     private String author;
     private double price;
 ​
     public Book(String name, String author, double price) {
         this.name = name;
         this.author = author;
         this.price = price;
    }
 ​
     @Override
     public String toString() {
         return "Book{" +
                 "name='" + name + '\'' +
                 ", author='" + author + '\'' +
                 ", price=" + price +
                 '}';
    }
 ​
     public String getName() {
         return name;
    }
 ​
     public void setName(String name) {
         this.name = name;
    }
 ​
     public String getAuthor() {
         return author;
    }
 ​
     public void setAuthor(String author) {
         this.author = author;
    }
 ​
     public double getPrice() {
         return price;
    }
 ​
     public void setPrice(double price) {
         this.price = price;
    }
 }

3.4 Collection接口遍历对象方式2-for循环增强

增强for循环,可以代替Iterator迭代器,特点:增强for就是简化版的Iterator,本质一样,只能用于遍历集合或数组;

1. 基本语法

for(元素类型 元素名 : 集合名或数组名){

访问元素

}

2.底层原理

  1. 使用增强For,在Collection集合

  2. 增强for,底层仍然是迭代器

  3. 增强for可以理解成就是简化版本的迭代器遍历

  4. 快捷方式 I 或者 集合对象.for

  5. 增强for也可以直接在数组中使用

 public class CollectionFor {
     public static void main(String[] args) {
         Collection coll = new ArrayList();
         coll.add(new Book("小李飞刀", "古龙", 5.1));
         coll.add(new Book("红楼梦", "曹雪芹", 34.6));
         coll.add(new Book("三国演义", "罗贯中", 10.1));
 ​
         //1.使用增强For,在Collection集合
         //2.增强for,底层仍然是迭代器
         //3.增强for可以理解成就是简化版本的迭代器遍历
         //4.快捷方式 I 或者 集合对象.for
 ​
         for(Object book:coll){
             System.out.println("book"+book);
        }
 ​
         //增强for也可以直接在数组中使用
         int[] nums = {1,8,10,20};
         for(int i:nums){
             System.out.println(i);
        }
    }
 }

4. List接口和常用方法

4.1 List接口基本介绍

List接口是Collection接口的子接口

  1. List集合中元素有序(即添加顺序和取出顺序一致),且可重复;

  2. List集合中的每个元素都有器对应的顺序索引,即支持索引;

  3. List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素

  4. JDK API中List接口的实现类有很多,常用的有:ArrayList,LinkedList,Vector;

image-20221103234128407

4.2 List接口常用方法

  1. void add(int index,Object ele):在index位置插入ele元素;

  2. boolean addAll(int index,Collection eles):从index位置开始将eles中的所有元素添加进来;

  3. Object get(int index):获取指定index位置的元素;

  4. int indexOf(Object obj):返回obj在集合中首次出现的位置;

  5. int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置;

  6. Object remove(int index):移除指定index位置的元素,并返回此元素;

  7. Object set(int index,Object ele):设置指定index位置的元素为ele,相当于是替换;

  8. List subList(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集合; //注意:返回的子集合 包左不包右 fromIndex <= subList < toIndex

 public class ListMethod {
     @SuppressWarnings("all")
     public static void main(String[] args) {
         List list = new ArrayList();
         list.add("张三丰");
         list.add("贾宝玉");
         list.add("张三");
         //1.void add(int index,Object ele):在index位置插入ele元素
         //在index = 1的位置插入一个对象
         list.add(1,"张三");
         System.out.println("list="+list);
         System.out.println("----------------");
         //2.boolean addAll(int index,Collection eles):从index位置开始将eles中的所有元素添加进来
         List list2 = new ArrayList();
         list2.add("jack");
         list2.add("tom");
         list.addAll(1,list2);
         System.out.println("list="+list);
         System.out.println("----------------");
         //3.Object get(int index):获取指定index位置的元素
         //4.int indexOf(Object obj):返回obj在集合中首次出现的位置
         System.out.println(list.indexOf("tom"));//2
         System.out.println("----------------");
         //5.int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
         System.out.println(list);
         System.out.println(list.lastIndexOf("张三"));//5
         System.out.println("----------------");
         //6. Object remove(int index):移除指定index位置的元素,并返回此元素
         list.remove(0);
         System.out.println("remove:"+list);
         System.out.println("----------------");
         //7.Object set(int index,Object ele):设置指定index位置的元素为ele,相当于是替换
         list.set(1,"阿狸");
         System.out.println(list);
         System.out.println("----------------");
         //8.List subList(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集合
         //注意:返回的子集合 包左不包右 fromIndex <= subList < toIndex
         System.out.println(list);
         List resList = list.subList(1,list.size());
         System.out.println(resList);
    }
 }

4.3 List的三种遍历方式

List接口实现子类[ArrayList,LinkedList,Vector],遍历方式相同

  1. 方式一:使用Iterator

 Iterator iter  =  list.Iterator();
 ​
 while(iter.hasNext()){
 ​
 • Object 0 = iter.next();
 ​
 }

 

  1. 方式二:使用增强for

 for(Object o : list){
 ​
 }

 

  1. 方式三:使用普通for

 for (int i = 0; i < list.size(); i++) {
     Object obj = list.get(i);
     System.out.println(obj);
 }

说明:使用LinkedList完成使用方式和ArrayList一样

5. ArrayList底层结构和源码分析

5.1 ArrayList的注意事项

  • permits all elements,including null;(允许所有元素,包括null),ArrayList可以加入null,并且多个;

  • ArrayList是由数组来实现数据存储的

  • ArrayList基本等同于Vector,除了ArrayList是线程不安全(执行效率高)看源码,没有synchronized关键字修饰

在多线程情况下,不建议使用ArrayList;

5.2 ArrayList的底层操作机制源码分析(重点,难点)

  • ArrayList中维护了一个Object类型的elementData数组

     transient Object[] elementData;//transient 表示瞬间,短暂的,表示该属性不会被序列化

(Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过程)

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

  • image-20221103200951833

  • 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍;

6. Vector底层结构和源码剖析

6.1 Vector的基本介绍

  1. Vector类的定义说明;

 public class Vector<E>
     extends AbstractList<E>
     implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  1. Vector底层也是一个对象数组;

 protected Object[] elementData;
  1. Vector是线程同步,即线程安全,Vector类的操作方法带有synchronized;

 public synchronized E get(int index) {
     if (index >= elementCount)
         throw new ArrayIndexOutOfBoundsException(index);
 ​
     return elementData(index);
 }
  1. 在开发中,需要线程同步安全时,考虑使用Vector;

6.2 Vector和ArrayList的比较

image-20221103190940467

6.3 Vector底层结构

 public class Vector_ {
     public static void main(String[] args) {
         //无参构造器
         //Vector vector = new Vector();
         Vector vector = new Vector(10);
         for (int i = 0; i < 10; i++) {
             vector.add(i);
        }
         vector.add(100);
         System.out.println("vector="+vector);
 ​
         //解读源码
         /*
         //1. new Vector() 底层
             public Vector() {
                 this(10);
              }
         补充:如果是有参数构造 Vector vector = new Vector(8);
         走的方法是:
             public Vector(int initialCapacity) {
                 this(initialCapacity, 0);
             }
         2.vector.add(i)
         2.1 //下面这个方法就添加数据到vector集合
             public synchronized boolean add(E e) {
                 modCount++;
                 ensureCapacityHelper(elementCount + 1);
                 elementData[elementCount++] = e;
                 return true;
             }
         2.2 //确定是否需要扩容 条件 :   minCapacity - elementData.length > 0
                 private void ensureCapacityHelper(int minCapacity) {
                     // overflow-conscious code
                     if (minCapacity - elementData.length > 0)
                         grow(minCapacity);
                 }
         2.3 //如果需要的数组大小 不够用,就扩容 ,扩容的算法
             //newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                            capacityIncrement : oldCapacity);
             //就是扩容两倍
                  private void grow(int minCapacity) {
                     // overflow-conscious code
                     int oldCapacity = elementData.length;
                     int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                                      capacityIncrement : oldCapacity);
                     if (newCapacity - minCapacity < 0)
                         newCapacity = minCapacity;
                     if (newCapacity - MAX_ARRAY_SIZE > 0)
                         newCapacity = hugeCapacity(minCapacity);
                     elementData = Arrays.copyOf(elementData, newCapacity);
                 }
         */
    }
 }

7. LinkedList底层结构

7.1 LinkedList的全面说明

  1. LinkedList底层实现了双向链表和双端队列特点;

  2. 可以添加任意元素(元素可以重复,包括null);

  3. 线程不安全,没有实现同步;

7.2LinkedList的底层操作机制

  1. LinkedList底层维护了一个双向链表;

  2. LinkedList中维护了两个属性first和last分别指向首节点和尾结点;

  3. 每个节点(Node对象),里面又维护了prev,next,item三个属性,其中通过prev指向前一个,通过next指向后一个节点,最终实现双向链表;

  4. 所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高;

  5. 模拟一个简单的双向链表;

image-20221103213238150

 public class LinkedList01 {
     public static void main(String[] args) {
         //模拟一个简单的双向链表
         Node jack = new Node("jack");
         Node tom = new Node("tom");
         Node mary = new Node("mary");
         //连接三个节点,形成双向链表
         //jack -> tom -> mary
         jack.next = tom;
         tom.next = mary;
         //mary -> tom -> jack
         mary.pre = tom;
         tom.pre = jack;
 ​
         Node first = jack;//让first引用指向jack,就是双向链表的头结点
         Node last = mary;//让last引用指向mary,就是双向链表的尾结点
 ​
         //演示 , 从头到尾遍历
         System.out.println("从头到尾遍历===================");
         while (true) {
             if (first == null) {
                 break;
            }
             System.out.println(first);
             first = first.next;
        }
 ​
         //演示 , 从尾到头遍历
         System.out.println("从尾到头遍历===================");
         while (true) {
             if (last == null) {
                 break;
            }
             System.out.println(last);
             last = last.pre;
        }
 ​
         //演示链表的添加对象/数据,是多么方便
         //要求,在tom   和 mary之间 插入一个对象 smith
         //1.先创建一个Node结点,name就是smith
         Node smith = new Node("smith");
         //下面就把smith加入双向链表了
         tom.next = smith;
         smith.pre = tom;
         smith.next = mary;
         mary.pre=smith;
         System.out.println("===========================");
         //让fist再次指向jack
         first = jack;//重置一下first
         while (true) {
             if (first == null) {
                 break;
            }
             System.out.println(first);
             first = first.next;
        }
    }
 }
 ​
 //定义一个Node类,Node对象 表示双向链表的一个节点
 class Node {
     public Object item;//真正存放数据
     public Node next;//指向后一个节点
     public Node pre;//指向前一个节点
 ​
     public Node(Object item) {
         this.item = item;
    }
 ​
     @Override
     public String toString() {
         return "Node name=" + item;
    }
 }

7.3 LinkedList底层结构

 public class LinkedListCRUD {
     public static void main(String[] args) {
 ​
         LinkedList linkedList = new LinkedList();
         linkedList.add(1);
         linkedList.add(2);
         linkedList.add(3);
         System.out.println("linkedList="+linkedList);
 ​
         //演示一个删除结点的
         linkedList.remove();//这里默认删除的是第一个结点
         //linkedList.remove(2);
         System.out.println("LinkedList="+linkedList);
 ​
         //修改某个结点
         linkedList.set(1,999);
         System.out.println("LinkedList="+linkedList);
 ​
         //得到某个结点对象
         //get(1)是得到双向链表的第二个对象
         Object o = linkedList.get(1);
         System.out.println(o);//999
 ​
         //因为LinkedList是实现了List接口 ,遍历方式
         System.out.println("====LinkedList遍历迭代器====");
         Iterator iterator = linkedList.iterator();
         while (iterator.hasNext()) {
             Object next =  iterator.next();
             System.out.println(next);
        }
         System.out.println("====LinkedList遍历增强For====");
         for (Object oo :linkedList) {
             System.out.println(oo);
        }
         System.out.println("====LinkedList遍历普通for====");
         for (int i = 0; i < linkedList.size(); i++) {
             System.out.println(linkedList.get(i));
        }
 ​
 ​
         //源码阅读
         /*
             1. LinkedList linkedList = new LinkedList();
                  public LinkedList(){}
             2.这时LinkedList的属性:first = null last=null
             3.执行添加方法
                 public boolean add(E e) {
                     linkLast(e);
                     return true;
                  }
             4.将新的结点,加入到双向链表的最后;
                 void linkLast(E e) {
                     final Node<E> l = last;
                     final Node<E> newNode = new Node<>(l, e, null);
                     last = newNode;
                     if (l == null)
                         first = newNode;
                     else
                         l.next = newNode;
                     size++;
                     modCount++;
                 }
          */
 ​
         /*
             linkedList.remove();//这里默认删除的是第一个结点
             1.执行removeFirst
               public E remove() {
                  return removeFirst();
               }
             2.执行
                public E removeFirst() {
                    final Node<E> f = first;
                    if (f == null)
                        throw new NoSuchElementException();
                    return unlinkFirst(f);
                }
             3. 执行unlinkFirst,将f指向的双向链表的第一个结点拿掉
                private E unlinkFirst(Node<E> f) {
                     // assert f == first && f != null;
                     final E element = f.item;
                     final Node<E> next = f.next;
                     f.item = null;
                     f.next = null; // help GC
                     first = next;
                     if (next == null)
                         last = null;
                     else
                         next.prev = null;
                     size--;
                     modCount++;
                     return element;
                 }
          */
    }
 }

7.4 ArrayList和LinkedList的比较

  1. ArrayList和LinkedList的比较

image-20221103233256770

ArrayList改查的效率较高,因为可以通过索引直接定位;LinkedList改查效率较低,会从头开始,一个一个遍历;

  1. 如何选择ArrayList和LinkedList:

  • 如果我们改查的操作多,选择ArrayList;

  • 如果我们增删的操作多,选择LinkedList;

  • 一般来说,在程序中,80%-90%都是查询,因此大多数情况下会选择ArrayList;

  • 在一个项目中,根据业务灵活选择,也可以这样,一个模块下使用的是ArrayList,另一个模块下选择的是LinkedList,也就是说,要根据业务来进行选择;

8. Set接口和常用方法

8.1 Set接口基本介绍

  1. 无序(添加和取出顺序不一致),没有索引;

  2. 不允许重复元素,所以最多包含一个null;

  3. JDKAPI中Set接口的实现类有:

image-20221103234103670

8.2 Set接口的常用方法

和List接口一样,Set接口也是Collection的子接口,因此,常用方法和Collection接口一样;

8.3 Set接口的遍历方式

同Collection的遍历方式一样,因为Set接口是Collection接口的子接口;

  1. 可以使用迭代器

  2. 增强for

  3. 不能使用索引的方式获取

 public class Setmethod {
     public static void main(String[] args) {
 ​
         //1. 以Set接口的实现类,HashSet来讲解Set接口的方法
         //2. set接口的实现类的对象(Set接口对象),不能存放重复的元素,可以添加一个null
         //3. set接口对象存放数据是无序的(即添加的顺序和取出的顺序不一致)
         //4. 注意:取出的顺序虽然不是添加的顺序,但是他是固定的;
         Set set = new HashSet();
         Set set2 = new HashSet();
         set.add("john");
         set.add("lucy");
         set.add("mary");
         set.add("jack");
         set.add(null);
         set.add(null);
         for (int i = 0; i < 3; i++) {
             System.out.println("set=" + set);
        }
 ​
         //删除
         set.remove(null);
         boolean flag = set.equals(5);
         System.out.println(flag);
         //遍历
         //方式1: 使用迭代器
         System.out.println("=========使用迭代器=========");
         Iterator iterator = set.iterator();
         while (iterator.hasNext()) {
             Object obj = iterator.next();
             System.out.println("obj=" + obj);
        }
 ​
         //方式2: 使用增强For
         System.out.println("=========使用增强for=========");
         for (Object o : set) {
             System.out.println("o=" + o);
        }
         //set接口对象,不能通过索引获取
    }
 }

9. Set接口实现类-HashSet

9.1HashSet的全面说明

  1. HashSet实现了Set接口;

  2. HashSet实际上是HashMap,看下源码;

 public HashSet() {
     map = new HashMap<>();
 }
  1. 可以存放null值,但是只能有一个null;

  2. HashSet不保证元素是有序,取决于hash后,在确定索引的结果;(不保证存放元素的顺序和取出顺序一致);

  3. 不能有重复元素/对象,在Set接口使用已经讲过;

9.2 HashSet底层机制说明

分析HashSet底层是HashMap,HashMap底层是(数组+链表+红黑树);

为了方便理解,模拟一个简单的数组+链表结构;

标签:list,System,println,add,集合,public,out
From: https://www.cnblogs.com/wangzheng888/p/16858493.html

相关文章

  • Java集合精选常见面试题
    前言博主只是这篇文章的搬运工,为了加强记忆自己梳理了一遍并扩展了部分内容。集合拓展链接:集合概述&集合之Collection接口-至安-博客园(cnblogs.com)Java集合概览......
  • Java高级架构师-Java基础(集合)
    Java高级架构师-Java基础(集合)集合框架Java.util.CollectionCollection接口中的共性功能1.添加boobleanadd(Objectobj);往该集合中添加元素,一次添加一个bo......
  • 两个整数集合最快算法求交集_F_hawk189_新浪博客
    这是来自腾讯2014年软件开发笔试题: A,B两个整数集合,设计一个算法求他们的交集,尽可能的高效。一、第一种算法,暴力求解,直接采用遍历或者枚举的方法,这种办法最简单易想,但是时......
  • Python基础之列表,元组,字典,集合,推导式,迭代器
    目录1列表1.1定义1.2访问列表中的值1.3更新列表1.4删除列表元素1.5Python列表脚本操作符1.6Python列表截取与拼接1.7嵌套列表1.8列表比较2元组2.1定义2.2创建......
  • Flutter 按钮 大集合
    Flutter按钮大集合......
  • 集合框架及源码解读
    为什么需要学习集合框架?集合:存放多个元素内容框架:底层封装好,提供简单的API给开发人员使用集合框架:JDK帮助我们封装好,可以直接简单使用集合下面让我们看一下这段代码pu......
  • 【JAVA】java中实现map集合的数据存取详解三种方法。Android程序员也是要会写的
    作者:程序员小冰,GitHub主页:​​https://github.com/QQ986945193​​​新浪微博:​​​http://weibo.com/mcxiaobing​​长期维护的Android项目,里面包括常用功能实现,以及知......
  • 集合注入
    集合注入其实在实际的开发中很少用到1.注入数组对象<propertyname="array">  <array>     <value>100</value>     <value>200</value> ......
  • List<T>集合转Map<String, List<T>>
     没描述,直接看结果吧。packagecom.anchnet.sync;importcn.hutool.json.JSONUtil;importlombok.AllArgsConstructor;importlombok.Data;importlombok.NoArgsConstructo......
  • [2022.10.31]集合与数组
    数组与集合1.集合与数组存储数据概述:集合、数组都是对多个数据进行存储操作的结构,简称Java容器。说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt,.jpg,......