1. 集合的概述
- 集合是Java中存储对象的一种容器。
- 集合的大小不固定,启动后可以动态变化,类型也可以选择不固定。集合更像气球,
- 集合非常适合做元素的增删操作。
- 注意:集合中只能存储引用类型数据,如果要存基本类型数据可以选用包装类。
2. 集合类体系结构
- Collection单列集合,每个元素(数据)只包含一个值
- Map双列集合,每个元素包含两个值(键值对)
3. Collection集合体系
3.1 Collection集合特点
- List系列集合:
ArrayList、LinkList:有序、可重复、有索引。 - Set系列集合:
HashSet:无序、不重复、无索引;LinkedHashSet:有序、不重复、无索引。
TreeSet:按照大小默认升序排序、不重复、无索引。
3.2 集合对于泛型的支持
- 集合都是支持泛型的,可以在编译阶段约束集合只能操作某种数据类型
注意:集合和泛型都只能支持引用数据类型,不支持基本数据类型,所以集合中存储的元素都认为是对象。
4. Collection集合常用API
public class CollectionDemo {
public static void main(String[] args) {
// HashSet:添加的元素是无序,不重复,无索引。
Collection<String> c = new ArrayList<>();
// 1.添加元素, 添加成功返回true。
c.add("Java");
c.add("HTML");
System.out.println(c.add("HTML"));
c.add("MySQL");
c.add("Java");
System.out.println(c.add("黑马"));
System.out.println(c); // [Java, HTML, HTML, MySQL, Java, 黑马]
// 2.清空集合的元素。
// c.clear();
// System.out.println(c);
// 3.判断集合是否为空 是空返回true,反之false。
System.out.println(c.isEmpty());
// 4.获取集合的大小。
System.out.println(c.size());
// 5.判断集合中是否包含某个元素。
System.out.println(c.contains("Java")); // true
System.out.println(c.contains("java")); // false
System.out.println(c.contains("黑马")); // true
// 6.删除某个元素:如果有多个重复元素默认删除前面的第一个!
System.out.println(c.remove("java")); // false
System.out.println(c);
System.out.println(c.remove("Java")); // true
System.out.println(c);
// 7.把集合转换成数组 [HTML, HTML, MySQL, Java, 黑马]
Object[] arrs = c.toArray();
System.out.println("数组:" + Arrays.toString(arrs));
System.out.println("----------------------拓展----------------------");
Collection<String> c1 = new ArrayList<>();
c1.add("java1");
c1.add("java2");
Collection<String> c2 = new ArrayList<>();
c2.add("赵敏");
c2.add("殷素素");
// addAll把c2集合的元素全部倒入到c1中去。
c1.addAll(c2);
System.out.println(c1);
System.out.println(c2);
}
}
5. Collection集合的遍历方式
5.1 方式一:迭代器
5.1.1 迭代器遍历概述
- 遍历就是一个一个的把容器中的元素访问一遍。
- 迭代器在Java中的代表是Iterator,迭代器是集合的专用遍历方式。
5.2.2 Collection集合获取迭代器
5.2.3 Iterator中的常用方法
public class CollectionDemo01 {
public static void main(String[] args) {
ArrayList<String> lists = new ArrayList<>();
lists.add("赵敏");
lists.add("小昭");
lists.add("素素");
lists.add("灭绝");
System.out.println(lists);
// [赵敏, 小昭, 素素, 灭绝]
// it
// 1、得到当前集合的迭代器对象
Iterator<String> it = lists.iterator();
// String ele = it.next();
// System.out.println(ele);
// System.out.println(it.next());
// System.out.println(it.next());
// System.out.println(it.next());
// System.out.println(it.next()); // NoSuchElementException 出现无此元素异常的错误
// 2、定义while循环
while (it.hasNext()){
String ele = it.next();
System.out.println(ele);
}
System.out.println("----------------------------");
}
}
5.2 方式二:foreach/增强for循环
- 增强for循环:既可以遍历集合也可以遍历数组。
- 它是JDK5之后出现的,其内部原理是一个Iterator迭代器,遍历集合相当于是迭代器的简化写法。
- 实现Iterable接口的类才可以使用迭代器和增强for,Collection接口已经实现了Iterable接口。
格式:
public class CollectionDemo02 {
public static void main(String[] args) {
Collection<String> lists = new ArrayList<>();
lists.add("赵敏");
lists.add("小昭");
lists.add("殷素素");
lists.add("周芷若");
System.out.println(lists);
// [赵敏, 小昭, 殷素素, 周芷若]
// ele
for (String ele : lists){
System.out.println(ele);
}
System.out.println("-----------------------");
double[] scores = {100, 99.5, 59.5};
for (double score : scores) {
System.out.println(score);
// if (score == 59.5){
// score = 100.0; // 修改无意义,不会影响数组的元素值
// }
}
// System.out.println(Arrays.toString(scores));
}
}
5.3 方式三:Lambda表达式
public class CollectionDemo03 {
public static void main(String[] args) {
Collection<String> lists = new ArrayList<>();
lists.add("赵敏");
lists.add("小昭");
lists.add("殷素素");
lists.add("周芷若");
System.out.println(lists);
// [赵敏, 小昭, 殷素素, 周芷若]
// s
// lists.forEach(new Consumer<String>() {
// @Override
// public void accept(String s) {
// System.out.println(s);
// }
// });
// lists.forEach(s -> {
// System.out.println(s);
// });
// lists.forEach(s -> System.out.println(s));
lists.forEach( System.out::println);
}
}
注意:集合中存储的是元素对象的地址。
6. List系列集合
6.1 List系列集合特点
- ArrayList、LinekdList :有序,可重复,有索引。
- 有序:存储和取出的元素顺序一致
- 有索引:可以通过索引操作元素
- 可重复:存储的元素可以重复
6.2 List集合特有方法
- List集合因为支持索引,所以多了很多索引操作的独特api,其他Collection的功能List也都继承了。
6.3 List集合的遍历方式小结
- 迭代器
- 增强for循环
- Lambda表达式
- for循环(因为List集合存在索引)
public class ListDemo02 {
public static void main(String[] args) {
List<String> lists = new ArrayList<>();
lists.add("java1");
lists.add("java2");
lists.add("java3");
/** (1)for循环。 */
System.out.println("-----------------------");
for (int i = 0; i < lists.size(); i++) {
String ele = lists.get(i);
System.out.println(ele);
}
/** (2)迭代器。 */
System.out.println("-----------------------");
Iterator<String> it = lists.iterator();
while (it.hasNext()){
String ele = it.next();
System.out.println(ele);
}
/** (3)foreach */
System.out.println("-----------------------");
for (String ele : lists) {
System.out.println(ele);
}
/** (4)JDK 1.8开始之后的Lambda表达式 */
System.out.println("-----------------------");
lists.forEach(s -> {
System.out.println(s);
});
}
}
6.4 ArrayList集合底层原理
- ArrayList底层是基于数组实现的:根据索引定位元素快,增删需要做元素的移位操作。
- 第一次创建集合并添加第一个元素的时候,在底层创建一个默认长度为10的数组。
6.5 LinkedList集合的底层原理
6.5.1 LinkedList的特点
- 底层数据结构是双链表,查询慢,首尾操作的速度是极快的,所以多了很多首尾操作的特有API。
6.5.2 LinkedList集合的特有功能
public class ListDemo03 {
public static void main(String[] args) {
// LinkedList可以完成队列结构和栈结构(双链表)
// 栈
LinkedList<String> stack = new LinkedList<>();
// 压栈、入栈
stack.push("第1颗子弹"); //stack.addFirst("第1颗子弹");
stack.push("第2颗子弹");
stack.push("第3颗子弹");
stack.push("第4颗子弹");
System.out.println(stack);
// 出栈 弹栈
System.out.println(stack.pop()); // stack.removeFirst()
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack);
// 队列
LinkedList<String> queue = new LinkedList<>();
// 入队
queue.addLast("1号");
queue.addLast("2号");
queue.addLast("3号");
queue.addLast("4号");
System.out.println(queue);
// 出队
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue);
}
}
7. 集合的并发修改异常问题
当我们从集合中找出某个元素并删除的时候可能出现一种并发修改异常问题。
哪些遍历存在问题?
- 迭代器遍历集合且直接用集合删除元素的时候可能出现。
- 增强for循环遍历集合且直接用集合删除元素的时候可能出现。
哪种遍历且删除元素不出问题?
- 迭代器遍历集合但是用迭代器自己的删除方法操作可以解决。
- 使用for循环遍历并删除元素不会存在这个问题。
/**
目标:研究集合遍历并删除元素可能出现的:并发修改异常问题。
*/
public class Test {
public static void main(String[] args) {
// 1、准备数据
ArrayList<String> list = new ArrayList<>();
list.add("黑马");
list.add("Java");
list.add("Java");
list.add("赵敏");
list.add("赵敏");
list.add("素素");
System.out.println(list);
// [黑马, Java, Java, 赵敏, 赵敏, 素素]
// it
// 需求:删除全部的Java信息。
// a、迭代器遍历删除
Iterator<String> it = list.iterator();
// while (it.hasNext()){
// String ele = it.next();
// if("Java".equals(ele)){
// // 删除Java
// // list.remove(ele); // 集合删除会出毛病
// it.remove(); // 删除迭代器所在位置的元素值(没毛病)
// }
// }
// System.out.println(list);
// b、foreach遍历删除 (会出现问题,这种无法解决的,foreach不能边遍历边删除,会出bug)
// for (String s : list) {
// if("Java".equals(s)){
// list.remove(s);
// }
// }
// c、lambda表达式(会出现问题,这种无法解决的,Lambda遍历不能边遍历边删除,会出bug)
// list.forEach(s -> {
// if("Java".equals(s)){
// list.remove(s);
// }
// });
// d、for循环(边遍历边删除集合没毛病,但是必须从后面开始遍历删除才不会出现漏掉应该删除的元素)
for (int i = list.size() - 1; i >= 0 ; i--) {
String ele = list.get(i);
if("Java".equals(ele)){
list.remove(ele);
}
}
System.out.println(list);
}
}
8. Set系列集合
8.1 Set系列集合特点
- HashSet:无序、不重复、无索引
- LinkedHashSet:有序、不重复、无索引
- TreeSet:排序、不重复、无索引
Set集合的功能上基本与Collection的API一致。
8.2 HashSet元素无序的底层原理:哈希表
- HashSet集合底层采取哈希表存储的数据
- 哈希表是一种对于增删改查数据性能都较好的结构。
8.2.1 哈希表的组成
- JDK8开始前,底层使用数组+链表组成
- JDK8开始后,底层采用数组+链表+红黑树组成
8.2.2 哈希值
- 是JDK根据对象的地址,按照某种规则算出来的int类型的数值。
- Object类的API:public int hashCode():返回对象的哈希值
- 对象的哈希值特点:1.同一个对象多次调用hashCode()方法返回的哈希值是相同的;2.默认情况下,不同对象的哈希值是不同的
8.2.3 HashSet1.7版本原理解析:数组+链表+(结合哈希算法)
8.2.4 HashSet1.8版本原理解析
- 底层结构:哈希表(数组、链表、红黑树的结合体)
- 当挂在元素下面的数据过多时,查询性能降低,从JDK8开始后,当链表长度超过8的时候,自动转换为红黑树。
8.3 HashSet元素去重复底层原理
- 如果希望Set集合认为2个内容一样的对象是重复的,必须重写对象的hashCode()和equals()方法
8.4 实现类:LinkedHashSet
- 有序、不重复、无索引
- 这里的有序指的是保证存储和取出的元素顺序一致
- 原理:底层数据结构依然是哈希表,只是每个元素又额外多了一个双链表的机制记录存储的顺序
8.5 实现类:TreeSet
8.5.1 TreeSet集合概述和特点
- 不重复、无索引、可排序
- 可排序:按照元素的大小默认升序(由小到大)排序
- TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好
- 注意:TreeSet集合是一定要排序的,可以将元素按照指定的规则进行排序
8.5.2 TreeSet集合默认的规则
- 对于数值类型:Interger,Double,官方默认按照大小进行升序排序
- 对于字符串类型:默认按照首字符的编号升序排序。
- 对于自定义类型如Student对象,TreeSet无法直接排序。
结论:想要使用TreeSet存储自定义类型,需要指定排序规则
8.5.3 自定义排序规则
- 方式一:让自定义的类(如学生类)实现Comparable接口重写里面的compareTo方法来定制比较规则。
- 方式二:TreeSet集合有参数构造器,可以设置Comparator接口对应的比较器对象,来定制比较规则。
public class Apple implements Comparable<Apple>{
private String name;
private String color;
private double price;
private int weight;
public Apple() {
}
public Apple(String name, String color, double price, int weight) {
this.name = name;
this.color = color;
this.price = price;
this.weight = weight;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
@Override
public String toString() {
return "Apple{" +
"name='" + name + '\'' +
", color='" + color + '\'' +
", price=" + price +
", weight=" + weight +
'}';
}
/**
* 方式一:类自定义比较规则
* @param o
* @return
*/
@Override
public int compareTo(Apple o) {
// 按照重量进行比较的
return this.weight - o.weight; //去掉重量重复的元素
// return this.weight - o.weight >= 0 ? 1 : -1; // 保留重量重复的元素
}
}
public class SetDemo5 {
public static void main(String[] args) {
Set<Integer> sets = new TreeSet<>(); //不重复 无索引 可排序
sets.add(23);
sets.add(24);
sets.add(12);
sets.add(8);
System.out.println(sets);
Set<String> sets1 = new TreeSet<>(); //不重复 无索引 可排序
sets1.add("Java");
sets1.add("Java");
sets1.add("angela");
sets1.add("黑马");
sets1.add("About");
sets1.add("Python");
sets1.add("UI");
sets1.add("UI");
System.out.println(sets1);
System.out.println("--------------------------------------");
// 方式二:优先使用:集合自带比较器对象进行规则定制
// 默认
// Set<Apple> apples = new TreeSet<>(new Comparator<Apple>() {
// @Override
// public int compare(Apple o1, Apple o2) {
// // return o1.getWeight() - o2.getWeight(); // 升序
// // return o2.getWeight() - o1.getWeight(); // 降序
// // 浮点型建议直接使用Double.compare进行比较
// return Double.compare(o2.getPrice(), o1.getPrice()); // 降序
// }
// });
Set<Apple> apples = new TreeSet<> ((o1, o2) -> Double.compare(o2.getPrice(), o1.getPrice())); // 降序
apples.add(new Apple("红富士", "红色", 9.9, 500));
apples.add(new Apple("青苹果", "绿色", 15.9, 300));
apples.add(new Apple("绿苹果", "青色", 29.9, 400));
apples.add(new Apple("黄苹果", "黄色", 9.8, 500));
System.out.println(apples);
}
}
9. 补充知识
9.1 可变参数
9.1.1 可变参数概念
- 可变参数用在形参中可以接收多个数据
- 可变参数的形式:数据类型…参数名称
9.1.2 可变参数的作用
- 传输参数非常灵活,方便。可以不传输参数,可以传输1个或者多个,也可以传输一个数组
- 可变参数在方法内部本质上就是一个数组
9.1.3 可变参数的注意事项
- 一个形参列表中可变参数只能有一个
- 可变参数必须放在形参列表的最后面
9.2 集合工具类Collections
9.2.1 Collections集合工具类概念
- java.utils.Collections:是集合工具类
- Collections并不属于集合,是用来操作集合的工具类
9.2.2 Collections常用的API
9.2.3 Collections排序常用API
- 使用范围:只能对List集合的排序
- 方式一:
注意:本方式不可以直接对自定义类型的List集合排序,除非自定义类型实现了比较规则compara接口 - 方式二:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class CollectionsDemo02 {
public static void main(String[] args) {
List<Apple> apples = new ArrayList<>();
apples.add(new Apple("红富士", "红色", 9.9, 500));
apples.add(new Apple("青苹果", "绿色", 15.9, 300));
apples.add(new Apple("绿苹果", "青色", 29.9, 400));
apples.add(new Apple("黄苹果", "黄色", 9.8, 500));
// Collections.sort(apples); // 方式一:可以的,Apple类已经重写了比较规则
// System.out.println(apples);
// 方式二:sort方法自带比较器对象
// Collections.sort(apples, new Comparator<Apple>() {
// @Override
// public int compare(Apple o1, Apple o2) {
// return Double.compare(o1.getPrice(), o2.getPrice()); // 按照价格排序
// }
// });
Collections.sort(apples, (o1, o2) -> Double.compare(o1.getPrice(), o2.getPrice()));
System.out.println(apples);
}
}
10. Map集合体系
10.1 Map集合概述
- Map集合是一种双列集合,每个元素包含两个数据。
- Map集合的每个元素的格式:key=value(键值对元素)。
- Map集合也被称为“键值对集合”。
- Map集合的完整格式:{key1=value1, key2=value2, key3=value3, …}
10.2 Map集合体系特点
- Map集合的特点都是由键决定的。
- Map集合的键是无序,不重复的,无索引的,值不做要求(可以重复)。
- Map集合后面重复的键对应的值会覆盖前面重复键的值。
- Map集合的键值对都可以为null。
HashMap:元素按照键是无序,不重复,无索引,值不做要求。(与Map体系一致)。
LinkedHashMap:元素按照键是有序,不重复,无索引,值不做要求。
TreeMap:元素按照键是排序,不重复,无索引的,值不做要求。
10.3 Map集合常用API
public class MapDemo {
public static void main(String[] args) {
// 1.添加元素: 无序,不重复,无索引。
Map<String , Integer> maps = new HashMap<>();
maps.put("iphoneX",10);
maps.put("娃娃",20);
maps.put("iphoneX",100);// Map集合后面重复的键对应的元素会覆盖前面重复的整个元素!
maps.put("huawei",100);
maps.put("生活用品",10);
maps.put("手表",10);
// {huawei=100, 手表=10, 生活用品=10, iphoneX=100, 娃娃=20}
System.out.println(maps);
// 2.清空集合
// maps.clear();
// System.out.println(maps);
// 3.判断集合是否为空,为空返回true ,反之!
System.out.println(maps.isEmpty());
// 4.根据键获取对应值:public V get(Object key)
Integer key = maps.get("huawei");
System.out.println(key);
System.out.println(maps.get("生活用品")); // 10
System.out.println(maps.get("生活用品2")); // null
// 5.根据键删除整个元素。(删除键会返回键的值)
System.out.println(maps.remove("iphoneX"));
System.out.println(maps);
// 6.判断是否包含某个键 ,包含返回true ,反之
System.out.println(maps.containsKey("娃娃")); // true
System.out.println(maps.containsKey("娃娃2")); // false
System.out.println(maps.containsKey("iphoneX")); // false
// 7.判断是否包含某个值。
System.out.println(maps.containsValue(100)); //
System.out.println(maps.containsValue(10)); //
System.out.println(maps.containsValue(22)); //
// {huawei=100, 手表=10, 生活用品=10, 娃娃=20}
// 8.获取全部键的集合:public Set<K> keySet()
Set<String> keys = maps.keySet();
System.out.println(keys);
System.out.println("------------------------------");
// 9.获取全部值的集合:Collection<V> values();
Collection<Integer> values = maps.values();
System.out.println(values);
// 10.集合的大小
System.out.println(maps.size()); // 4
// 11.合并其他Map集合。(拓展)
Map<String , Integer> map1 = new HashMap<>();
map1.put("java1", 1);
map1.put("java2", 100);
Map<String , Integer> map2 = new HashMap<>();
map2.put("java2", 1);
map2.put("java3", 100);
map1.putAll(map2); // 把集合map2的元素拷贝一份到map1中去
System.out.println(map1);
System.out.println(map2);
}
}
10.4 Map集合的遍历方式
10.4.1 方式一:键找值
- 先获取Map集合的全部键的Set集合。
- 遍历键的Set集合,然后通过键提取对应值
键找值涉及到的API:
public class MapDemo01 {
public static void main(String[] args) {
Map<String , Integer> maps = new HashMap<>();
// 1.添加元素: 无序,不重复,无索引。
maps.put("娃娃",30);
maps.put("iphoneX",100);
maps.put("huawei",1000);
maps.put("生活用品",10);
maps.put("手表",10);
System.out.println(maps);
// maps = {huawei=1000, 手表=10, 生活用品=10, iphoneX=100, 娃娃=30}
// 1、键找值:第一步:先拿到集合的全部键。
Set<String> keys = maps.keySet();
// 2、第二步:遍历每个键,根据键找值
for (String key : keys) {
int value = maps.get(key);
System.out.println(key + "===>" + value);
}
}
}
10.4.2 方式二:键值对
- 先把Map集合转换成Set集合,Set集合中每个元素都是键值对实体类型了。
- 遍历Set集合,然后提取键以及提取值。
键值对涉及到的API:
public class MapDemo02 {
public static void main(String[] args) {
Map<String , Integer> maps = new HashMap<>();
// 1.添加元素: 无序,不重复,无索引。
maps.put("娃娃",30);
maps.put("iphoneX",100);
maps.put("huawei",1000);
maps.put("生活用品",10);
maps.put("手表",10);
System.out.println(maps);
// maps = {huawei=1000, 手表=10, 生活用品=10, iphoneX=100, 娃娃=30}
/**
maps = {huawei=1000, 手表=10, 生活用品=10, iphoneX=100, 娃娃=30}
👇
使用foreach遍历map集合.发现Map集合的键值对元素直接是没有类型的。所以不可以直接foreach遍历集合。
👇
可以通过调用Map的方法 entrySet把Map集合转换成Set集合形式 maps.entrySet();
👇
Set<Map.Entry<String,Integer>> entries = maps.entrySet();
[(huawei=1000), (手表=10), (生活用品=10), (iphoneX=100), (娃娃=30)]
entry
👇
此时可以使用foreach遍历
*/
// 1、把Map集合转换成Set集合
Set<Map.Entry<String, Integer>> entries = maps.entrySet();
// 2、开始遍历
for(Map.Entry<String, Integer> entry : entries){
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key + "====>" + value);
}
}
}
10.4.3 方式三:Lambda
public class MapDemo03 {
public static void main(String[] args) {
Map<String , Integer> maps = new HashMap<>();
// 1.添加元素: 无序,不重复,无索引。
maps.put("娃娃",30);
maps.put("iphoneX",100);// Map集合后面重复的键对应的元素会覆盖前面重复的整个元素!
maps.put("huawei",1000);
maps.put("生活用品",10);
maps.put("手表",10);
System.out.println(maps);
// maps = {huawei=1000, 手表=10, 生活用品=10, iphoneX=100, 娃娃=30}
// maps.forEach(new BiConsumer<String, Integer>() {
// @Override
// public void accept(String key, Integer value) {
// System.out.println(key + "---->" + value);
// }
// });
maps.forEach(( k, v) -> {
System.out.println(k + "---->" + v);
});
}
}
10.5 Map集合的实现类
10.5.1 HashMap
- HashMap是Map里面的一个类。特点都是由键决定的:无序、不重复、无索引
- 没有额外需要学习的特有方法,直接使用Map里面的方法就可以了
- HashMap跟HashSet底层原理是一模一样的,都是哈希表结构,只是HashMap的每个元素包含两个值而已。
实际上:Set系列集合的底层就是Map实现的,只是Set集合中的元素只要键数据,不要值数据而已。
10.5.2 LinkedHashMap
- 由键决定:有序、不重复、无索引
- 这里的有序指的是保证存储和取出的元素顺序一致
- 原理:底层数据结构依然是哈希表,只是每个键值对元素又额外多了一个双链表的机制记录存储的顺序
10.5.3 TreeMap
- 由键决定特性:不重复、无索引、可排序
- 可排序:按照键数据的大小默认升序(由小到大)排序。只能对键排序
- 注意:TreeMap集合是一定要排序的,可以默认排序,也可以将键按照指定的规则进行排序
1.类实现Comparable接口,重写比较规则
2.集合自定义Comparator比较器对象,重写比较规则 - TreeMap跟TreeSet底层原理是一样的
10.6 集合的嵌套
/**
需求:统计投票人数
*/
public class MapTest4 {
public static void main(String[] args) {
// 1、要求程序记录每个学生选择的情况
// 使用一个Map集合存储。
Map<String, List<String>> data = new HashMap<>();
// 2、把学生选择的数据存入进去
List<String> selects = new ArrayList<>();
Collections.addAll(selects, "A", "C");
data.put("name1", selects);
List<String> selects1 = new ArrayList<>();
Collections.addAll(selects1, "B", "C", "D");
data.put("name2", selects1);
List<String> selects2 = new ArrayList<>();
Collections.addAll(selects2, "A", "B", "C", "D");
data.put("name3", selects2);
System.out.println(data);
// 3、统计每个景点选择的人数
Map<String, Integer> infos = new HashMap<>();
// 4、提取所有人选择的景点的值
Collection<List<String>> values = data.values();
System.out.println(values);
// values = [[A, B, C, D], [B, C, D], [A, C]]
for (List<String> value : values) {
for (String s : value) {
// 有没有包含这个景点
if(infos.containsKey(s)){
infos.put(s, infos.get(s) + 1);
}else {
infos.put(s, 1);
}
}
}
System.out.println(infos);
}
}
11. 不可变集合
- 不可变集合,就是不可被修改的集合
- 集合的数据项在创建的时候提供,并且在整个生命周期中都不可改变。否则报错
如何创建不可变集合
- 在List、Set、Map接口中,都存在of方法,可以创建一个不可变集合。
/**
目标:不可变集合
*/
public class CollectionDemo {
public static void main(String[] args) {
// 1、不可变的List集合
List<Double> lists = List.of(569.5, 700.5, 523.0, 570.5);
// lists.add(689.0);
// lists.set(2, 698.5);
// System.out.println(lists);
double score = lists.get(1);
System.out.println(score);
// 2、不可变的Set集合
Set<String> names = Set.of("迪丽热巴", "迪丽热九", "马尔扎哈", "卡尔眨巴" );
// names.add("三少爷");
System.out.println(names);
// 3、不可变的Map集合
Map<String, Integer> maps = Map.of("huawei",2, "Java开发", 1 , "手表", 1);
// maps.put("衣服", 3);
System.out.println(maps);
}
}