一、集合框架体系
集合是Java中提供的一种容器,可以用来存储多个数据,根据不同存储方式形成的体系结构,就叫做集合框架体系。
image.png
每一种容器类底层拥有不同的底层算法。
既然数组可以存储多个数据,为什么要出现集合?
数组的长度是固定的,集合的长度是可变的。
使用Java类封装出一个个容器类,开发者只需要直接调用即可,不用再手动创建容器类。
集合中存储的数据,叫做元素,元素只能是对象(引用类型)。
二、 容器的分类
根据容器的存储特点的不同,可以分成三种情况:
image.png
- List(列表):允许记录添加顺序,允许元素重复。
- Set(集合):不记录添加顺序,不允许元素重复。
- Map(映射):容器中每一个元素都包含一对key和value,key不允许重复,value可以重复。严格上说,并不是容器(集合),是两个容器中元素映射关系。
注意:List和Set接口继承于Collection接口,Map接口不继承Collection接口
image.png
- Collection接口:泛指广义上集合,主要表示List和Set两种存储方式。
- List接口:表示列表,规定了允许记录添加顺序,允许元素重复的规范。
- Set接口:表示狭义上集合,规定了不记录添加顺序,不允许元素重复的规范。
- Map接口:表示映射关系,规定了两个集合映射关系的规范。
注意:我们使用的容器接口或类都处于java.util包中。
三、List接口
List接口是Collection接口子接口,List接口定义了一种规范,要求该容器允许**记录元素的添加顺序,也允许元素重复。那么List接口的实现类都会遵循这一种规范。
List集合存储特点:
- 允许元素重复
- 允许记录元素的添加先后顺序
该接口常用的实现类有: - ArrayList类:数组列表,表示数组结构,采用数组实现,开发中使用对多的实现类,
- LinkedList类:链表,表示双向列表和双向队列结构,采用链表实现,使用不多。
- Stack类:栈,表示栈结构,采用数组实现,使用不多。
- Vector类:向量,其实就是古老的ArrayList,采用数组实现,使用不多。
1、List常用API方法
添加操作
boolean add(Object e):将元素添加到列表的末尾
- void add(int index, Object element):在列表的指定位置插入指定的元素
- boolean addAll(Collection c):把c列表中的所有元素添加到当前列表中
删除操作
Object remove(int index):从列表中删除指定索引位置的元素,并返回被删除的元素 - boolean removeAll(Collection c):从此列表中移除c列表中的所有元素
修改操作
Object set(int index, Object ele):修改列表中指定索引位置的元素,返回被替换的旧元素
查询操作
int size():返回当前列表中元素个数 - boolean isEmpty():判断当前列表中元素个数是否为0
Object get(int index):查询列表中指定索引位置对应的元素 - Object[] toArray():把列表对象转换为Object数组
- boolean contains(Object o):判断列表是否存在指定对象
注意,标红的是经常使用的方法。
2、ArrayList类
ArrayList类,基于数组算法的列表,通过查看源代码会发现底层其实就是一个Object数组。
操作List接口常用方法
public class ArrayListDemo1 { public static void main(String[] args) { //创建一个默认长度的列表对象 List list = new ArrayList(); //打印集合中元素的个数 System.out.println("元素数量:"+list.size());//0 //添加操作:向列表中添加4个元素 list.add("Will"); list.add(100); list.add(true); list.add("Lucy"); //查询操作: System.out.println("列表中所有元素:"+list);//输出:[Will, 100, true, Lucy] System.out.println("元素数量:"+list.size());//4 System.out.println("第一个元素:"+list.get(0));//Will //修改操作:把索引为2的元素,替换为wolfcode list.set(2, "wolfcode"); System.out.println("修改后:"+list);//输出:[Will, 100, wolfcode, Lucy] //删除操作:删除索引为1的元素 list.remove(1); System.out.println("删除后:"+list);//输出:[Will, wolfcode, Lucy] } }
3、LinkedList类
ArrayList类,基于数组算法的列表,通过查看源代码会发现底层其实就是一个Object数组。
LinkedList类,底层采用链表算法,实现了链表,队列,栈的数据结构。无论是链表还是队列主要操作的都是头和尾的元素,因此在LinkedList类中除了List接口的方法,还有很多操作头尾的方法。
- void addFirst(Object e) 将指定元素插入此列表的开头。
- void addLast(Object e) 将指定元素添加到此列表的结尾。
- Object getFirst() 返回此列表的第一个元素。
- Object getLast() 返回此列表的最后一个元素。
- Object removeFirst() 移除并返回此列表的第一个元素。
- Object removeLast() 移除并返回此列表的最后一个元素。
- boolean offerFirst(Object e) 在此列表的开头插入指定的元素。
- boolean offerLast(Object e) 在此列表末尾插入指定的元素。
- Object peekFirst() 获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。
- Object peekLast() 获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null。
- Object pollFirst() 获取并移除此列表的第一个元素;如果此列表为空,则返回 null。
- Object pollLast() 获取并移除此列表的最后一个元素;如果此列表为空,则返回 null。
- void push(Object e) 将元素推入此列表所表示的栈。
- Object pop() 从此列表所表示的栈处弹出一个元素。
- Object peek() 获取但不移除此列表的头(第一个元素)。
LinkedList之所以有这么多方法,是因为自身实现了多种数据结构,而不同的数据结构的操作方法名称不同,在开发中LinkedList使用不是很多,知道存储特点就可以了。
public class LinkedListDemo { public static void main(String[] args) { LinkedList list = new LinkedList(); //添加元素 list.addFirst("A"); list.addFirst("B"); System.out.println(list); list.addFirst("C"); System.out.println(list); list.addLast("D"); System.out.println(list); //获取元素 System.out.println("获取第一个元素:" + list.getFirst());//C System.out.println("获取最后一个元素:" + list.getLast());//D //删除元素 list.removeFirst(); System.out.println("删除第一个元素后:" + list);//[B, A, D] list.removeLast(); System.out.println("删除最后一个元素后:" + list);//[B, A] } }
4、Stack和Vector类
Vector类:基于数组算法实现的列表,其实就是ArrayList类的前身。和ArrayList的区别在于方法使用synchronized修饰,所以相对于ArrayList来说,线程安全,但是效率就低了点。
Stack类:表示栈,是Vector类的子类,具有后进先出(LIFO)的特点,拥有push(入栈),pop(出栈)方法。
四、集合元素迭代
1、集合元素遍历
对集合中的每一个元素获取出来。
List<String> list = new ArrayList<>(); list.add("西施"); list.add("王昭君"); list.add("貂蝉"); list.add("杨玉环");
使用for遍历
for (int index = 0; index < list.size(); index++) { String ele = list.get(index); System.out.println(ele); }
使用迭代器遍历
Iterator表示迭代器对象,迭代器中拥有一个指针,默认指向第一个元素之前,
- boolean hasNext():判断指针后是否存在下一个元素
- Object next():获取指针位置下一个元素,获取后指针向后移动一位
Iterator<String> it = list.iterator(); while(it.hasNext()) { String ele = it.next(); System.out.println(ele); }
操作原理如下图:
image.png
使用for-each遍历,推荐
语法:
for(元素类型 变量 : 数组/Iterable实例对象){ //TODO }
这里Iterable实例对象表示,Iterable接口实现类对象,其实就是Collection,不包括Map。
for (String ele : list) { System.out.println(ele); }
2、并发修改异常
需求:在迭代集合时删除集合元素,比如删除王昭君。
List<String> list = new ArrayList<>(); list.add("西施"); list.add("王昭君"); list.add("貂蝉"); list.add("杨玉环"); System.out.println(list); for (String ele : list) { if("王昭君".equals(ele)) { list.remove(ele); } } System.out.println(list);
此时报错java.util.ConcurrentModificationException,并发修改异常。
造成该错误的原因是,不允许在迭代过程中改变集合的长度(不能删除和增加)。如果要在迭代过程中删除元素,就不能使用集合的remove方法,只能使用迭代器的remove方法,此时只能使用迭代器来操作,不能使用foreach。
List<String> list = new ArrayList<>(); list.add("西施"); list.add("王昭君"); list.add("貂蝉"); list.add("杨玉环"); System.out.println(list); //获取迭代器对象 Iterator<String> it = list.iterator(); while(it.hasNext()) { String ele = it.next(); if("王昭君".equals(ele)) { it.remove(); } } System.out.println(list);
五、Set接口
Set是Collection子接口,Set接口定义了一种规范,也就是该容器不记录元素的添加顺序,也不允许元素重复,那么Set接口的实现类都遵循这一种规范。
Set集合存储特点:
- 不允许元素重复
- 不会记录元素的添加先后顺序
Set只包含从Collection继承的方法,不过Set无法记住添加的顺序,不允许包含重复的元素。当试图添加两个相同元素进Set集合,添加操作失败,add()方法返回false。
Set接口定义了一种规范,也就是该容器不记录元素的添加顺序,也不允许元素重复。。
Set接口常用的实现类有: - HashSet类:底层采用哈希表实现,开发中使用对多的实现类,重点。
- TreeSet类:底层采用红黑树实现,可以对集合中元素排序,使用不多
1、HashSet类
HashSet底层采用哈希表实现,元素对象的hashCode值决定了在哈希表中的存储位置。
当往HashSet集合中添加新的元素对象时,先回判断该对象和集合对象中的hashCode值:
- 不等: 直接把该新的对象存储到hashCode指定的位置。
- 相等: 再继续判断新对象和集合对象中的equals做比较。
- 若equals为true:则视为是同一个对象,则不保存。
- 若equals为false:存储在之前对象同槽位的链表上,此时操作比较麻烦,不讨论。
在哈希表中元素对象的hashCode和equals方法的很重要。
每一个存储到好像表中的对象,都得覆盖hashCode和equals方法用来判断是否是同一个对象,一般的,根据对象的字段数据比较来判断,通常情况下equals为true的时候hashCode也应该相等。
Eclipse可以根据对象哪些字段做比较而自动生成hashCode和equals方法。
需求1:操作Set接口常用方法
public class ArrayListDemo2 { public static void main(String[] args) { Set<String> set = new HashSet<>(); //添加操作:向列表中添加4个元素 set.add("Will"); set.add("wolf"); set.add("code"); set.add("Lucy"); //查询操作: System.out.println("集合中所有元素:" + set);//[code, wolf, Will, Lucy] System.out.println("元素数量:" + set.size());//4 System.out.println("是否存在某个元素:" + set.contains("code"));//true System.out.println("是否存在某个元素:" + set.contains("code2"));//false //删除操作:删除code元素 set.remove("code"); System.out.println("删除后:" + set);//[wolf, Will, Lucy] //使用for-each遍历 for (String ele : set) { System.out.println(ele); } //使用迭代器遍历 Iterator<String> it = set.iterator(); while (it.hasNext()) { Object ele = it.next(); System.out.println(ele); } } }
六、Map接口
1、认识Map
2、Map常用方法
添加操作
boolean put(Object key,Object value):存储一个键值对到Map中
- boolean putAll(Map m):把m中的所有键值对添加到当前Map中
删除操作
Object remove(Object key):从Map中删除指定key的键值对,并返回被删除key对应的value
修改操作 - 无专门的方法,可以调用put方法,存储相同key,不同value的键值对,可以覆盖原来的。
查询操作 - int size():返回当前Map中键值对个数
- boolean isEmpty():判断当前Map中键值对个数是否为0.
Object get(Object key):返回Map中指定key对应的value值,如果不存在该key,返回null - boolean containsKey(Object key):判断Map中是否包含指定key
- boolean containsValue(Object value):判断Map中是否包含指定value
Set keySet():返回Map中所有key所组成的Set集合
Collection values():返回Map中所有value所组成的Collection集合 - Set entrySet():返回Map中所有键值对所组成的Set集合
注意,标红的是重度使用的方法。
3、HashMap
HashMap底层基于哈希表算法,Map中存储的key对象的hashCode值决定了在哈希表中的存储位置,因为Map中的key是Set,所以不能保证添加的先后顺序,也不允许重复。
需求1:操作Map接口常用方法
public class HashMapDemo1{ public static void main(String[] args) { Map<String, String> map = new HashMap<>(); map.put("girl1", "西施"); map.put("girl2", "王昭君"); map.put("girl3", "貂蝉"); map.put("girl4", "杨玉环"); System.out.println("map中有多少键值对:"+map.size()); System.out.println(map); System.out.println("是否包含key为girl1:"+map.containsKey("girl1")); System.out.println("是否包含value为貂蝉:"+map.containsValue("貂蝉")); //替换key为girl3的value值 map.put("girl3", "小乔"); System.out.println(map); //删除key为girl3的键值对 map.remove("girl3"); System.out.println(map); } }
Map的迭代遍历:
//获取Map中所有的key Set<String> keys = map.keySet(); System.out.println("Map中所有key:"+keys); //获取Map中所有的value Collection<String> values = map.values(); System.out.println("Map中所有value:"+values); //获取Map中所有的key-value(键值对) Set<Entry<String, String>> entrys = map.entrySet(); for (Entry<String, String> entry : entrys) { String key = entry.getKey(); String value = entry.getValue(); System.out.println(key+"->"+value); }