容器【基本使用、索引操作、并集、交集、差集】(二)-全面详解(学习总结---从入门到深化)

 

 

目录

 泛型中的通配符

 统配符的上下限定 统配符的上限限定

 通配符的下限限定

泛型局限性和常见错误

List接口介绍

 ArrayList容器的基本使用

ArrayList容器的索引操作

 ArrayList的并集、交集、差集

判断数组是否扩容

Vector容器的基本使用

Vector源码分析


 泛型中的通配符

 无界通配符
“?”表示类型通配符,用于代替具体的类型。它只能在“<>”中使用。可以解决当具体类型不确定的问题。

语法结构

public void showFlag(Generic<?> generic){  }

示例

public class Generic<T> {

  private T  flag;

  public void setFlag(T flag){
    this.flag = flag;
 }

  public T getFlag(){
    return this.flag;
  }
}


public class ShowMsg {
  public void showFlag(Generic<?> generic){
  
  System.out.println(generic.getFlag());
 }
}


public class Test3 {
  public static void main(String[] args) {

    ShowMsg showMsg = new ShowMsg();
    Generic<Integer> generic = new  Generic<>();
    generic.setFlag(20);
    showMsg.showFlag(generic);

    Generic<Number> generic1 = new  Generic<>();
    generic1.setFlag(50);
    showMsg.showFlag(generic1);

    Generic<String> generic2 = new Generic<>();
    generic2.setFlag("old");
    showMsg.showFlag(generic2);
 }
}

实时效果反馈
1.在泛型中,无界通配符使用什么符号来表示?
A !
B ?
C #
D *

答案
1=>B

 统配符的上下限定
 统配符的上限限定

 对通配符的上限的限定:<? extends 类型>  ?实际类型可以是上限限定中所约定的类型,也可以是约定类型的子类型;

语法结构

public void showFlag(Generic<? extends Number> generic){ }

示例

public class ShowMsg {

  public void showFlag(Generic<? extends Number> generic){

        System.out.println(generic.getFlag());
  }
}


public class Test4 {

  public static void main(String[] args) {

    ShowMsg showMsg = new ShowMsg();
    Generic<Integer> generic = new  Generic<>();
    generic.setFlag(20);

    showMsg.showFlag(generic);
    Generic<Number> generic1 = new  Generic<>();

    generic1.setFlag(50);
    showMsg.showFlag(generic1);
 }
}

实时效果反馈
1.对通配符的上限的限定是指
A 实际类型只能是上限限定中所约定的类型;
B 实际类型只能是上限限定中所约定类型的子类型;

C 实际类型可以是上限限定中所约定的类型,也可以是约定类型的子类型;
D 实际类型可以是上限限定中所约定的类型,也可以是约定类型的父类型;

答案
1=>C

 通配符的下限限定

 对通配符的下限的限定:<? super 类型>  ?实际类型可以是下限限定中所约定的类型,也可以是约定类型的父类型;

语法结构

public void showFlag(Generic<? super Integer> generic){ }

示例

public class ShowMsg {

  public void showFlag(Generic<? super Integer> generic){
  
     System.out.println(generic.getFlag());
   }
}


public class Test6 {

  public static void main(String[] args) {

    ShowMsg showMsg = new ShowMsg();
    Generic<Integer> generic = new Generic<>();
    generic.setFlag(20);
    showMsg.showFlag(generic);

    Generic<Number> generic1 = new Generic<>();
    generic1.setFlag(50);
    showMsg.showFlag(generic1);
 }
}

实时效果反馈

1.对通配符的下限的限定是指
A 实际类型只能是下限限定中所约定的类型;
B 实际类型只能是下限限定中所约定类型的子类型;
C 实际类型可以是下限限定中所约定的类型,也可以是约定类型的子类型;
D 实际类型可以是下限限定中所约定的类型,也可以是约定类型的父类型;

答案

1=>D

泛型局限性和常见错误

 泛型主要用于编译阶段,编译后生成的字节码class文件不包含泛型中的类型信息。 类型参数在编译后会被替换成Object,运行时虚拟机并不知道泛型。因此,使用泛型时,如下几种情况是错的:

 实时效果反馈 


1.如下哪个选项是错误的使用泛型?
A Generic
B Generic
C Generic
D Generic

答案
1=>D

二、容器介绍

容器简介

容器,是用来容纳物体、管理物体。生活中,我们会用到各种各样的容器。如锅碗瓢盆、箱子和包等。

 程序中的“容器”也有类似的功能,用来容纳和管理数据。比如,如下新闻网站的新闻列表、教育网站的课程列表就是用“容器”来管理:

开发和学习中需要时刻和数据打交道,如何组织这些数据是我们编程中重要的内容。 我们一般通过“容器”来容纳和管理数据。事实上,我们前面所学的数组就是一种容器,可以在其中
放置对象或基本类型数据。
数组的优势:是一种简单的线性序列,可以快速地访问数组元素,效率高。如果从查询效率和类型检查的角度讲,数组是最好的。
数组的劣势:不灵活。容量需要事先定义好,不能随着需求的变化而扩容。比如:我们在一个用户管理系统中,要把今天注册的所有用户取出来,那么这样的用户有多少个?我们在写程序时是无法确定的。因此,在这里就不能使用数组。

 基于数组并不能满足我们对于“管理和组织数据的需求”,所以我们需要一种更强大、更灵活、容量随时可扩的容器来装载我们的对象。 这就是我们今天要学习的容器,也叫集合(Collection)。

 实时效果反馈

1.Java中容器的作用是什么?
A 容纳数据
B 处理数据
C 生产数据
D 销毁数据

答案
1=>A

 容器的结构

结构图

 单例集合

 双例集合

 实时效果反馈

1.如下哪个接口不是容器接口?
A List
B Set
C Map
D Comparable

答案
1=>D

 单例集合


Collection接口介绍

Collection 表示一组对象,它是集中、收集的意思。Collection接口的两个子接口是List、Set接口。

 

 Collection接口中定义的方法

方法  说明
boolean add(Object element) 增加元素到容器中
boolean remove(Object element) 从容器中移除元素
boolean contains(Object element) 容器中是否包含该元素
int size() 容器中元素的数量
boolean isEmpty() 容器是否为空
void clear() 清空容器中所有元素
Iterator iterator() 获得迭代器,用于遍历所有元素
boolean containsAll(Collection c) 本容器是否包含c容器中的所有元素
boolean addAll(Collection c) 将容器c中所有元素增加到本容器
boolean removeAll(Collection c) 移除本容器和容器c中都包含的元素
boolean retainAll(Collection c) 取本容器和容器c中都包含的元素,移除非交集元素
Object[] toArray() 转化成Object数组

由于List、Set是Collection的子接口,意味着所有List、Set的实现类都有上面的方法。

JDK8之后,Collection接口新增的方法(将在JDK新特性和函数式编程中介绍):

新增方法  说明
removeIf 作用是删除容器中所有满足filter指定条件的元素
stream
parallelStream
stream和parallelStream 分别返回该容器的Stream视图表示,不同之处在于parallelStream()返回并行的Stream,Stream是Java函数式编程的核心类
spliterator 可分割的迭代器,不同以往的iterator需要顺序迭代,Spliterator可以分割为若干个小的迭代器进行并行操作,可以实现多线程操作提高效率

实时效果反馈 

1.如下哪个接口不是Collection接口的子接口?
A List
B Set
C Map
D Queue

答案
1=>C

List接口介绍

 List接口特点

List是有序、可重复的容器。

有序:有序(元素存入集合的顺序和取出的顺序一致)。List中每个元 素都有索引标记。可以根据元素的索引标记(在List中的位置)访问 元素,从而精确控制这些元素。

可重复:List允许加入重复的元素。更确切地讲,List通常允许满足 e1.equals(e2) 的元素重复加入容器。

List接口中的常用方法

除了Collection接口中的方法,List多了一些跟顺序(索引)有关的方 法,参见下表:

 

 ArrayList容器的基本使用

 ArrayList容器的基本使用

 ArrayList是List接口的实现类。是List存储特征的具体实现。 ArrayList底层是用数组实现的存储。 特点:查询效率高,增删效率 低,线程不安全。

public class ArrayListTest {
    public static void main(String[] args) {
        //实例化ArrayList容器
        List<String> list  = new ArrayList<>();
        //添加元素
        boolean flag1 = list.add("oldlu");
        boolean flag2 = list.add("itbz");
        boolean flag3 = list.add("sxt");
        boolean flag4 = list.add("sxt");
        System.out.println(flag1+"\t"+flag2+"\t"+flag3+"\t"+flag4);
        //删除元素
        boolean flag4 = list.remove("oldlu");
        System.out.println(flag4);
        //获取容器中元素的个数
        int size = list.size();
        System.out.println(size);
        //判断容器是否为空
        boolean empty = list.isEmpty();
        System.out.println(empty);
        //容器中是否包含指定的元素
        boolean value = list.contains("itbz");
        System.out.println(value);
        
        
        //清空容器
        list.clear();
        Object[] objects1 = list.toArray();
        System.out.println(Arrays.toString(objects1));
   }
}

ArrayList容器的索引操作

public class ArrayListTest2 {
    public static void main(String[] args) {
        //实例化容器
        List<String> list = new ArrayList<>();

        //添加元素
        list.add("oldlu");
        list.add("itbz");

        //向指定位置添加元素
        list.add(0,"sxt");
        System.out.println("获取元素");
        String value1 = list.get(0);
        System.out.println(value1);
        System.out.println("获取所有元素方式一");

        //使用普通for循环
        for(int i=0;i<list.size();i++){
            System.out.println(list.get(i));
         }
        System.out.println("获取所有元素方式二");

        //使用Foreach循环
        for(String str:list){
            System.out.println(str);
       }
       System.out.println("元素替换");
        list.set(1,"kevin");
        for(String str:list){
            System.out.println(str);
       }
        System.out.println("根据索引位置删除元素);
        String value2 = list.remove(1);
        System.out.println(value2);
        System.out.println("----------------");
        for(String str:list){
            System.out.println(str);
       }
        System.out.println("查找元素第一次出现的位置");
        int value3 = list.indexOf("sxt");
        System.out.println(value3);
        System.out.println("查找元素最后一次出现的位置");
        list.add("sxt");
        for(String str:list){
            System.out.println(str);
       }
        int value4 = list.lastIndexOf("sxt");
        System.out.println(value4);
   }
}

 ArrayList的并集、交集、差集

并集

  //并集操作:将另一个容器中的元素添加到当前容器中
        List<String> a  = new ArrayList<>();
        a.add("a");
        a.add("b");
        a.add("c");

        List<String> b = new ArrayList<>();
        b.add("a");
        b.add("b");
        b.add("c");
        //a并集b
        a.addAll(b);
        for(String str :a){
            System.out.println(str);
       }

交集

        //交集操作:保留相同的,删除不同的
        List<String> a1  = new ArrayList<>();
        a1.add("a");
        a1.add("b");
        a1.add("c");

        List<String> b1 = new ArrayList<>();
        b1.add("a");
        b1.add("d");
        b1.add("e");
        
        //交集操作
        a1.retainAll(b1);
        for(String str :a1){
            System.out.println(str);
       }

差集

         //差集操作:保留不同的,删除相同的
        List<String> a2  = new ArrayList<>();
        a2.add("a");
        a2.add("b");
        a2.add("c");

        List<String> b2= new ArrayList<>();
        b2.add("b");
        b2.add("c");
        b2.add("d");
        a2.removeAll(b2);
        for(String str :a2){
            System.out.println(str);
       }

ArrayList源码分析

ArrayList底层是用数组实现的存储。

成员变量

/**
* Default initial capacity.
*/
   private static final int DEFAULT_CAPACITY = 10;
/**
* The array buffer into which the elements of the ArrayList are stored.
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/

transient Object[] elementData; // nonprivate to simplify nested class access


/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/

private int size;

数组初始大小

/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;

添加元素

/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  //Increments modCount!!
    elementData[size++] = e;
    return true;
}

判断数组是否扩容

//容量检查
private void ensureCapacityInternal(int minCapacity) {  
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//容量确认
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    //判断是否需要扩容,数组中的元素个数-数组长度,如果大于0表明需要扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

数组扩容

/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
  // overflow-conscious code
    int oldCapacity = elementData.length;
  //扩容1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    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);
}

Vector容器的基本使用

Vector底层是用数组实现的,相关的方法都加了同步检查,因此“线 程安全,效率低”。 比如,indexOf方法就增加了synchronized同步 标记。

 

 Vector的使用

Vector的使用与ArrayList是相同的,因为他们都实现了List接口, 对List接口中的抽象方法做了具体实现。

public class VectorTest {
    public static void main(String[] args) {
        //实例化Vector
        List<String> v = new Vector<>();
        v.add("a");
        v.add("b");
        v.add("a");
        for(int i=0;i<v.size();i++){
            System.out.println(v.get(i));
       }
        System.out.println("----------------------");
        for(String str:v){
            System.out.println(str);
       }
   }
}

Vector源码分析

成员变量

/**
* The array buffer into which the components of the vector are
* stored. The capacity of the vector is the length of this array buffer,
* and is at least large enough to contain all the vector's elements.
*
* <p>Any array elements following the last element in the Vector are null.
*
* @serial
*/

protected Object[] elementData;

    /**
     * The number of valid components in this {@code Vector} object.
     * Components {@code elementData[0]} through
     * {@code elementData[elementCount-1]} are the actual items.
     *
     * @serial
     */

protected int elementCount;
    
    /**
     * The amount by which the capacity of the vector is automatically
     * incremented when its size becomes greater than its capacity. If
     * the capacity increment is less than or equal to zero, the capacity
     * of the vector is doubled each time it needs to grow.
     *
     * @serial
     */
protected int capacityIncrement;

构造方法

public Vector() {
    this(10);
}

添加元素

/**
* Appends the specified element to the end of this Vector.
*
* @param e element to be appended to this Vector
* @return {@code true} (as specified by {@link Collection#add})
* @since 1.2
*/

public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}

数组扩容

/**
* This implements the unsynchronized semantics of ensureCapacity.
* Synchronized methods in this class can internally call this
* method for ensuring capacity without incurring the cost of an
* extra synchronization.
*
* @see #ensureCapacity(int)
*/

private void ensureCapacityHelper(int minCapacity) {
    // overflow-conscious code
   //判断是否需要扩容,数组中的元素个数-数组长度,如果大于0表明需要扩容
    
if (minCapacity - elementData.length >0)
        grow(minCapacity);
}
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;

   //扩容2倍
    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);
}

猜你喜欢

转载自blog.csdn.net/m0_58719994/article/details/131669654