JavaSE进阶-05-集合(重要)


1.主要集合概述

1.1什么是集合?集合的作用?

答:
  集合实际上就是一个容器,可以容纳其他类型的数据(数组其实就是一个集合)。
  集合可以一次容纳多个对象。在实际开发中,假设连接数据库,数据库当中有10条记录,那么如果要把这10条记录查询出来。在java程序中会将10条数据封装成10个java对象,然后将10个java对象放到某一个集合当中,将集合传到前端,然后遍历集合,将数据一个一个展现出来。

1.2集合存储的对象

  集合不能直接存储基本数据类型,另外集合也不能直接存储java对象。集合当中存储的都是java对象的内存地址(存储的引用),集合中任何时候存储的都是引用
  集合在java中本身是一个容器,是一个对象

1.3集合与数据结构

  在java中每一个不同的集合,底层会对应不同的数据结构。往不同的集合中存储元素,等于将数据放到了不同的数据结构当中。
  数据结构:数据存储的结构就是数据结构。不同的数据结构,数据存储方式不同。例如:数组、二叉树、链表、哈希表......
  (注意:java已经将数据结构实现了,写好了常用的集合类。在本章中只需要掌握怎么使用,在什么情况下选择哪一种合适的结合去使用即可。)
new ArrayList();//创建一个集合,底层是数组
new LinkedList();//创建一个集合,底层是链表
new TreeSet();//创建一个集合,底层是二叉树

1.4集合在Java jdk中的位置

  java.util.*
   所有的集合类和集合接口都在java.util包下。

1.5集合继承结构图

Iterator it = "Collection对象".iterator();
所有集合继承Iterable的含义是:所有集合都是可迭代的

总结:

  • ArrayList:底层是数组
  • LinkedList:底层是双向链表。
  • Vector:底层是数组,线程安全的,效率较低,使用较少。
  • HashSet:底层是HashMap,放到HashSet集合中的元素等同于放到HashMap集合key部分了。
  • TreeSet:底层是TreeMap,放到TreeSet集合中的元素等同于放到TreeMap集合key部分了。
  • HashMap:底层是哈希表。
  • Hashtable:底层也是哈希表,只不过线程安全的,效率较低,使用较少。
  • Properties:是线程安全的,并且key和value只能存储字符串String。
  • TreeMap:底层是二叉树。TreeMap集合的key可以自动按照大小顺序排序。

List集合存储元素的特点:
  有序:存进去的顺序和取出来的顺序相同,每一个元素都有下标。
  可重复:存进去1,可以再存储一个1。

Set集合存储元素的特点:
  无序:存进去的顺序和取出来的顺序不同。Set集合元素没有下标。
  不可重复:存进去1,不能再存储一个1。

SortedSet(SortedMap)集合存储元素的特点:
  无序:存进去的顺序和取出来的顺序不同。SortedSet集合元素没有下标。
  不可重复:存进去1,不能再存储一个1。
  可排序:可以按照大小顺序排列。

1.6在java中集合的种类

一类是单个方式存储元素:
  这一类集合中超级父接口:java.util.Collection
一类是以键值对(key-value)的方式存储元素:
  这一类集合中超级父接口:java.util.Map


2.Collection和Iterator

java.util.Collection接口中常用的方法
  1.Collection中能存放什么元素?
  没有使用“泛型”之前,Collection中可以存储Object的所有子类型。
  使用了“泛型”之后,Collection中只能存储某个具体的类型。
(集合中不能直接存储基本数据类型,也不能存储java对象,只是对象的内存地址)
  2.Collection中的常用方法
  boolean add(Object e)向集合中添加元素
   int size() 获取集合中元素的个数
  void clear() 清空集合
  boolean contains(Object o) 判断当前集合中是否包含元素o,包含返回true,不包含返回false
  boolean remove(Object o) 删除集合中的某个元素
   boolean isEmpty() 判断集合中元素的个数是否为空
  Object[] toArray() 调用这个方法可以把集合转换成数组。【作为了解,使用不多】

package com.bjpowernode.javase.collection;

import java.util.ArrayList;
import java.util.Collection;

public class CollectionTest01 {
    public static void main(String[] args) {
        //创建一个集合对象
        /*Collection collection = new Collection() ; 接口是抽象的,无法实例化*/
        //多态
        Collection collection =new ArrayList();
        //测试Collection接口中的常用方法
        collection.add(1200);//自动装箱,实际上是放进去一个对象的内存地址。Integer x=new Integer();
        collection.add(3.14);
        collection.add(new Object());
        collection.add(true);
        collection.add(new Student());

        //获取集合中元素的个数
        System.out.println("集合中元素的个数是:"+collection.size());//5

        //清空集合
        collection.clear();
        System.out.println("清空集合后元素的个数是:"+collection.size());//0

        //再向集合中添加元素
        collection.add("hello");//“hello”对象的内存地址放到了集合当中
        collection.add("world");
        collection.add("浩克");
        collection.add("绿巨人");
        collection.add(1);

        //判断集合中是否包含“绿军人”
        boolean flag=collection.contains("绿巨人");
        System.out.println(flag);//true
        flag=collection.contains("钢铁侠");
        System.out.println(flag);//false

        System.out.println("集合中元素的个数是:"+collection.size());//5
        //删除集合中某个元素
        collection.remove(1);
        System.out.println("删除操作后集合中元素的个数是:"+collection.size());//4

        //判断集合是否为空(集合中是否存在元素)
        System.out.println(collection.isEmpty());//false
        collection.clear();
        System.out.println(collection.isEmpty());//true

        collection.add("abc");
        collection.add("def");
        collection.add(100);
        collection.add("helloworld!");
        collection.add(new Student());

        //转换成数组,仅供了解,使用不多
        Object[] objects=collection.toArray();
        for (int i = 0; i < objects.length; i++) {
            //遍历数组
            Object o=objects[i];
            System.out.println(o);
        }
    }
}

class Student{}

contatins
:boolean contains(Object o):判断集合中是否包含某个对象0,如果包含返回true,如果不包含返回false。

contains方法是用来判断集合中是否包含某个元素的方法,那么它再底层是怎么判断集合是否包含某个元素的呢?

调用了equals方法进行对比。
equals方法返回true,就表示包含这个元素。

package com.bjpowernode.javase.collection;

import java.util.ArrayList;
import java.util.Collection;

public class CollectionTest04 {
   public static void main(String[] args) {
       Collection c = new ArrayList();

       //向集合中存储元素
       String s1=new String("abc");// s1=0x111
       String s2=new String("def");// s2=0x222
       c.add(s1);
       c.add(s2);
       String x=new String("abc");// x=0x555
      System.out.println(c.contains(x));//关键看调没调equals->true
   }
}

  
没有重写equals()就是false👇

package com.bjpowernode.javase.collection;

import java.util.ArrayList;
import java.util.Collection;

public class CollectionTest05 {
   public static void main(String[] args) {
       //创建集合对象
       Collection c=new ArrayList();
       User u1=new User("jack");
       User u2=new User("jack");
       //添加元素
       c.add(u1);
       //判断集合中是否包含u2
       >System.out.println(c.contains(u2));//false
   }
}

class User{
   private String name;
   public User() {}
   public User(String name) {
       this.name = name;
   }
   //没有重写equals方法
}

重写equals()后就是true👇

   //重写equals方法后
   @Override
   public boolean equals(Object o) {
       if (this == o) return true;
       if (o == null || getClass() != o.getClass()) return false;
       User user = (User) o;
       return Objects.equals(name, user.name);
   }

总结:放在集合中的元素要重写equals()

remove()
:boolean remove(Object o) 删除集合中的某个元素

public class CollectionTest05 {
   public static void main(String[] args) {
       //创建集合对象
       Collection c=new ArrayList();
       User u1=new User("jack");
       User u2=new User("jack");
       //添加元素
       c.add(u1);
       //删除u2
       c.remove(u2);
       //判断集合中是否包含u1
       System.out.println(c.contains(u1));//false
       System.out.println(c.contains(u2));//false
   }
}

用remove删除不在集合的u2,结果在集合的u1也没了,证明remove()调用了equals()。

Iterator(重点)

  bollean hasNext():如果仍有元素可以迭代,则返回true。
  Object next(): 返回迭代的下一个元素。
  是所有Collection通用的一种方式,在Map集合中不能用。
存进去是什么类型,取出来还是这个类型。

package com.bjpowernode.javase.collection;

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

public class CollectionTest02 {
    public static void main(String[] args) {
        //创建集合对象
        Collection c = new ArrayList();

        //添加元素
        c.add("abc");
        c.add("def");
        c.add(100);
        c.add(new Object());

        //对集合Collection进行遍历/迭代
        //第一步:获取集合对象的迭代器对象Iterator
        Iterator it = c.iterator();
        //第二部:通过以上获取的迭代器对象开始迭代/遍历集合
        /*
            bollean hasNext():如果仍有元素可以迭代,则返回true
            Object next(): 返回迭代的下一个元素
        */
        while (it.hasNext()){
            Object obj=it.next();
            System.out.println(obj);
        }
    }

}

注意:集合结构只要发送改变,迭代器必须重新获取!
Iterator it=c.iterator();
获取的迭代器对象,迭代器用来遍历集合。此时相当于对当前集合的状态拍了一个快照
迭代器迭代的时候会参照这个快照进行迭代。在迭代的过程中会不断比较快照和原集合的状态,如果原集合状态发生改变,则报错。

迭代过程中使用remove()
集合对象调用remove->报错
  原因:直接通过集合去删除元素,没有通知迭代器,导致原集合与快照状态不一致。
迭代器对象调用remove->正常
  原因:使用迭代器删除元素时,会自动更新迭代器(删除快照的元素),并且更新集合(删除集合中对应的元素),原集合与快照状态一致。

总结:在迭代元素的过程中,一定要使用迭代器Iterator的remove方法删除元素。不要使用集合自带的remove方法删除元素。


3.List

3.1 List集合存储元素的特点:
  有序:存进去的顺序和取出来的顺序相同,每一个元素都有下标(可通过下标遍历)。
  可重复:存进去1,可以再存储一个1

3.2 List集合特有的方法(常用):
  void add(index,E element):在列表的指定位置插入指定元素。//这个方法使用不多,因为对于ArrayList集合来说效率比较低

  E get(int index):根据下标获取元素。

  int indexOf(Object o):获取指定对象第一次出现出的索引。

  int lastIndexOf(Object o):获取指定对象最后一次出现出的索引。

  E remove(int index):删除指定下标的元素。

  E set(int index,E element):修改指定下标的元素。

题外话:
  计算机英语这几个单词要知道:
    增:add、save、new
    删:delete、drop、remove
    改:update、set、modify
    查:find、get、query、select



3.3 ArrayList集合
(1)默认初始化容量10
(底层先创建一个长度为0的数组,当添加第一个元素的时候,初始化容量10)

(2)集合底层是一个Object[]数据

(3)构造方法:
  ArrayList():构造一个初始容量为10的空列表
  ArrayList(int initialCapacity):构造一个具有指定初始容量的空列表
  ArrayList(Collection<? extends E> c):构造一个包含指定collection的元素的列表,这些元素是按照该collection的迭代器返回它们的顺序排列的。

(4)ArrayList集合的扩容:扩容到原容量的1.5倍。
  尽可能少的扩容,因为数组扩容效率比较低。

(5)ArrayList实际使用较多。因为往数组末尾添加元素,效率不受影响。另外,我们检索/查找某个元素的操作比较多。



3.4 LinkedList集合
(1)LinkedList底层采用了双向链表数据结构。
(2)对于链表数据结构来说,随机增删效率高,检索效率低.
(3)链表中的元素在空间存储上,内存地址不连续.

链表优点:
  由于链表上的元素在空间存储上内存地址不连续,所以随机增删元素效率高(因为增删元素不涉及到大量元素位置)。如果遇到随机增删集合中元素的业务比较多时,建议使用LinkedList。

链表缺点:
  不能通过数学表达式计算被查找元素的内存地址,每一次查询某个元素都需要从头节点开始往下遍历,查询效率低。



3.5 Vector集合
(1)底层是一个数组
(2)初始化容量:10
(3)扩容之后是原容量的2倍
(4)Vector是线程安全的



4.Set

Set集合存储元素的特点:
  无序:存进去的顺序和取出来的顺序不同。Set集合元素没有下标。
  不可重复:存进去1,不能再存储一个1。


4.1 HashSet集合

4.2 TreeSet集合

(主要在Map中讲,因为HashSet、TreeSet底层调用了HashMap、TreeMap.相当于HashMap、TreeMap的一个key)



5.Map

(1)Map集合和Collection集合没用关系.
(2)Map集合以key和value的这种键值对的方式存储元素.
(3)key和value都是存储java对象的内存地址.
(4)所有Map集合的key特点:无序不可重复的
(5)Map集合的key和Set集合存储元素特点相同.



5.1 Map接口中的常用方法:

5.1.1  V put(K key,V value):将指定的值与映射中的指定键关联,即向Map集合中添加键值对。

5.1.2  V get(Object key):返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回null。即通过key获取value。

5.1.3  void clear():从此映射中移除所有映射关系。即清空Map集合。

5.1.4  boolean containsKey(Object key):如果此映射包含指定键的映射关系,则返回true。即判断Map中是否包含某个key。

5.1.5  boolean containsValue(Object value):如果此映射将一个或多个键映射到指定值,则返回true。即判断Map中是否包含某个value。
注意:containsValue底层调用的都是equals进行比对的,所以自定义的类型需要重写equals。

5.1.6  boolean isEmpty():如果此映射未包含键-值映射关系,则返回true。即判断Map集合中元素个数是否为0。

5.1.7  Set keySet():返回此映射中包含的键的Set视图。即获取Map集合中所有的key。

5.1.8  V remove(Object key):如果存在一个键的映射关系,则将其从此映射中移除.即通过key删除键值对。

5.1.9  int size():返回此映射中的键-值映射关系数。即获取Map集合中键值对的个数。

5.1.10  Collection<V> values():返回此映射中包含的值的Collection视图.即获取Map集合中所有的value,返回一个Collection。

5.1.11  Set<Map.Entry<K,V>> entrySet(): 即将Map集合转换成Set集合

【注意:Map集合通过entrySet()方法转换成的这个Set集合,Set集合中元素的类型是Map.Entry<K,V>】
【Map.Entry和String一样,都是一种类型的名字。只不过:Map.Entry是Map中的静态内部类。如👇】

public class MyClass {
    public static void main(String[] args) {
        Set<MyMap.MyEntry<Integer,String>> set=new HashSet<>();
    }
}

class MyMap{
    public static class MyEntry<K,V>{
        
    }
}

Map集合的遍历(非常重要!)

遍历Map的第一种方式👇:

思路:因为Map集合的key部分是Set的集合,故可以借助遍历Set集合的思想。先获取Map的key部分,再通过遍历key获取Map的value部分。

package com.bjpowernode.javase.collection;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class MapTest02 {
    public static void main(String[] args) {
        //获取集合对象
        Map<Integer,String> hamap=new HashMap<>();
        //添加元素
        hamap.put(1,"zhangsan");
        hamap.put(2,"lisi");
        hamap.put(3,"wangwu");
        hamap.put(4,"guliu");
        //遍历Map集合
        //获取所有的key,所有的key是一个Set集合
        Set<Integer> keys=hamap.keySet();
        
       //使用迭代器👇遍历key,通过key获取value
        Iterator<Integer> iterator=keys.iterator();
        while (iterator.hasNext()){
            Integer key=iterator.next();
            String value=hamap.get(key);
            System.out.println(key+"="+value);
        }

        //使用增强for循环👇遍历key,通过key获取value
        for (int i:keys
             ) {
            System.out.println(i+"="+hamap.get(i));
        }
    }
}

遍历Map的第二种方式👇:

思路:使用Set<Map.Entry<K,V>> entrySet(),把Map集合直接全部转换成Set集合。
Set集合中元素的类型是:Map.Entry

public class MapTest02 {
    public static void main(String[] args) {
        //获取集合对象
        Map<Integer,String> hamap=new HashMap<>();
        //添加元素
        hamap.put(1,"zhangsan");
        hamap.put(2,"lisi");
        hamap.put(3,"wangwu");
        hamap.put(4,"guliu");
        //Set集合中元素的类型是:Map.Entry
        Set<Map.Entry<Integer,String>> set=hamap.entrySet();
        //遍历Set集合,每一次取出一个Node
        //1.迭代器
        Iterator<Map.Entry<Integer,String>> it2=set.iterator();
        while (it2.hasNext()){
            Map.Entry<Integer,String> node=it2.next();
            Integer key=node.getKey();
            String value=node.getValue();
            System.out.println(key+"="+value);
        }

        //2.增强for循环
        for (Map.Entry<Integer,String> node:set
             ) {
            Integer key=node.getKey();
            String value=node.getValue();
            System.out.println(key+"="+value);
        }
    }
}


总结:第二种方式效率高。因为第一种方式在获取到key后,用key去哈希表找value,这是需要耗费一定时间的。而第二种方式是把key和value放一块了,故大数据量情况下建议使用第二种。



5.2 HashMap集合
①HashMap底层是哈希表/散列表的数据结构。
②哈希表是一个数组和单向链表的结合体。
  数组:在查询方面效率很高,随机增删方面效率低。
  单向链表:在随即增删方面效率较高,在查询方面效率很低。

为什么哈希表的随机增删、以及查询效率都很高
  哈希表将以上的两种数据结构融合在一起,充分发挥它们各自的优点。随机增删在链表上完成,查询不需要全部扫描,只需要部分扫描。


❗重点:由👆图可知,HashMap集合的key会先后调用两个方法,一个方法是hashCode(),一个方法是equals(),那么这两个方法都需要重写

注意:同一个单向链表上所有节点的hash相同,因为它们的数组下表是一样的。但同一个链表上k和k的equals方法返回的是false,即都不想等。

③HashMap集合的key部分特点:
  无序,不可重复。
  为什么无序?因为是随机挂到单向链表上。
  不可重复是怎么保证的?equals方法保证HashMap集合的key不可重复,如果key重复了,value会覆盖。

放在HashMap集合key部分的元素其实就是放在HashSet集合中了。所以HashSet集合中的元素也需要同时重写hashCode()+equals()方法。

④HashMap集合底层的源代码:

public class HashMap{
        //HashMap底层实际上就是一个数组(一维数组)。
        Node<K,V>[] table;

        //静态的内部类HashMap.Node
        static class Node<K,V>{
                final int hash;//哈希值(是key的hashCode方法执行的结果,通过哈希函数/算法可以转换存储成数组的下标)
                final K key;//存储到Map集合中的那个key
                V value;//存储到Map集合中的那个value
                Node<K,V> next;//下一个节点的内存地址。
        }
}


⑤哈希表HashMap使用不当时,无法发挥性能!
  假设将所有的hashCode()方法返回值固定为某个值,那么会导致底层哈希表变成了纯单向链表。这种情况称为:散列分布不均匀

假设将所有的hashCode()方法返回值都设定为不一样的值,会出现什么问题?
  这样的话导致底层哈希表就成为一维数组了,没有链表的概念。也是散列分布不均匀。

散列分布不均匀:
  假设有100个元素,10个单向链表,那么每个单向链表上有10个节点,这是最好的,是散列分布均匀的。

⑥放在HashMap集合key部分的元素,以及放在HashSet集合中的元素,需要同时重写hashCode和equals方法。(重要)

 向Map集合中存,以及从Map集合中取,都是先调用key的hashCode方法,
 然后再调用equals方法!
equals可能调用也可能不调用。
  拿put(p,v)举例,什么时候equals不会调用?
   k.hashCode()方法返回哈希值,哈希值经过哈希算法转换成
   数组下标,数组下标位置上如果是null,equals不需要执行。
  拿get(k)举例,什么时候equals不会调用?
   k.hashCode()方法返回哈希值,哈希值经过哈希算法转换成
   数组下标,数组下标位置上如果是null,equals不需要执行。
(hash值一样,一定在同一个单链表上。在同一个单链表上,hash值不一定相同,称为哈希碰撞。即有可能两个不同的原始值在经过哈希运算后得到同样的结果, 这样就是哈希碰撞。 )

package com.bjpowernode.javase.bean;

import java.util.HashSet;
import java.util.Set;

public class HashMapTest02 {
   public static void main(String[] args) {
       Student s1=new Student("zhangsan");
       Student s2=new Student("zhangsan");
       /*System.out.println(s1.equals(s2));//重写equals方法前false*/

       System.out.println(s1.equals(s2));//重写equals方法前true

       System.out.println("s1的hashCode="+s1.hashCode());//460141958(重写hashCode之后:1432604525)
       System.out.println("s2的hashCode="+s2.hashCode());//1163157884(重写hashCode之后:1432604525)

       //s1.equals(s2)结果是true,表示s1和s2是一样的,那么往HashSet集合中放的话,
       //按理说只能放进去1个。(HashSet集合特点:无序不可重复)
       Set<Student> students=new HashSet<>();
       students.add(s1);
       students.add(s2);
       System.out.println(students.size());//这个结果按理说应该是1,但结果是2,显然不符合hashSet集合存储特点。
   }
}

注意:
 由👆程序运行结果可知,如果一个类的equals方法重写了,那么hashCode()方法必须重写。
 并且equals方法返回如果是true,hashCode()方法返回的值必须一样。

 equals方法返回true表示两个对象相同,在同一个单向链表上比较。那么对于同一个单向链表上的节点来说,它们的哈希值都是相同的。所以hashCode()方法的返回值也应该相同。

⑦HashMap集合的默认初始化容量是16,容量达到75%的时候,数组开始扩容。HashMap集合初始化容量必须是2的倍数,这是要达到散列均匀,提高HashMap集合的存取效率所必需的。

⑧HashMap集合在JDK8之后,如果哈希表单向链表中元素超过8个,单向链表会变成红黑树数据结构。当红黑树上的节点数量小于6时,会重新把红黑树变成单向链表数据结构。这种方式也是为了提高检索效率,二叉树的检索会再次缩小扫描范围,提高效率。



5.3 Hashtable集合

  • Hashtable:底层也是哈希表,只不过线程安全的,效率较低,使用较少。

①Hashtable的key和value都不能为null(HashMap的key和value都是可以为null的)

②Hashtable的初始化容量是11,扩容是原容量*2+1



5.4 Properties集合

  • Properties:是线程安全的,是一个Map集合,继承Hashtable并且key和value只能存储字符串String。Prooerties被称为属性类对象。
package com.bjpowernode.javase.collection;

import java.util.Properties;

public class PropertiesTest01 {
    public static void main(String[] args) {
        //创建一个Properties对象
        Properties pro=new Properties();

        //需要掌握Properties的分两个方法,一个存,一个取。
        pro.setProperty("url","jdbc:mysql://localhost:3306/bjpowernode");
        pro.setProperty("driver","com.mysql.jdbc.Driver");
        pro.setProperty("username","root");
        pro.setProperty("password","123");

        //通过key获取value
        String url=pro.getProperty("url");
        String driver=pro.getProperty("driver");
        String username=pro.getProperty("username");
        String password=pro.getProperty("password");

        System.out.println(url);
        System.out.println(driver);
        System.out.println(username);
        System.out.println(password);
    }
}



5.5 TreeMap集合

  • TreeMap:底层是二叉树。TreeMap集合的key可以自动按照大小顺序排序。



6.Collections工具类

 将非线程安全的ArrayList集合转换成线程安全的👇
 集合工具类:java.util.Collections;

//非线程安全
List myList=new ArrayList();
//转换成线程安全的
Collections.synchronizedList(myList);



7.Comparable与Comparator



8.泛型(重要)

使用泛型的好处:
 ①集合中存储的元素类型统一了.
 ②从集合中取出的元素类型是泛型指定的类型,不需要进行大量的"向下转型".

使用泛型的缺点:
 ①导致集合中存储的元素缺乏多样性.

注意:泛型这种语法机制,只在程序编译阶段起作用,只是给编译器参考的.(运行阶段泛型没用!)

不使用泛型的缺点👇:
从集合中取出的对象,无法调用子类特有方法和父类方法,除非使用instanceof判断,再向下转型

package com.bjpowernode.javase.collection;

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

public class GenericTest01 {
   public static void main(String[] args) {
       //不使用泛型机制,分析程序存在缺点
       List mylist=new ArrayList();
       //准备对象
       Cat c=new Cat();
       Bird b=new Bird();
       //将对象添加到集合中
       mylist.add(c);
       mylist.add(b);
       //遍历集合,取出Cat让它抓老鼠,取出Birl让它飞
       Iterator it=mylist.iterator();
       while (it.hasNext()){
           Object obj=it.next();
           //obj.catchMouse();错误
           //obj.fly();错误
           /*从集合中取出的对象,无法调用其特有方法和其父类方法,
             除非先使用instanceof判断,再向下转型*/
           if (obj instanceof Animal){
               Animal a= (Animal) obj;
               a.move();
           }
       }
   }
}

class Animal{
   //父类自带的方法
   public void move(){
       System.out.println("动物在移动!");
   }
}

class Cat extends Animal{
   public void catchMouse(){
       System.out.println("猫抓老鼠!");
   }
}

class Bird extends Animal{
   public void fly(){
       System.out.println("鸟儿飞翔!");
   }
}

使用泛型后👇:
用泛型指定集合中存储的数据类型
存储其他数据类型编译就会报错,这样集合中元素的数据类型更加统一了.

public class GenericTest01 {
   public static void main(String[] args) {
       //使用JDK5之后的泛型机制
       List<Animal> mylist=new ArrayList<Animal>();
       Cat c=new Cat();
       Bird b=new Bird();
       //使用泛型List<Animal>之后,表示List集合中只允许存储Animal类型的数据
       mylist.add(c);
       mylist.add(b);
       Iterator<Animal> it=mylist.iterator();
       while (it.hasNext()) {
           //使用泛型之后,每一次迭代返回的数据都是Animal类型
           Animal a=it.next();
           //这里不需要进行强制类型转换
           a.move();
       }
   }
}

注意:使用泛型后可以直接调用父类方法.但想调用子类特有方法还需要向下转型. 即👇

    while (it.hasNext()) {
        Animal a=it.next();
        if (a instanceof Bird){
            Bird x= (Bird) a;
            x.fly();
        }
        if (a instanceof Cat){
            Cat y= (Cat) a;
            y.catchMouse();
        }
    }

自动类型推断机制(钻石表达式)

List<父类> myList=new ArrayList<>();
ArrayList<这里的类型自动判断👆>()

public class GenericTest02 {
    public static void main(String[] args) {
        //JDK8之后允许:ArrayList<这里的类型自动判断>()
        List<Animal> myList=new ArrayList<>();
        
        myList.add(new Animal());
        myList.add(new Cat());
        myList.add(new Bird());
    }
}

自定义泛型

自定义泛型的时候,<>尖括号里面的标识符随便写.
 Java源代码中常出现的是:
   E是Element(元素)单词首字母.
   T是Type(类型)单词首字母.
(写了泛型不用,默认是Object类型)

package com.bjpowernode.javase.collection;

public class GenericTest03<自定义泛型随便写> {

    public void doSome(自定义泛型随便写 o){
        System.out.println(o);
    }

    public static void main(String[] args) {
        GenericTest03<String> gt1=new GenericTest03();
        gt1.doSome("abv");

        GenericTest03<Integer> gt2=new GenericTest03();
        gt2.doSome(1);

        MyIterator<String> mi=new MyIterator<>();
        String s1=mi.get();

        MyIterator<Animal> mi2=new MyIterator<>();
        Animal s2=mi2.get();
    }
}

class MyIterator<T>{
    public T get(){
        return null;
    }
}


9.增强for循环(foreach)

for(元素类型 变量名 : 数组或集合){
    循环体
}

foreach缺点:没有下标,在需要使用下标的循环中不建议使用增强for循环.

package com.bjpowernode.javase.collection;

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

public class ForEachTest02 {
    public static void main(String[] args) {
        //创建List集合
        List<String> strList=new ArrayList<>();
        //添加元素
        strList.add("hello");
        strList.add("world");
        strList.add("kitty");
        strList.add("henghengheng");
        //遍历,使用迭代器方式
        Iterator<String> it=strList.iterator();
        while (it.hasNext()){
            String s =it.next();
            System.out.println(s);
        }

        System.out.println("==================");
        //使用下标方式
        for (int i = 0; i <strList.size() ; i++) {
            System.out.println(strList.get(i));
        }

        System.out.println("==================");
        //使用增强for循环
        for (String s: strList
             ) {
            System.out.println(s);
        }
    }
}



笔记来源:B站动力节点Java零基础教程视频

视频链接:https://www.bilibili.com/video/BV1Rx411876f

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容