一、集合框架概述
1.Collection和Map
java.util.Collection
和java.util.Map
- 集合按照存储结构可以分为两大类,分别是单列集合
Collection
和双列集合Map
- 单列集合就可以按照字面意思理解为一列,双列就是有两列嘛
key->value
2.Collection集合
- 上述图片应该非常简单清晰明了的说明了
Collection
集合的结构和之间的区别 Collection
是单列集合的根接口- 它有两个重要的子接口
java.util.List
:元素有序可重复java.util.Set
:元素无序不可重复
- 子接口下面就是具体的实现类,就是我们平时使用得最多的那几个
3.Collection集合常用方法
定义了单列集合的一些通用的方法,这些方法适用于上述所有的单例集合。注意每个方法的返回值。另外注意集合的使用过程中是一种多态形式,同一个接口由不同的实现类来实现。
public boolean add(E e)
: 把给定的对象添加到当前集合中 。public void clear()
:清空集合中所有的元素。public boolean remove(E e)
: 把给定的对象在当前集合中删除。public boolean contains(E e)
: 判断当前集合中是否包含给定的对象。public boolean isEmpty()
: 判断当前集合是否为空。public int size()
: 返回集合中元素的个数。public Object[] toArray()
: 把集合中的元素,存储到数组中。
二、迭代器
java.util.Iterator
通用的取出集合中元素的方法。
1.常用方法
java.util.Iterator
接口:迭代器(对集合进行遍历)- 有两个常用的方法
boolean hasNext()
如果仍有元素可以迭代,则返回 true。 判断集合中还有没有下一个元素,有就返回true,没有就返回falseE next()
返回迭代的下一个元素。 取出集合中的下一个元素
2.如何获取迭代器
Iterator
迭代器,是一个接口,我们无法直接使用,需要使用Iterator接口的实现类对象,获取实现类的方式比较特殊- Collection接口中有一个方法,叫iterator(),这个方法返回的就是迭代器的实现类对象
Iterator<E> iterator()
返回在此 collection 的元素上进行迭代的迭代器。
3.使用步骤(掌握)
- 1.使用集合中的方法
iterator()
获取迭代器的实现类对象,使用Iterator
接口接收(多态) - 2.使用
Iterator
接口中的方法hasNext
判断还有没有下一个元素 - 3.使用
Iterator
接口中的方法next
取出集合中的下一个元素
4.使用实例
public static void main(String[] args) {
//创建一个集合对象
Collection<String> coll = new ArrayList<>();
//往集合中添加元素
coll.add("姚明");
coll.add("科比");
coll.add("麦迪");
coll.add("詹姆斯");
coll.add("艾弗森");
/*
1.使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)
注意:
Iterator<E>接口也是有泛型的,迭代器的泛型跟着集合走,集合是什么泛型,迭代器就是什么泛型
*/
//多态 接口 实现类对象
Iterator<String> iterator = coll.iterator();
//遍历迭代器 获取元素
//判断是否还有下一个元素
while (iterator.hasNext()){
//获取当前元素并且指针前移
String e = iterator.next();
System.out.println(e);
}
}
5.迭代器使用原理
三、增强型for循环(更常用)
用来遍历数组和集合。
底层实现就是迭代器,但是用了for循环的格式,简化了迭代器的书写。
1.简介
- Collectionextends Iterable:所有的单列集合都可以使用增强for
- public interface Iterable实现这个接口允许对象成为 “foreach” 语句的目标。
- 增强for循环:用来遍历集合和数组
- 格式:
for(集合/数组的数据类型 变量名: 集合名/数组名){ sout(变量名); }
2.使用实例
public static void main(String[] args) {
demo02();
}
//使用增强for循环遍历集合
private static void demo02() {
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
for(String s : list){
System.out.println(s);
}
}
//使用增强for循环遍历数组
private static void demo01() {
int[] arr = {1,2,3,4,5};
for(int i:arr){
System.out.println(i);
}
}
3.注意点
- 它用于遍历Collection和数组。通常只进行遍历元素,不要在遍历的过程中对集合元素进行增删操作。
- 必须有被遍历的目标。目标只能是Collection或者是数组。新式for仅仅作为遍历操作出现。
四、泛型
1.泛型的概念
2.使用泛型的好处
2.1使用集合对象,不使用泛型会带来的问题
2.1.1实例
public static void main(String[] args) {
show01();
}
private static void show01() {
ArrayList list = new ArrayList();
list.add("abc");
list.add(1);
//使用迭代器遍历list集合
//获取迭代器
Iterator it = list.iterator();
//使用迭代器中的方法hasNext和next遍历集合
while(it.hasNext()){
//取出元素也是Object类型
Object obj = it.next();
System.out.println(obj);
//想要使用String类特有的方法,length获取字符串的长度;不能使用 多态 Object obj = "abc";
//需要向下转型
//会抛出ClassCastException类型转换异常,不能把Integer类型转换为String类型
String s = (String)obj;
System.out.println(s.length());
}
}
- 运行报错
2.1.2注意点
- 集合不使用泛型,默认的类型就是
Object
类型,可以存储任意类型的数据 - 想要使用
String
类特有的方法,length
获取字符串的长度;不能使用 多态Object obj = "abc";
需要向下转型,但是这个转型并不是都能转。因为不清楚预知的类型 - 会抛出ClassCastException类型转换异常,不能把Integer类型转换为String类型
2.2使用集合使用泛型
2.2.1实例
public static void main(String[] args) {
show02();
}
/*
创建集合对象,使用泛型
好处:
1.避免了类型转换的麻烦,存储的是什么类型,取出的就是什么类型
2.把运行期异常(代码运行之后会抛出的异常),提升到了编译期(写代码的时候会报错)
弊端:
泛型是什么类型,只能存储什么类型的数据
*/
private static void show02() {
ArrayList<String> list = new ArrayList<>();
list.add("abc");
//list.add(1);//add(java.lang.String)in ArrayList cannot be applied to (int)
//使用迭代器遍历list集合
Iterator<String> it = list.iterator();
while(it.hasNext()){
String s = it.next();
System.out.println(s+"->"+s.length());
}
}
2.2.2注意点
3.定义和使用含有泛型的类
4.定义和使用含有泛型的方法
4.1定义方法
- 定义含有泛型的方法:泛型定义在方法的修饰符和返回值类型之间
- 格式:
修饰符 <泛型> 返回值类型 方法名(参数列表(使用泛型)){
方法体;
}
- 含有泛型的方法,在调用方法的时候确定泛型的数据类型
- 传递什么类型的参数,泛型就是什么类型
4.2实例
5.定义和使用含有泛型的接口
5.1第一种使用方式
5.2第二种使用方式
6.泛型通配符
6.1基本使用:只能作为方法的参数使用
6.2不能创建对象使用
6.3泛型的上限限定和下限限定(了解)
- 泛型的上限限定:
? extends E
代表使用的泛型只能是E类型的子类/本身 - 泛型的下限限定:
? super E
代表使用的泛型只能是E类型的父类/本身
6.3.1上限
6.3.2下限
五、List集合
java.util.List
接口继承了Collection
接口
1、List接口的三大特点
- 有序
- 有索引,因此包含了一些带索引的方法
- 元素允许重复(Set不允许重复)
2.List接口中带索引的方法(独有)
public void add(int index, E element)
: 将指定的元素,添加到该集合中的指定位置上。public E get(int index)
:返回集合中指定位置的元素。public E remove(int index)
: 移除列表中指定位置的元素, 返回的是被移除的元素。public E set(int index, E element)
:用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
2.1方法使用实例
public static void main(String[] args) {
//创建一个list结合对象 多态
List<String> list = new ArrayList<>();
//添加元素
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("a");
//打印集合
//打印结果不是hashcode 说明重写了toString方法
System.out.println(list);
//指定位置上添加指定元素
//public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。
list.add(3,"three");
System.out.println(list);
//移除指定位置的元素,返回值是被移除的元素
String remove = list.remove(2);
System.out.println("被移除的元素:" + remove);
System.out.println(list);
//指定元素替换集合中指定位置的元素,返回值是更新前的元素
String setE = list.set(4, "four");
System.out.println("被替换的元素为:" + setE);
System.out.println(list);
//List的遍历有三种方式
//1.for循环
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
System.out.println("-----------------");
//2.迭代器
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("-----------------");
//3.增强型for循环
for (String s : list) {
System.out.println(s);
}
}
3.注意:操作索引的时候,一定要防止索引越界异常
IndexOutOfBoundsException
:索引越界异常,集合会报ArrayIndexOutOfBoundsException
:数组索引越界异常StringIndexOutOfBoundsException
:字符串索引越界异常
4.ArrayList实现类
- 大小可变的数组(动态数组)
- 查询快,增删慢
- 线程不同步,线程不安全
5.LinkedList实现类
5.1特点
- 链表实现
- 查询慢,增删快
- 有大量的操作首尾元素的特有方法
- 使用
LinkedList
时,创建对象不建议使用多态
5.2特有方法
-
public void addFirst(E e):将指定元素插入此列表的开头。
-
public void addLast(E e):将指定元素添加到此列表的结尾。
-
public void push(E e):将元素推入此列表所表示的堆栈。
-
public E getFirst():返回此列表的第一个元素。
-
public E getLast():返回此列表的最后一个元素。
-
public E removeFirst():移除并返回此列表的第一个元素。
-
public E removeLast():移除并返回此列表的最后一个元素。
-
public E pop():从此列表所表示的堆栈处弹出一个元素。
-
public boolean isEmpty():如果列表不包含元素,则返回true。
5.3使用实例
public class Demo02LinkedList {
public static void main(String[] args) {
show01();
System.out.println("---------------");
show02();
System.out.println("---------------");
show03();
}
/*
- public void addFirst(E e):将指定元素插入此列表的开头。
- public void addLast(E e):将指定元素添加到此列表的结尾。
- public void push(E e):将元素推入此列表所表示的堆栈。此方法等效于 addFirst(E)。
*/
private static void show01() {
//创建LinkeeList对象
LinkedList<String> linked = new LinkedList<>();
//添加元素
linked.add("a");
linked.add("b");
linked.add("c");
System.out.println(linked);//[a, b, c]
//将指定元素插入到开头
linked.addFirst("first");
System.out.println(linked);
//指定元素插入到末尾
linked.addLast("last");
System.out.println(linked);
//push等价于 addFirst
linked.push("push is equal to the addFirst Method");
System.out.println(linked);
}
/*
- public E getFirst():返回此列表的第一个元素。
- public E getLast():返回此列表的最后一个元素。
*/
private static void show02() {
//创建LinkeeList对象
LinkedList<String> linked = new LinkedList<>();
//添加元素
linked.add("a");
linked.add("b");
linked.add("c");
System.out.println(linked);//[a, b, c]
String first = linked.getFirst();
System.out.println(first);
String last = linked.getLast();
System.out.println(last);
}
/*
- public E removeFirst():移除并返回此列表的第一个元素。
- public E removeLast():移除并返回此列表的最后一个元素。
- public E pop():从此列表所表示的堆栈处弹出一个元素。此方法相当于 removeFirst
*/
private static void show03() {
//创建LinkeeList对象
LinkedList<String> linked = new LinkedList<>();
//添加元素
linked.add("a");
linked.add("b");
linked.add("c");
System.out.println(linked);//[a, b, c]
String s = linked.removeFirst();
System.out.println("移除并返回:" + s);
String s1 = linked.removeLast();
System.out.println("移除并返回:" + s1);
System.out.println(linked);
}
}
6.Vector(了解)
- 底层也是数组
- 同步的,线程安全的
六、Set集合
java.util.Set接口 extends Collection接口
1.特点
- 1.不允许存储重复的元素
- 2.没有索引,没有带索引的方法,也不能使用普通的for循环遍历
2.HashSet(常用来去重)
java.util.HashSet集合 implements Set接口
2.1特点
- 1.不允许存储重复的元素
- 2.没有索引,没有带索引的方法,也不能使用普通的for循环遍历
- 3.是一个无序的集合,存储元素和取出元素的顺序有可能不一致
- 4.底层是一个哈希表结构(查询的速度非常的快)
2.2使用实例
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
//使用add方法往集合中添加元素
set.add(1);
set.add(3);
set.add(2);
set.add(1);
//使用迭代器遍历set集合
Iterator<Integer> it = set.iterator();
while (it.hasNext()){
Integer n = it.next();
System.out.println(n);//1,2,3
}
//使用增强for遍历set集合
System.out.println("-----------------");
for (Integer i : set) {
System.out.println(i);
}
}
3.哈希值
3.1对象哈希值的获取
- 哈希值:是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来得到地址,不是数据实际存储的物理地址)
- 在Object类有一个方法,可以获取对象的哈希值
- hashCode方法的源码:
- public native int hashCode();
- native:代表该方法调用的是本地操作系统的方法
3.2重写hashcode()方法
3.3toString方法
return getClass().getName() + "@" + Integer.toHexString(hashCode());
3.4String类的hashcode()方法
4.HashSet集合存储数据的结构
- jdk1.8版本之前:哈希表=数组加链表
- jdk1.8版本之后:
- 哈希表=数组+链表
- 哈希表=数组+红黑树
5.HashSet不允许存储重复元素的原理
5.1现象观察
5.2原理分析
5.3小结
- HashSet对象在调用add方法的时候,做了两件事
- 首先计算待存储对象的hashcode,并且在哈希表中查找是否存在该hashcode
- 如果不存在该hashcode,则把待存储对象添加到哈希表中
- 如果存在该hashcode,那么就会比较该哈希表中该hashcode位置上已经存储的元素是否和待存储元素相同,如果相同则不会存储,如果不同则会存储。(疑问:如果该hashcode上的元素已经很多了,这个时候需要一一比对,是否会降低效率?如果是,源码是如何解决这个问题的?)
6.HashSet存储自定义类型元素
自定义的类必须重新
equals
方法和hashcode
方法,才能实现存储的元素唯一
6.1自定义类没有重写equal和hashcode方法
6.2自定义类重写equal和hashcode方法
7.LinkedHashSet
继承了 HashSet
java.util.LinkedHashSet集合 extends HashSet集合
LinkedHashSet集合特点:
- 底层是一个哈希表(数组+链表/红黑树)+链表:多了一条链表(记录元素的存储顺序),保证元素有序
- 有序,无重复
七、可变参数
1.场景引入
- 如果我需要一个方法来计算多个整数和
- 但是我事先无法知道要计算多少个整数的和
- 这个时候可以用可变参数来解决这个问题
2.基本原理和使用
- 可变参数:是JDK1.5之后出现的新特性
- 使用前提:
- 当方法的参数列表数据类型已经确定,但是参数的个数不确定,就可以使用可变参数.
- 使用格式:定义方法时使用
修饰符 返回值类型 方法名(数据类型...变量名){}
- 可变参数的原理:
- 可变参数底层就是一个数组,根据传递参数个数不同,会创建不同长度的数组,来存储这些参数
- 传递的参数个数,可以是0个(不传递),1,2…多个
2.1使用实例
3. 可变参数的注意事项
- 1.一个方法的参数列表,只能有一个可变参数
- 2.如果方法的参数有多个,那么可变参数必须写在参数列表的末尾
4.可变参数的特殊写法(某些源码)
八、Collections集合工具类
注意
Collection
是Collection
集合的根接口,而Collections
是工具类,是用来操作集合的一组工具方法
1.往集合添加多个元素和打乱顺序
java.utils.Collections
是集合工具类,用来对集合进行操作。部分方法如下:public static <T> boolean addAll(Collection<T> c, T... elements)
:往集合中添加一些元素。public static void shuffle(List<?> list)
打乱顺序:打乱集合顺序。
2.sort方法
2.1基本使用
2.2对自定义类型的元素排序、Comparable接口
public static <T> void sort(List<T> list):
将集合中元素按照默认规则排序。
- 被排序的集合里边存储的元素,必须实现Comparable,重写接口中的方法compareTo定义排序的规则
- 自己(this)-参数:升序
3.Comparator排序
public static <T> void sort(List<T> list,Comparator<? super T> )
:将集合中元素按照指定规则排序。
3.1 Comparator和Comparable的区别
- Comparable:自己(this)和别人(参数)比较,自己需要实现Comparable接口,重写比较的规则compareTo方法
- Comparator:相当于找一个第三方的裁判,比较两个
- Comparator的排序规则:o1-o2:升序
3.2java原生数据类型的排序
3.3 自定义类型排序(按单个属性)
3.4组合排序
九、Map集合
1特点
- 1.Map集合是一个双列集合,一个元素包含两个值(一个key,一个value)
- 2.Map集合中的元素,key和value的数据类型可以相同,也可以不同
- 3.Map集合中的元素,key是不允许重复的,value是可以重复的
- 4.Map集合中的元素,key和value是一一对应
2.常用接口
2.1HashMap
java.util.HashMap<k,v>
集合implements Map<k,v>
接口
- 1.HashMap集合底层是哈希表:查询的速度特别的快
- JDK1.8之前:数组+单向链表
- JDK1.8之后:数组+单向链表|红黑树(链表的长度超过8):提高查询的速度
- 2.hashMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致
2.2LinkedHashMap
java.util.LinkedHashMap<k,v>
集合extends HashMap<k,v>
集合
- 1.LinkedHashMap集合底层是哈希表+链表(保证迭代的顺序)
- 2.LinkedHashMap集合是一个有序的集合,存储元素和取出元素的顺序是一致的
3.常用方法
public V put(K key, V value)
: 把指定的键与指定的值添加到Map集合中。- 存储键值对的时候,key不重复,返回值V是null
- 存储键值对的时候,key重复,会使用新的value替换map中重复的value,返回被替换的value值
public V remove(Object key)
: 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。public V get(Object key)
根据指定的键,在Map集合中获取对应的值。- key存在,返回对应的value值
- key不存在,返回null
boolean containsKey(Object key)
判断集合中是否包含指定的键。
4.遍历方法1(keySet):通过键找值进行遍历
- 通过
map
的keySet()
方法获取map
中所有的key
并存储在一个set
中 - 那么接下来遍历这个
set
即可- 迭代器
- 增强型for循环
4.1使用实例
public static void main(String[] args) {
//创建Map集合对象
Map<String,Integer> map = new HashMap<>();
map.put("赵丽颖",168);
map.put("杨颖",165);
map.put("林志玲",178);
//1.使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中
Set<String> set = map.keySet();
//2.遍历set集合,获取Map集合中的每一个key
//使用迭代器遍历Set集合
Iterator<String> it = set.iterator();
while (it.hasNext()){
String key = it.next();
//3.通过Map集合中的方法get(key),通过key找到value
Integer value = map.get(key);
System.out.println(key+"="+value);
}
5.遍历方法2(entrySet()):使用Entry对象遍历
Set<Map.Entry<K,V>> entrySet()
返回此映射中包含的映射关系的 Set 视图。- 然后再遍历这个set
Entry
对象提供了两个方法getKey()
和getValue()
5.1使用实例
public static void main(String[] args) {
//创建Map集合对象
Map<String,Integer> map = new HashMap<>();
map.put("赵丽颖",168);
map.put("杨颖",165);
map.put("林志玲",178);
//2.获取entrySet对象
Set<Map.Entry<String, Integer>> set = map.entrySet();
//3.遍历set
Iterator<Map.Entry<String, Integer>> iterator = set.iterator();
while (iterator.hasNext()){
Map.Entry<String, Integer> next = iterator.next();
System.out.println(next.getKey() + "=" + next.getValue());
}
}
6.HashMap存储自定义类型的键值对
6.1自定义类型作为value的时候
使用上没有限制
6.2自定义类型作为key的时候
由于必须保证
key
的唯一性,所以自定义类必须重写hashcode
和equals
方法
7.LinkedHashMap
记住有序即可
8.HashTable
- 底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢
- HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程的集合,速度快
- HashMap集合(之前学的所有的集合):可以存储null值,null键
- Hashtable集合,不能存储null值,null键
- Hashtable和Vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap,ArrayList)取代了
- Hashtable的子类Properties依然活跃在历史舞台
- Properties集合是一个唯一和IO流相结合的集合
public static void main(String[] args) {
HashMap<String,String> map = new HashMap<>();
map.put(null,"a");
map.put("b",null);
map.put(null,null);
System.out.println(map);//{null=null, b=null}
Hashtable<String,String> table = new Hashtable<>();
//table.put(null,"a");//NullPointerException
//table.put("b",null);//NullPointerException
table.put(null,null);//NullPointerException
}