Java集合类详解

集合

集合类的特点:提供一种存储空间可变的存储模型,存储的数据容量可以随时发生改变。

Collection:单列(接口)

概述:

是单列集合的顶层接口,表示一组对象,这些对象也成为Cpllection的元素

JDK不提供此接口的任何直接实现,它提供更具体的子接口(如List和Set)实现

实现方法

 //创建Collection集合的对象
        Collection<String> c = new ArrayList<String>();

        //添加元素:boolean add (E e)
        c.add("hello");
        c.add("My");
        c.add("java");

        //输出集合对象
        System.out.println(c);

常用方法

方法名 说明
boolean add(E e) 添加元素
boolean remove(Object o) 从集合中移除指定的元素
void clear() 清空集合中的元素
boolean contains(Object o) 判断集合中是否存在指定的元素
boolean isEmpty() 判断集合是否为空
int size() 集合的长度,也就是集合中元素的个数
		Collection<String> c=new ArrayList<>();

        //添加元素
        /*System.out.println(c.add("Hello"));
        System.out.println(c.add("World"));
        System.out.println(c.add("World"));
        */

        /*选择关键字 ctrl+B查看源码
          alt+7 打开一个窗口,查看该类的所有信息
        ArrayList源码:
        public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
        }
        永远返回true */
        c.add("hello");
        c.add("World");
        c.add("World");

        //移除指定元素
        System.out.println(c.remove("World"));//true
        System.out.println(c.remove("world"));//false

        //清空集合所有元素
        c.clear();

        //判断集合中是否存在指定元素
        System.out.println(c.contains("World"));//true
        System.out.println(c.contains("javaSE"));//false

        //判断集合是否为空
        System.out.println(c.isEmpty());

        //查看集合长度
        System.out.println(c.size());//3

        //输出集合对象
        System.out.println(c);

遍历

Iterator:迭代器,集合的专用遍历方式

  • Iterator<E> iterator():返回此集合中的迭代器,通过集合的iterator方法得到

  • 迭代器是通过集合的iterator()方法得到的,所以我们说他是依赖集合而存在的

    常用方法

    E next():返回迭代中的下一个元素

    boolean hasNext():如果迭代器具有更多的元素,返回true

    	    Collection<String> c=new ArrayList<>();
    
            c.add("hello");
            c.add("World");
            c.add("World");
    
            //返回此集合中的迭代器,通过集合的iterator方法得到
            Iterator<String> it = c.iterator();
    
            //源码:
            public Iterator<E> iterator() {
                return new Itr();
            }
            
            private class Itr implements Iterator<E>{
                ...
            }
    
            
            System.out.println(it.next());
            System.out.println(it.next());
            System.out.println(it.next());
            System.out.println(it.next());//抛出异常:NoSuchElementException 元素不存在
     //判断是否还有元素;hasNext()
          if (it.hasNext()){
              System.out.println(it.next());
          }
          if (it.hasNext()){
              System.out.println(it.next());
          }
          if (it.hasNext()){
              System.out.println(it.next());
          }
          if (it.hasNext()){
              System.out.println(it.next());
          }
          
           //用while改进判断
          while(it.hasNext()){
              //System.out.println(it.next());
              String s=it.next();
              System.out.println(s);
          }  
    
    @Description TODO 创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
    
     		Collection<Student> students=new ArrayList<>();
    
            Student s1=new Student("钟无艳",201,21,"男",80.99);
            Student s2=new Student("诸葛亮",202,22,"男",99.99);
            Student s3=new Student("伽罗",203,23,"女",80.80);
    
            students.add(s1);
            students.add(s2);
            students.add(s3);
    
            Iterator<Student> it = students.iterator();
    
            while (it.hasNext()) {
                Student s =it.next();
                System.out.println(s.getName() + "," + s.getId() + "," + s.getAge() + "," + s.getSex() + "," + s.getGrade());
            }
    

List:可重复(接口)

概述

  • 有序集合(也称为序列),用户可以精确控制列表中每个元素的插入位置,用户可以通过整数索引访问元素,并搜索列表中的元素
  • 与Set集合不同,列表通常允许重复的元素
		List<String> l=new ArrayList<>();

        l.add("hello");
        l.add("world");
        l.add("java");
        l.add("world");

        System.out.println(l);

        Iterator<String> it = l.iterator();
        while (it.hasNext()){
            String s=it.next();
            System.out.println(s);
        }

特有方法:

方法名 说明
void add(int index,E element) 在此集合中的指定位置插入指定的元素
E remove(int index) 删除指定索引处的元素,返回被删除的元素
E set(int index,E element) 修改指定索引处的元素,返回被修改的元素
E get(int index) 返回指定索引处的元素
        List<String> list=new ArrayList<>();

        list.add("hello");
        list.add("world");
        list.add("java");

        list.add(1,"javaee");//[hello, javaee, world, java]
        list.add(11,"javaee");//[hello, javaee, world, java] //越界异常IndexOutOfBoundsException

        list.remove(1); //[hello, world, java]

        list.set(1,"javaee");   //[hello, javaee, java]

        System.out.println(list.get(1));    //javaee

        System.out.println(list);

		//用for循环使用get()方法遍历集合
        for (int i = 0; i<list.size(); i++)
        {
            String s=list.get(i);
            System.out.println(s);
        }
@Description TODO 创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合

        List<Student> list = new ArrayList<>();

        Student s1 = new Student("钟无艳", 201, 21, "男", 80.99);
        Student s2 = new Student("诸葛亮", 202, 22, "男", 80.99);
        Student s3 = new Student("伽罗", 203, 23, "男", 80.99);

        list.add(s1);
        list.add(s2);
        list.add(s3);

        Iterator<Student> it = list.iterator();

        while (it.hasNext()) {
            Student s =it.next();
            System.out.println(s.getName() + "," + s.getId() + "," + s.getAge() + "," + s.getSex() + "," + s.getGrade());
        }
        System.out.println("--------------------");

        for (int i = 0; i < list.size(); i++) {
            Student s = list.get(i);
            System.out.println(s.getName() + "," + s.getId() + "," + s.getAge() + "," + s.getSex() + "," + s.getGrade());
        }

并发修改异常

@Description TODO 遍历集合,得到每一个元素,检查是否有“world”这个元素,如果有,就添加一个“javaee”元素		
        List<String> list = new ArrayList<>();

        list.add("hello");
        list.add("world");
        list.add("java");

        Iterator<String> it = list.iterator();

        while (it.hasNext()) {
            String s=it.next();//抛出异常:ConcurrentModificationException:并发修改异常
            if (s.equals("world")){
                list.add("javaee");
            }
        }

		for (int i = 0; i < list.size() ; i++) {
            String s=list.get(i);
            if (s.equals("world")){
                list.add("javaee");
            }
        }//succeed
        System.out.println(list);

源码如下:

public interface List<E>{
    Iterator<E> iterator();
    boolean add(E e);
}

public abstract class AbstractList<E>{
    protected int modCount = 0;
}

public class ArrayList<E> extends AbstractList<E> implements List<E>{
    public E get(int index) {
        Objects.checkIndex(index, size);
        return elementData(index);
    }//没有实际修改集合的次数和预期修改集合的次数的判断

    public boolean add(E e) {
            modCount++;//实际修改集合次数++
            add(e, elementData, size);
            return true;
    }

    public Iterator<E> iterator() {
        return new Itr();
    }

    private class Itr implements Iterator<E> {
        int expectedModCount = modCount;
        /*
            modCount:实际修改集合的次数
            expectedModCount:预期修改集合的次数
            开始都为0
        */

        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)//判断是否相等,modCount++,与expectedModCount不相等
                throw new ConcurrentModificationException();
        }
    }

}

ListIterator :列表迭代器

  • 通过List集合的listIterator()方法得到,所以说它是List集合特有的迭代器

  • 继承于Iterator,用于程序员沿任意方向遍历列表的迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置。

  • 常用方法

    E next() :返回迭代中的下一个元素

    boolean hasNext() :如果迭代具有更多元素,返回true

    E previus :返回列表中上一个元素

    bookean hasPrevious() :如果列表迭代器在相反方向遍历列表时具有更多元素,则返回true

    void add(E e) :将指定的元素插入列表

List<String> list = new ArrayList<>();

        list.add("hello");
        list.add("world");
        list.add("java");

        ListIterator<String> lit = list.listIterator();

        while (lit.hasNext()){
            String s = lit.next();
            System.out.println(s);
        }

        System.out.println("-----------");

        while(lit.hasPrevious()){
            String s =lit.previous();
            System.out.println(s);
        }

        System.out.println("-------------");
        

        ListIterator<String> lit = list.listIterator();

        while (lit.hasNext()) {
            String s= lit.next();
            if(s.equals("world")){
                lit.add("javaee");  //success
            }
        }
        //使用列表迭代器实现添加元素,并无异常
        System.out.println(list);


  • 增强for循环 :简化数组和Collection集合的遍历

    实现Iterable接口的类允许其对象称为增强型for语句的目标

    内部原理是一个Iterator迭代器

    • 格式:

      for(元素数据类型 变量名:数组或Collection集合){
      	//在此处使用变量即可,该变量就是元素
      }
      
      int[] arr={1,2,3,4,5};
      for(int i:arr){
        System.out.println(i);
      }
      
      int[] arr = {1, 2, 3, 4, 5};
      
              for (int i : arr) {
                  System.out.println(i);
              }
      
          System.out.println("--------");
      
          String[] stArray = {"hello", "world", "java"};
          for (String s : stArray) {
              System.out.println(s);
          }
      
          System.out.println("-----------");
          List<String> list=new ArrayList<>();
      
          list.add("hello");
          list.add("world");
          list.add("java");
      
          for (String s:list){
              System.out.println(s);
          }
      
          System.out.println("-----------");
          //内部是一个Iterator迭代器
      
          for (String s:list){
              if (s.equals("world")){
                  list.add("javaee"); //并发修改异常:ConcurrentModificationException;因此内部是一个Iterator迭代器
              }
          }
      

      ​“`

至此,共有三种方式遍历集合:①迭代器—集合特有的遍历方式。②普通for—带有索引的遍历方式。③增强for—最方便的遍历方式。

  • 若只为了遍历,选择增强for的方式
  • 若使用到了索引,则选择普通for的方式

数据结构:

栈:先进后出

队列:先进先出

数组:查询快、增删慢的模型

链表:增删快、查询慢的模型

⭐ArrayList(实现类)

​ ArrayList的底层数据结构是数组 ,查询快、增删慢

LinkedList(实现类)

​ LinkedList的底层数据结构是链表,查询慢,增删快

​ LinkedList集合的特有方法

方法名 说明
public void addFirst(E e) 在该列表开头插入指定元素
public void addLast(E e) 将指定的元素追加到此列表的末尾
public E getFirst() 返回此列表中的第一个元素
public E getLast() 返回此列表中的最后一个元素
public E removeFirst() 从此列表中删除并返回第一个元素
public E removeLast() 从此列表中删除并返回最后一个元素
LinkedList<String> linkedList =new LinkedList<>();

        linkedList.add("hello");
        linkedList.add("world");
        linkedList.add("java");

        System.out.println(linkedList);

        //在该列表开头插入指定元素
        linkedList.addFirst("javaee");
        //将指定的元素追加到此列表的末尾
        linkedList.addLast("javaee");
        //返回此列表中的第一个元素
        System.out.println(linkedList.getFirst());
        //返回此列表中的最后一个元素
        System.out.println(linkedList.getLast());
        //从此列表中删除并返回第一个元素
        System.out.println(linkedList.removeFirst());
        //从此列表中删除并返回最后一个元素
        System.out.println(linkedList.removeLast());

        System.out.println(linkedList);
        //分别使用ArrayList和LinkedList完成存储字符串遍历
		//创建集合对象ArrayList
        ArrayList<String> arrayList = new ArrayList<>();

        arrayList.add("hello");
        arrayList.add("world");
        arrayList.add("java");

        Iterator<String> it = arrayList.iterator();

        while (it.hasNext()) {
            String s = it.next();
            System.out.println(s);
        }
        System.out.println("---------------");
        for (int i = 0; i < arrayList.size(); i++) {
            String s = arrayList.get(i);
            System.out.println(s);
        }
        System.out.println("---------------");
        for (String s : arrayList) {
            System.out.println(s);
        }
        System.out.println("---------------");

        //创建集合对象LinkedList
        LinkedList<String> linkedList = new LinkedList<>();

        linkedList.add("Hello");
        linkedList.add("World");
        linkedList.add("Java");

        Iterator<String> it1 = linkedList.iterator();

        while(it1.hasNext()){
            String s= it1.next();
            System.out.println(s);
        }
        System.out.println("---------------");
        for (int i =0;i<linkedList.size();i++){
            String s= linkedList.get(i);
            System.out.println(s);
        }
        System.out.println("---------------");
        for(String s:linkedList){
            System.out.println(s);
        }
        System.out.println("---------------");
    }

Set:不可重复(接口)

特点:

  • 不包含重复元素的集合
  • 没有带索引的方法,所以不能使用普通for循环遍历

HashSet:实现Set接口,对集合的迭代顺序不作任何保证

        //创建集合对象
        Set<String> set= new HashSet<>();

        set.add("hello");
        set.add("world");
        set.add("java");
        set.add("world");//最终只会添加一个world

        for (String s:set){
            System.out.println(s);//world java hello
        }

哈希值

哈希值是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值

Object类中的public int hashCode()可以获取对象的哈希值

对象的哈希值的特点

  • 同一个对象多次调用hashCode()方法返回的哈希值是相同的

  • 默认情况下,不同对象的哈希值是不相同的

  • 通过方法重写,可以实现让不同对象的哈希值相同

HashSet(实现类)

特点

  • 底层数据结构是哈希表
  • 对集合的迭代顺序不做任何保证,也就是说不保证存储和去除的元素顺序一致
  • 没有带索引的方法,所以不能使用普通for循环遍历
  • 由于是Set集合,所以是不包含重复元素的集合
HashSet<String> hashSet=new HashSet<>();

        hashSet.add("hallo");
        hashSet.add("world");
        hashSet.add("java");

        for (String s :hashSet){
            System.out.println(s);
        }

HashSet集合保证元素唯一性源码分析

HashSet<String> hashSet=new HashSet<>();

hashSet.add("hallo");
hashSet.add("world");
hashSet.add("java");

-------------------------------------------------

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

//hash值和元素的hashCode()方法相关
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    //如果哈希表美亚由初始化,就对其初始化
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    //根据对象的哈希值计算对象的存储位置,如果该位置没有元素就存储元素
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        /*
            存入的元素和以前的元素比较哈希值
                如果哈希值不同,会继续向下执行,把元素添加到集合
                如果哈希值相同,会调用对象的equals()方法进行比较
                    如果返回false,说明元素重复,不存储
        */
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}
LinkedHashSet(实现类)
  • 继承于HashSet
  • 由哈希表和链表实现的Set接口,具有可预测的迭代次序
  • 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
  • 由哈希表保证元素唯一,也就是说没有重复的元素
        LinkedHashSet<String> linkedHashSet=new LinkedHashSet<>();

        linkedHashSet.add("hello");
        linkedHashSet.add("world");
        linkedHashSet.add("java");
        linkedHashSet.add("world");

        for (String s:linkedHashSet){
            System.out.println(s);//hello world java
        }

TreeSet(实现类)

特点:

  • 元素有序,这里的顺序不是指存储和去除的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
    • TreeSet():根据其元素的自然排序进行排序
    • TreeSet(Comparator comparator):根据指定的比较器进行排序
  • 没有带索引的方法,所以不能使用for循环遍历
  • 由于是Set集合,所以不包含重复元素的集合
        //创建集合
        TreeSet<Integer> treeSet=new TreeSet<>();

        treeSet.add(15);
        treeSet.add(44);
        treeSet.add(55);
        treeSet.add(40);
        treeSet.add(10);
        treeSet.add(15);

        for (Integer i:treeSet){
            System.out.print(i+" "); //10 15 40 44 55 自然排序
        }
自然排序Comparable的使用
  • 用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
  • 自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(To)方法
  • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
public class Student implements Comparable<Student> {
  @Override
    public int compareTo(Student s) {
        //return 0;
        //return 1;
        //return -1;
        int num = this.age - s.age;

        int num2 = num == 0 ? this.name.compareTo(s.name) : num;
        return num2;
        ---------------------------------------------------------------------
        return 0:输出结果为duheng,21 认为s2和s1是同一个元素
        return 1:输出结果为duheng,21   love,18   chencui,22  认为s1比s2大,因此按照存储顺序输出
        return -1:输出结果为chencui,22   love,18  duheng,21        认为s2比s1大,因此按照存储顺序逆向输出
        return num = this.age-s.age: love,18  duheng,21   chencui,22  this即为s2,按照升序排列
        return num = s.age-this.age: chencui,22   duheng,21   love,18  this即为s2,按照降序排列

        int num = this.age - s.age;
        int num2 = num == 0 ? this.name.compareTo(s.name) : num;    //年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
        return num2;
    }
}


/*   * 存储学生对象并遍历,创建TreeSet集合使用无参构造方法
     * 按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序*/
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Student> ts = new TreeSet<>();

        Student s1=new Student("dyxx",21);
        Student s2=new Student("love",18);
        Student s3=new Student("zhulu",22);
        Student s4=new Student("sss",18);

        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);

        for (Student s:ts){
            System.out.println(s.getName()+","+s.getAge());//Exception  ClassCastException   Comparable
        }
        //因此需要让Student实现Comparable接口,并重写方法compareTo方法
        
    }
比较器排序Comparator的使用
  • 用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
  • 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compareTo(To1,To2)方法
  • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
        //创建集合对象
        TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                int num = s1.getAge() - s2.getAge();
                int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
                return num2;
            }
        });

        Student s1=new Student("duheng",21);
        Student s2=new Student("chencui",22);
        Student s3=new Student("love",18);

        ts.add(s1);
        ts.add(s2);
        ts.add(s3);

        for (Student s:ts){
            System.out.println(s.getName()+","+s.getAge());//Exception  ClassCastException   Comparable
        }

泛型

将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型,这种参数类型可以用在类、方法和接口中,分别为泛型类、泛型方法、泛型接口

定义格式=

  • <类型>:指定一种类型的格式。这里的类型可以看成时形参
  • <类型1,类型2…>:指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成时形参
  • 将来具体调用时候给定的类型可以看成是实参,并且实参的类型只能是引用数据类型

好处

  • 把运行时期的问题提前到了编译期间
  • 避免了强制类型转换
        //创建集合对象
        //Collection c=new ArrayList();

        Collection<String> c = new ArrayList<>();//100报错:提前报错 迭代器中类型自动为String类型,不需要强转

        c.add("hello"); //将字符串赋值给object——向上转型
        c.add("world");
        c.add("java");
        //c.add(100);//自动封装为Integer

//        Iterator t = c.iterator();
        Iterator<String> t = c.iterator();
        while (t.hasNext()) {
//            Object obj=t.next();
//            System.out.println(obj);
//            String s= (String) t.next();//100:ClassCastException:因此课采用泛型来解决
            String s = t.next();
            System.out.println(s);
        }

泛型类

定义格式

  • 格式:修饰符 class 类名<类型>{}

  • 范例:public class Generic<T>{}

    ​ 此处的T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型

public class GenericClassDemo<T> {
    private  T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}


public static void main(String[] args) {
        Generic<String> g1 = new Generic<String>();
        g1.setT("杜恒");
        System.out.println(g1.getT());
  
        Generic<Integer> g2 = new Generic<Integer>();
        g2.setT(30);
        System.out.println(g1.getT());
    }

泛型方法

定义格式

  • 格式:修饰符<类型> 返回值类型 方法名(类型 变量名){}
  • 范例:public <T> void show(T t){}
/*public class Generic{
    public void show(String s){
        System.out.println(s);
    }
    
    public void show(Integer i){
        System.out.println(i);
    }
	
  	public void show(Boolean b){
        System.out.println(b);
    }*/
    
  	/*public void show(T t){
    	System.out.println(t);
  	}
}
*/
  
/*public class Generic<T>{
  	public<> void show(T t){
    	System.out.println(t);
  	}
}
*/

public class Generic{
  	public <T> void show(T t){	//泛型方法
    	System.out.println(t);
  	}
}

public static void main(String[] args) {
	/*Generic<String> g1 = new Generic<String>();
	g1.show("杜恒");
  
	Generic<Integer> g2 = new Generic<Integer>();
	g1.show(22);
  
	Generic<Boolean> g3 = new Generic<Boolean>();
	g1.show(true);*/
  
  	Generic g = new Generic();
  	g.show("诸葛亮");
  	g.show(21);
  	g.show(true);
  	g.show(12.34);
}

泛型接口

定义格式

  • 格式:修饰符interface 接口名<类型>{}
  • 范例:public interface Generic<T>{}
public interface Generic<T> {
    void show(T t);
}

public class GenericImpl<T> implements Generic<T> {
    @Override
    public void show(T t) {
        System.out.println(t);
    }
}

public class GenericInterfaceDemo {
    public static void main(String[] args) {
        Generic<String> g1 = new GenericImpl<>();
        g1.show("诸葛亮");

        Generic<Integer> g2 = new GenericImpl<>();
        g2.show(20);
    }
}

类型通配符

为了表示各种泛型List的父类,可以使用类型通配符

  • 类型通配符:<?>
  • List<?>:表示元素类型未知的List,他的元素可以匹配任何的类型
  • 这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中

如果我们不希望List<?>是任何泛型List的父类,只希望它代表某一类泛型List的父类,可以使用类型通配符的上限

  • 类型通配符上限:<?extends 类型>
  • List<? extends Number>:他表示的类型是Number或者其子类型

除了可以指定类型通配符的上限,也可以指定类型通配符的下限

  • 类型通配符下限:<?super 类型>
  • List<? super Number>:他表示的类型是Number或者其父类型
 //类型通配符List<?>
        List<?> list1 = new ArrayList<Object>();
        List<?> list2 = new ArrayList<Number>();
        List<?> list3 = new ArrayList<Integer>();
        System.out.println("---------");

        //类型通配符上限
        List<? extends Number> list4= new ArrayList<Object>();//上限是Number,不能是Number父类Object
        List<? extends Number> list5 = new ArrayList<Number>();
        List<? extends Number> list6 = new ArrayList<Integer>();

        //类型通配符下限
        List<? super Number> list7 =new ArrayList<Object>();
        List<? super Number> list8 =new ArrayList<Number>();
        List<? super Number> list9 =new ArrayList<Integer>();//下限是Number,不能是Number的子类Integer

可变参数

可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了

  • 格式:修饰符 返回值类型 方法名(数据类型… 变量名){}
  • 范例:public static int sum(int… a){}
    public static void main(String[] args) {
        System.out.println(sum(10, 20));
        System.out.println(sum(10, 20, 30));
        System.out.println(sum(10, 20, 30, 40));

        System.out.println(sum(10, 20, 30, 40, 50));
        System.out.println(sum(10, 20, 30, 40, 50, 60));
        System.out.println(sum(10, 20, 30, 40, 50, 60, 70));
    }

    public static int sum(int... a) {//a是数组
        int sum = 0;

        for (int i : a) {
            sum += i;
        }

        return sum;
    }

	public static int sum(int b,int... a){//一个方法有多个参数,包含可变参数的情况下,可变参数放在最后
        return 0;
    }

    /*public static int sum(int a,int b){
        return a+b;
    }

    public static int sum(int a,int b,int c){
        return a+b+c;
    }

    public static int sum(int a,int b,int c,int d){
        return a+b+c+d;
    }
    */

可变参数的使用

Arrays工具类中有一个静态方法:

  • public static <T> List<T> asList(T... a):返回由指定数组支持的固定大小的列表
  • 返回的集合不能做增删操作,可以修改

List接口中有一个静态方法:

  • public static <E> List<E> of(E... elements):返回包含任意数量元素的不可变列表
  • 返回的集合不能做增删改操作

Arrays工具类中有一个静态方法:

  • public static <E> Set<E> of(E... elements):返回一个包含任意数量元素的不可变集合
  • 在给元素时不能给重复元素
  • 返回的集合不能做增删操作,没有修改方法
        List<String> list = Arrays.asList("hello", "world", "java");//这样做就不用一个个add了

        list.add("javaee"); //UnsupportedOperationException:不支持所请求的操作
        list.remove("java"); //UnsupportedOperationException
        list.set(1,"javaee");//success  数组大小固定,不能添加删除,但是内容可改变

        System.out.println(list);
        

        List<String> list = List.of("hello", "world", "java","world");

        list.add("javaee"); //UnsupportedOperationException:不支持所请求的操作
        list.remove("java"); //UnsupportedOperationException
        list.set(1,"javaee");//UnsupportedOperationException    of:增删改都不可以

        System.out.println(list);

         

        Set<String> set=Set.of("hello", "world", "java","world");   //IllegalArgumentException:非法或不适当的参数
        Set<String> set=Set.of("hello", "world", "java");

        set.add("javaee");//UnsupportedOperationException
        set.remove("javaee");//UnsupportedOperationException
        //set无索引,没有set方法

Map:双列(接口)

概述

  • interface Map<K,V> K:键的类型;V:值的类型
  • 将键映射到值的对象;不能包含重复的键;每个键可以映射到最多一个值
  • 举例:学生的学号和姓名
    • 201 诸葛
    • 202 亮
    • 203 钟无艳

创建Map集合的对象

  • 多态的方式
  • 具体的实现类HashMap
    //创建集合对象
    Map<String,String> map=new HashMap<>();

    map.put("194070201","诸葛亮");//put(K key, V value)将指定的值与此映射中的指定键相关联
    map.put("194070202","钟无艳");
    map.put("194070203","伽罗");
    map.put("194070203","刘");//当键重复时,就会修改元素,替代掉以前的值{194070201=杜恒, 194070202=杜, 194070203=陈}

    System.out.println(map);    //{201=刘, 202=王, 203=李}

Map集合的基本功能

方法名 说明
V put(K key,V value) 添加元素
V remove(Object key) 更具键删除键值对元素
void clear() 移除所有的键值对元素
boolean containsKey(Object key) 判断集合是否包含指定的键
boolean containsValue(Object value) 判断集合是否包含指定的值
boolean isEmpty() 判断集合是否为空
int size() 集合的长度,也就是集合中键值对的个数
        //创建集合对象
        Map<String, String> map = new HashMap<>();

        //添加元素
        map.put("张无忌","赵敏");
        map.put("郭靖","黄蓉");
        map.put("杨过","小龙女");

        //System.out.println(map.remove("郭靖"));//黄蓉       {杨过=小龙女, 张无忌=赵敏}
        //System.out.println(map.remove("郭襄"));//null       {杨过=小龙女, 张无忌=赵敏}

        //map.clear();// {}慎重使用

        //System.out.println(map.containsKey("郭靖"));//true
        //System.out.println(map.containsKey("郭襄"));//false

        //System.out.println(map.containsValue("小龙女"));//true
        //System.out.println(map.containsValue("郭芙"));//false

        System.out.println(map.isEmpty());//false

        System.out.println(map.size());//3

        //输出集合对象
        System.out.println(map);

Map集合的获取功能

方法名 说明
V get(Object key) 根据键获取值
Set<K> keySet() 获取所有键的集合
Collection<V> values() 获取所有值的集合
Set<Map.Empty<K,V>> entrySet() 获取所有键值对对象的集合
        //创建集合对象
        Map<String, String> map = new HashMap<>();

        //添加元素
        map.put("张无忌","赵敏");
        map.put("郭靖","黄蓉");
        map.put("杨过","小龙女");

        System.out.println(map.get("张无忌"));//赵敏
        System.out.println(map.get("张三丰"));//null

        Set<String> keyset = map.keySet();
        for(String key:keyset){
            System.out.println(key);    //杨过    郭靖  张无忌
        }

        Collection<String> values = map.values();

        for (String value:values){
            System.out.println(value);  //小龙女   黄蓉  赵敏
        }

Map集合的遍历

  • 方式1

    我们刚才存储的元素时成对出现的,所以可以把Map看成是一个夫妻对的集合

    遍历思路

    • 把所有的丈夫集中起来
    • 遍历丈夫的集合,获取到每一个丈夫
    • 根据丈夫去找对应的妻子

    转换为Map集合中的操作:

    • 获取所有键的集合,用keySet()方法实现
    • 遍历键去找值,获取到每一个键,用增强for实现
    • 根据键去找值,用get(object key)方法实现
    //创建集合对象
            Map<String, String> map = new HashMap<>();
    
            //添加元素
            map.put("张无忌","赵敏");
            map.put("郭靖","黄蓉");
            map.put("杨过","小龙女");
    
            Set<String> keySet = map.keySet();
            
            for (String key:keySet){
                String value = map.get(key);
                System.out.println(key+","+value);
            }
    
  • 方式2

    我们刚才存储的元素时成对出现的,所以可以把Map看成是一个夫妻对的集合

    遍历思路

    • 获取所有结婚证的集合
    • 遍历结婚证的集合,获取到每一个结婚证
    • 根据结婚证去找对应的丈夫妻子

    转换为Map集合中的操作:

    • 获取所有键值对对象的集合,用entrySet()方法实现

      Set<Map.entry<K,V>> entrySet():获取所有键值对对象的结合

    • 遍历键值对对象的集合,获取到每一个键每一个键值对对象

      用增强for实现,得到每一个Map.Entry

    • 根据键值对对象获取值

      • 用getKey()得到键
      • 用getValue()得到值
     	    //创建集合对象
            Map<String, String> map = new HashMap<>();
    
            //添加元素
            map.put("张无忌", "赵敏");
            map.put("郭靖", "黄蓉");
            map.put("杨过", "小龙女");
    
            Set<Map.Entry<String, String>> entrySet = map.entrySet();
            for (Map.Entry<String, String> s : entrySet) {
                String key = s.getKey();
                String value = s.getValue();
                System.out.println(key + "," + value);
            }
    

HashMap(实现类)

实例:键是Student 值是String

        HashMap<String,Student> hm=new HashMap<>();

        Student s1= new Student("林青霞",30);
        Student s2= new Student("张曼玉",35);
        Student s3= new Student("王祖贤",36);

        hm.put("001",s1);
        hm.put("002",s2);
        hm.put("003",s3);

        Set<String> keySet = hm.keySet();

        for(String key:keySet){
            Student value=hm.get(key);
            System.out.println(key+","+value.getName()+","+value.getAge());
        }

        System.out.println("---------");

        Set<Map.Entry<String, Student>> entries = hm.entrySet();

        for (Map.Entry<String,Student> me:entries){
            String key=me.getKey();
            Student value=me.getValue();
            System.out.println(key+","+value.getName()+","+value.getAge());
        }

实例:键是String 值是Student

public class HashMapDemo {
    public static void main(String[] args) {
        HashMap<Student, String> hm = new HashMap<>();

        Student s1 = new Student("林青霞", 30);
        Student s2 = new Student("汤家凤", 35);
        Student s3 = new Student("张曼玉", 36);
        Student s4 = new Student("张曼玉", 36);

        hm.put(s1, "西安");
        hm.put(s2, "北京");
        hm.put(s3, "重庆");
        hm.put(s3, "重庆");
        hm.put(s4, "上海");

        Set<Student> keySet = hm.keySet();

        for (Student key : keySet) {
            String value = hm.get(key);

            System.out.println(key.getName() + "," + key.getAge() + "," + value);

        }

    }
}

public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return name != null ? name.equals(student.name) : student.name == null;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

hmoban主题是根据ripro二开的主题,极致后台体验,无插件,集成会员系统
自学咖网 » Java集合类详解