集合大家族:
总结下集合大家族,因为每次在回顾这里的时候都是零零乱乱的,现在好好梳理一番~,
集合类的特点就是:提供一种存储空间可变的存储模型,存储的数据容量可以随时发生改变。
集合分为单列集合和双列集合,如图所示:
这张图说明了集合相关的体系,例如,Colleciton集合类是一个接口。我们都知道接口或者抽象类是不能直接创建对象的,所以我们在这里用集合下边的子类去创建对象使用,Set和List。
关于Collection集合中的一些常用方法:
相关代码演示:
package com.yuan.collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* @Author:YuanHaHa
* @Date:2024/4/2 18:23
* 集合中的常用的一些方法。
* add():往集合中添加元素
* remove():移除集合中指定元素
* size():获得集合的长度
* isEmpty():集合是否为空
* clear():清空集合中的内容
* contains():包含指定元素
*/
public class CollectionDemo1 {
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
c.add("hello");
c.add("world");
c.add("java");
// 移除集合中指定元素
c.remove("java");
// size查看元素个数
System.out.println(c.size());
// 清空集合中的内容
c.clear();
// 判断集合中是否包含指定元素
c.contains("hello");
// 集合是否为空
System.out.println(c.isEmpty());
System.out.println(c);
/**
* 遍历集合的专属方式:迭代器遍历
*/
Iterator<String> it = c.iterator();
while (it.hasNext()){
String s = it.next();
System.out.println(s);
}
}
}
同样也能看出了遍历集合的方法采用iterator迭代器来进行遍历,这是集合特有的遍历方式,推荐~
继续看List集合:List类一个接口,常用的有ArrayList,LinkedList。
比如常见的面试题就是:ArrayList和LinkedList有什么区别?
答:ArrayList是用数组来写的,而数组这种数据结构,在内存中是连续的,可以根据get方法很轻松的获取到对应的值,因此查询的效率高,而LinkedList是用双向链表来写的,链表这种数据结构,通常一个链表有两部分组成,一个是值(value), 一个是(指向下一个节点的指针)next。所以链表很适用于增加或者是删除,只需要把next指针移除到被删除节点的下一个位置就好,添加同理。
package com.yuan.collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @Author:YuanHaHa
* @Date:2024/4/2 18:30
* 1.List集合可重复。
* 2.遍历顺序和插入元素顺序一致。
* List集合特有的方法:
* add(int index,E element),在集合中指定位置插入元素。
* remove(int index):删除指定索引处的元素。
* set(int index,Element):修改指定索引处的元素,返回被修改的元素。
* get(int index):返回被指定索引处的元素
*/
public class ArrayListDemo1 {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("hello");
stringList.add("world");
stringList.add("java");
stringList.add("java");
// 指定位置添加元素
stringList.add(1,"javaee");
// 返回被修改的元素
String r = stringList.set(2, "spring");
System.out.println(r);
// 取出指定位置的元素
System.out.println(stringList.get(2));
System.out.println(stringList);
}
}
来介绍下集合中的异常——并发修改异常:
Exception in thread “main” java.util.ConcurrentModificationException
package com.yuan.collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/**
* @Author:YuanHaHa
* @Date:2024/4/2 18:38
* 集合中的并发修改异常:
* Exception in thread "main" java.util.ConcurrentModificationException
* 主要原因就是:通过迭代器遍历的过程中,通过了集合添加了元素就造成了迭代器获取元素的时候
* 源码进行了判断 “预期修改的值” 和 “实际修改的值” 不一致。所以就产生了异常。
* 解决方法用for循环就可以
*/
public class ArrayListDemo2 {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("hello");
stringList.add("world");
stringList.add("java");
stringList.add("java");
// 改用for循环,通过集合的get方法得到元素,然后再添加。因为get方法内部没有判断。
for (int i = 0; i < stringList.size(); i++) {
String s = stringList.get(i);
if (s.equals("world")) {
stringList.add("javaee");
}
}
System.out.println(stringList);
// 还可以通过list集合的列表迭代器对集合进行正反向遍历
ListIterator<String> listIterator = stringList.listIterator();
while (listIterator.hasNext()) {
String s = listIterator.next();
if (s.equals("world")){
listIterator.add("springboot");
}
/**
* 用列表迭代器的add方法也可以实现往集合中添加元素,
* 其底层最终会把实际修改的值赋值给预期修改值。
*/
}
System.out.println(listIterator);
// 反向遍历
while (listIterator.hasPrevious()){
String s = listIterator.previous();
System.out.println(s);
}
/* Iterator<String> it = stringList.iterator();
while (it.hasNext()){
String s = it.next();
if (s.equals("world")) {
stringList.add("javaee");
}
}
System.out.println(stringList);*/
}
}
其实出现了并发修改异常,也就是当我们用集合的iterator迭代器方法进行遍历的时候,其源码进行了判断 “预期修改值” 和 “实际修改的值” 不一样,所以就产生了异常。
解决这种并发修改异常:
1.改用普通for循环来遍历通过集合的get方法区拿元素,然后在进行添加。
2.也可以使用list集合特有的方法,列表迭代器进行遍历。
总结下List集合遍历的几种方式:
1.采用迭代器。
2.采用普通for循环。
3.采用增强for循环。
package com.yuan.collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @Author:YuanHaHa
* @Date:2024/4/2 18:51
* 简单介绍下list集合遍历方式:
* 共有3种:
* 1.集合特有的迭代器遍历方式。
* 2.带有索引的for循环遍历。
* 3.增强for循环遍历。专门遍历数组和集合的。JDK5之后出现的。内部也是用迭代器写的
*/
public class ArrayListDemo3 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("yuanhh1");
list.add("yuanhh2");
list.add("yuanhh3");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
}
System.out.println("=============");
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
System.out.println(s);
}
System.out.println("=============");
for (String s : list){
System.out.println(s);
}
}
}
然后再来看下LinkedList的使用以及常用的方法:
package com.yuan.collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
/**
* @Author:YuanHaHa
* @Date:2024/4/2 19:10
* ArrayList和LinkedList:
* 前者是用数组写的。后者是用双链表写的。
* 既然是用双链表来写的,那么里边就有一些操作链表的方法:
* addFirst():往链表的头部插入新节点。
* addLast():往链表的尾部添加新节点。
* getFirst():获取链表的头节点。
* getLast():获取链表的尾节点。
* removeFirst():移除链表的头节点。
* removeLast():移除链表的尾节点
*/
public class ArrayLinkedList {
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<>();
array.add("hello");
array.add("world");
array.add("java");
Iterator<String> it = array.iterator();
while (it.hasNext()){
String s = it.next();
System.out.println(s);
}
System.out.println("================");
LinkedList<String> linked = new LinkedList<>();
linked.add("yuanhh1");
linked.add("yuanhh2");
linked.add("yuanhh3");
// 往链表的头部添加一个新节点
linked.addFirst("yuanhh");
// 往链表的尾部添加一个新节点
linked.addLast("kangyuanyuan");
// 移除链表中的头节点
String removeFirst = linked.removeFirst();
// 移除链表中的尾节点
String removedLast = linked.removeLast();
// 获取链表的头节点
String first = linked.getFirst();
// 获取链表的尾部节
String last = linked.getLast();
Iterator<String> itt = linked.iterator();
while (itt.hasNext()) {
String s = itt.next();
System.out.println(s);
}
}
}
再来看Set集合,Set集合也是一个接口,是不包含重复元素的集合,在这里一般常用的就是HashSet,LinkedHashSet,TreeSet:
package com.yuan.collection;
import java.util.HashSet;
import java.util.Iterator;
/**
* @Author:YuanHaHa
* @Date:2024/4/2 20:25
* 说下HashSet保证元素唯一的原因:
* 存入的元素和以前的元素比较哈希值:
* 如果哈希值不同,会继续向下执行,把元素添加到集合。
* 如果哈希值相同,则调用对象的equals方法进行比较:
* 如果返回true,会继续向下执行,把元素添加到集合,
* 如果返回false,说明元素重复,不存储
*/
public class HashSetDemo01 {
public static void main(String[] args) {
HashSet<String> hashSet = new HashSet<>();
hashSet.add("hello");
hashSet.add("world");
hashSet.add("java");
// 不包含重复元素
hashSet.add("java");
for (String s : hashSet){
System.out.println(s);
}
}
}
LinkedHashSet:
package com.yuan.collection;
import java.util.LinkedHashSet;
/**
* @Author:YuanHaHa
* @Date:2024/4/2 20:30
* 为啥LinkedHashSet保证元素有序呢?
* 1.哈希表和链表实现的set接口,具有可预测的迭代次序,
* 2.由链表保证元素有序,也就是说元素的存储和取出顺序是一致的。
* 3.由哈希表保证元素唯一,也就是说没有重复的元素。
*/
public class LinkedHashSetDemo {
public static void main(String[] args) {
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("hello");
linkedHashSet.add("world");
linkedHashSet.add("java");
// 重复不能存
linkedHashSet.add("java");
for (String s : linkedHashSet){
System.out.println(s);
}
}
}
TreeSet:
package com.yuan.collection;
import java.util.TreeSet;
/**
* @Author:YuanHaHa
* @Date:2024/4/2 20:41
*/
public class TreeSetDemo1 {
public static void main(String[] args) {
/**
* 如果这里用的是无参构造创建的对象那么就以自然排序来进行输出元素。
* 自然排序就是以下数据从小到大排,程序运行结果:
* 10
* 20
* 30
* 40
* 50
*/
TreeSet<Integer> set = new TreeSet<>();
set.add(10);
set.add(50);
set.add(30);
set.add(40);
set.add(20);
for (Integer i : set){
System.out.println(i);
}
}
}
最后再来总结下这些集合:
List,Set,Map接口下的一些常用集合
ArrayList:
package collection;
import java.util.ArrayList;
import java.util.Iterator;
/**
* @Author:YuanHaHa
* @Date:2024/4/15 15:39
*/
public class ArrayListDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
list.add("java");
list.add("java");
list.add("");
list.add("");
list.add("");
System.out.println(list.size());
Iterator<String> it = list.iterator();
while (it.hasNext()){
String value = it.next();
System.out.println(value);
/**
* 程序运行结果:
* 8
* hello
* world
* java
* java
* java
*
* ArrayList集合特点:有序可重复,可以存空串。
*/
}
}
}
LinkedList:
package collection;
import java.util.Iterator;
import java.util.LinkedList;
/**
* @Author:YuanHaHa
* @Date:2024/4/15 15:42
*/
public class LinkedListDemo {
/**
* List集合特点:有序可重复,可以存空串。
* @param args
*/
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("hello");
list.add("world");
list.add("java");
list.add("java");
list.add("");
list.add("");
System.out.println(list.size());
Iterator<String> it = list.iterator();
while (it.hasNext()){
String value = it.next();
System.out.println(value);
}
}
}
HashMap:
package collection;
import java.util.HashMap;
import java.util.Set;
/**
* @Author:YuanHaHa
* @Date:2024/4/15 15:44
*/
public class HashMapDemo {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
map.put("yuanhaha","1");
map.put("kangyuanyuan","2");
map.put("lisi","3");
map.put("zhangsan","3");
map.put("","");
// map.put("","3");
map.put("","1");
// map.put("lisi","4");
System.out.println(map.size());
Set<String> keySet = map.keySet();
for (String key : keySet) {
String value = map.get(key);
System.out.println(key + "," + value);
/**
* 输出结果:
* lisi,3
* kangyuanyuan,2
* yuanhaha,1
* 所以HashMap集合是无序的,并且key不能重复,值可以重复,且key和value都可以为空。
*/
}
}
}
LinkedHashMap:
package collection;
import java.util.LinkedHashMap;
import java.util.Set;
/**
* @Author:YuanHaHa
* @Date:2024/4/15 15:49
*/
public class LinkedHashMapDemo {
public static void main(String[] args) {
LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("yuanshuai","1");
linkedHashMap.put("kangyuanyuan","2");
linkedHashMap.put("lisi","3");
linkedHashMap.put("","");
linkedHashMap.put("1","");
linkedHashMap.put("","1");
linkedHashMap.put("2","1");
System.out.println(linkedHashMap.size());
Set<String> keySet = linkedHashMap.keySet();
for (String key : keySet) {
String value = linkedHashMap.get(key);
System.out.println(key + "," + value);
/**
* 运行结果:
* yuanshuai,1
* kangyuanyuan,2
* lisi,3
* LinkedHashMap有序,v也可以重复,但k不能重复,kv可为空
* 内部通过一个双向链表来保护元素的有序性。
*/
}
}
}
HashSet:
package collection;
import java.util.HashSet;
/**
* @Author:YuanHaHa
* @Date:2024/4/15 16:12
*/
public class HashSetDemo {
public static void main(String[] args) {
HashSet<String> hashSet = new HashSet<>();
hashSet.add("hello");
hashSet.add("hello");
hashSet.add("world");
hashSet.add("");
System.out.println(hashSet.size());
for (String value : hashSet) {
System.out.println(value);
/**
* HashSet无序且不重复,可以为空。
* 内部是哈希表。哈希表回去比较值。然后在比较内容
*/
}
}
}
LinkedHashSet:
package collection;
import java.util.LinkedHashSet;
/**
* @Author:YuanHaHa
* @Date:2024/4/15 16:15
*/
public class LinkedHashSetDemo {
public static void main(String[] args) {
LinkedHashSet<String> set = new LinkedHashSet<>();
set.add("hello");
set.add("world");
set.add("java");
set.add("java");
set.add("");
System.out.println(set.size());
for (String value : set) {
System.out.println(value);
/**
* LinkedHashSet有序,也可以为空,内部使用哈希表和链表维护存储和插入的顺序。
* 哈希表用来去重,链表用来维护插入顺序。
*/
}
}
}
TreeSet:
package collection;
import java.util.TreeSet;
/**
* @Author:YuanHaHa
* @Date:2024/4/15 16:19
*/
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet<String> set = new TreeSet<>();
set.add("hello");
set.add("java");
set.add("world");
set.add("");
System.out.println(set.size());
for (String value : set) {
System.out.println(value);
/**
* TreeSet有序,也可以为空
* 内部通过红黑树来实现有序,红黑树是一种平衡的二叉搜索树。
* 通过自定义自然排序或者比较器来进行排序,保证元素的唯一性和有序性。
*/
}
}
}
TreeMap:
package collection;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* @Author:YuanHaHa
* @Date:2024/4/15 16:05
*/
public class TreeMapDemo {
public static void main(String[] args) {
TreeMap<String, String> treeMap = new TreeMap<>();
treeMap.put("yuanshuai","1");
treeMap.put("kangyuanyuan","2");
treeMap.put("lisi","3");
treeMap.put("as","3");
// treeMap.put("","");
treeMap.put("aa","");
treeMap.put("","bb");
System.out.println(treeMap.size());
Set<Map.Entry<String, String>> entrySet = treeMap.entrySet();
for (Map.Entry<String, String> ma : entrySet){
String key = ma.getKey();
String value = ma.getValue();
System.out.println(key + "," + value);
/**
* TreeMap无序,k不能重复,v可以重复,kv可以为空
*
*/
}
}
}
接下来是个人随便想出来的几个问题:
1.HashMap和LinkedHashMap为啥我们用HashMap的时候比较多呢?
首先我们从基础来说,HashMap是不保证遍历顺序的,也就是说他的遍历是随机的。
而LinkedHashMap是保证遍历顺序的,怎样保证呢,和插入元素顺序一致。
上代码先看下各自遍历的结果~~
package usefulfunction;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Set;
/**
* @Author:YuanHaHa
* @Date:2024/4/1 22:17
*/
public class MapDemo {
public static void main(String[] args) {
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put("张三","1");
hashMap.put("李四","2");
hashMap.put("王五","3");
Set<String> keySet1 = hashMap.keySet();
for (String key : keySet1){
String value = hashMap.get(key);
System.out.println(key + "," + value);
}
System.out.println("-------------");
LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("袁哈哈","1");
linkedHashMap.put("李哈哈","2");
linkedHashMap.put("康哈哈","3");
Set<String> keySet2 = linkedHashMap.keySet();
for (String key : keySet2){
String value = linkedHashMap.get(key);
System.out.println(key + "," + value);
}
}
}
那么这个linkedHashMap是怎样维护顺序的呢?
它是使用了一个双向链表来维护插入顺序或者访问顺序来进行迭代,通过构造函数指定accessOrder参数为true,则迭代顺序将与元素的访问顺序一致,否则迭代顺序将与元素的插入顺序一致。