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);
}
}
}