1 集合与数组的区别
- 数组和集合的元素存储的个数
数组定义后类型确定,长度固定
集合类型可以不固定,大小是可变的
- 数组和集合存储元素的类型
数组可以存储基本类型和引用类型的数据
集合只能存储引用数据类型的数据
- 数组和集合适合的场景
数组适合做数据个数和类型确定的场景
数组适合做数据个数和类型确定的场景
2 集合的体系特点
注意:
集合和泛型都只能支持引用数据类型,不支持基本数据类型,所以集合中存储的元素都认为是对象
集合支持泛型
// 有序 可重复 有索引
Collection list = new ArrayList();
list.add("Java");
list.add("Java");
list.add(23);
list.add(false);
System.out.println(list);// [Java, Java, 23, false]
// 无序 不重复 无索引
Collection list1 = new HashSet();
list1.add("Java");
list1.add("Java");
list1.add(23);
list1.add(false);
System.out.println(list1);// [Java, false, 23]
3 Collection集合常用API
package com.itheima.d2_collection_api;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
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"));// true
System.out.println(c); // [Java, HTML, HTML]
// 2.清空集合的元素。
// c.clear();
// System.out.println(c);
// 3.判断集合是否为空 是空返回true,反之。
System.out.println(c.isEmpty());// false
// 4.获取集合的大小。
System.out.println(c.size());// 3
// 5.判断集合中是否包含某个元素。
System.out.println(c.contains("Java")); // true
System.out.println(c.contains("java")); // false
System.out.println(c.contains("黑马")); // false
// 6.删除某个元素:如果有多个重复元素默认删除前面的第一个!
System.out.println(c.remove("java")); // false
System.out.println(c.remove("Java")); // true
// 7.把集合转换成数组 [HTML, HTML, MySQL, Java, 黑马]
Object[] arrs = c.toArray();
// 输出数组:数组转换为String格式
System.out.println("数组:" + Arrays.toString(arrs));
System.out.println("arrs.toString() = " + arrs.toString());
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);// [java1, java2, 赵敏, 殷素素]
System.out.println(c2);// [赵敏, 殷素素]
}
}
4 Collection集合的遍历方式
- foreach/增强for循环
- 快捷键:lists.for
- 增强for循环:既可以遍历集合也可以遍历数组
- 修改第三方变量的值不会影响到集合中的元素
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);
}
- lambda表达式
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 );
}
5 Collection集合存储自定义类型的对象
- 集合中存储的是元素对象的地址
// 1、定义一个电影类
// 2、定义一个集合对象存储3部电影对象
Collection<Movie> movies = new ArrayList<>();
movies.add(new Movie("《你好,李焕英》", 9.5, "张小斐,贾玲,沈腾,陈赫"));
movies.add(new Movie("《唐人街探案》", 8.5, "王宝强,刘昊然,美女"));
movies.add(new Movie("《刺杀小说家》",8.6, "雷佳音,杨幂"));
System.out.println(movies);
// 3、遍历集合容器中的每个电影对象
for (Movie movie : movies) {
System.out.println("片名:" + movie.getName());
System.out.println("得分:" + movie.getScore());
System.out.println("主演:" + movie.getActor());
}
常见数据结构
6 List系列集合
- List系列集合特点
ArrayList、LinekdList :有序,可重复,有索引
有序:存储和取出的元素顺序一致
有索引:可以通过索引操作元素
可重复:存储的元素可以重复
- 特有API
// 1.创建一个ArrayList集合对象:
// List:有序,可重复,有索引的。
List<String> list = new ArrayList<>(); // 一行经典代码!
list.add("Java");
list.add("Java");
list.add("HTML");
list.add("HTML");
list.add("MySQL");
list.add("MySQL");
// [Java, Java, HTML, HTML, MySQL, MySQL]
System.out.println("list = " + list);
// 2.在某个索引位置插入元素。
list.add(2, "黑马");
// [Java, Java, 黑马, HTML, HTML, MySQL, MySQL]
System.out.println(list);
// 3.根据索引删除元素,返回被删除元素
System.out.println(list.remove(1));// Java
// [Java, 黑马, HTML, HTML, MySQL, MySQL]
System.out.println(list);
// 4.根据索引获取元素:返回集合中指定位置的元素。
// 黑马
System.out.println(list.get(1));
// 5.修改索引位置处的元素: public E set(int index, E element)
// 返回的是被修改的值:黑马
System.out.println(list.set(1, "传智教育"));
// [Java, 传智教育, HTML, HTML, MySQL, MySQL]
System.out.println(list);
List集合的遍历方式:迭代器 、增强for循环、Lambda表达式、for循环(因为List集合存在索引)
- ArrayList集合底层原理
- ArrayList底层是基于数组实现的:根据索引定位元素快,增删需要做元素的移位操作
- LinkedList集合的底层原理
- 底层数据结构是双链表,查询慢,首尾操作的速度是极快的
补充知识:集合的并发修改异常问题
- 从集合中的一批元素中找出某些数据并删除
- 增强for循环遍历集合且直接用集合删除元素的时候可能出现(foreach不能边遍历边删除)
for (String s : list) {
if("Java".equals(s)){
list.remove(s);
}
}
- lambda表达式(会出现问题,这种无法解决的,Lambda遍历不能边遍历边删除,会出bug)
list.forEach(s -> {
if("Java".equals(s)){
list.remove(s);
}
});
- for循环(边遍历边删除集合没毛病,必须从后面开始遍历删除才不会出现漏掉应该删除的元素)
for (int i = list.size() - 1; i >= 0 ; i--) {
String ele = list.get(i);
if("Java".equals(ele)){
list.remove(ele);
}
}
7 泛型深入
- 泛型只能支持引用数据类型
7.1 自定义泛型类
- 泛型类的格式:修饰符 class 类名<泛型变量>{ }
- 此处泛型变量T可以随便写为任意标识,常见的如E、T、K、V等
package com.itheima.d8_genericity_class;
import java.util.ArrayList;
public class MyArrayList<T> {
private ArrayList lists = new ArrayList();
public void add(T e){
lists.add(e);
}
public void remove(T e){
lists.remove(e);
}
@Override
public String toString() {
return lists.toString();
}
}
// 需求:模拟ArrayList定义一个MyArrayList ,关注泛型设计
MyArrayList<String> list = new MyArrayList<>();
list.add("Java");
list.add("Java");
list.add("MySQL");
list.remove("MySQL");
System.out.println(list);
7.1 自定义泛型方法
- 泛型方法的格式:修饰符 <泛型变量> 方法返回值 方法名称(形参列表){}
- 作用:使用泛型接收一切实际类型的参数,返回值也可以是一切类型
- 实现Arrays.toString(数组)的功能
public class GenericDemo {
public static void main(String[] args) {
String[] names = {"小璐", "蓉容", "小何"};
printArray(names);
Integer[] ages = {10, 20, 30};
printArray(ages);
}
public static <T> void printArray(T[] arr){
if(arr != null){
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i]).append(i == arr.length - 1 ? "" : ", ");
}
sb.append("]");
System.out.println(sb);
}else {
System.out.println(arr);
}
}
}
7.1 自定义泛型接口
- 泛型接口的格式:修饰符 interface 接口名称<泛型变量>{}
- 作用:泛型接口可以让实现类选择当前功能需要操作的数据类型
- 实现类可以在实现接口的时候传入自己操作的数据类型
public interface Data<E> {
void add(E e);
void delete(int id);
void update(E e);
E queryById(int id);
}
public class StudentData implements Data<Student>{
@Override
public void add(Student student) {
}
@Override
public void delete(int id) {
}
@Override
public void update(Student student) {
}
@Override
public Student queryById(int id) {
return null;
}
}
7.1 泛型通配符、上下限
- 通配符:?
? 可以在“使用泛型”的时候代表一切类型
E T K V 是在定义泛型的时候使用的
/**
所有车比赛
*/
public static void go(ArrayList<? extends Car> cars){
}
- 泛型的上下限:
? extends Car: ?必须是Car或者其子类 泛型上限
? super Car : ?必须是Car或者其父类 泛型下限
8 Set系列集合
- Set集合实现类特点
HashSet : 无序、不重复、无索引
LinkedHashSet:有序、不重复、无索引
TreeSet:排序、不重复、无索引
- Set集合的功能上基本上与Collection的API一致
8.1 实现类:HashSet集合元素无序的底层原理:哈希表
- HashSet集合底层采取哈希表存储的数据
- 哈希表的组成
DK8之前的,底层使用数组+链表组成
JDK8开始后,底层采用数组+链表+红黑树组成
- 哈希值:是JDK根据对象的地址,按照某种规则算出来的int类型的数值
- public int hashCode():返回对象的哈希值
- 对象的哈希值特点
同一个对象多次调用hashCode()方法返回的哈希值是相同的
默认情况下,不同对象的哈希值是不同的
8.2 实现类:LinkedHashSet
- 有序、不重复、无索引
- 有序指的是保证存储和取出的元素顺序一致
- 原理:底层数据结构是哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序
8.3 实现类:TreeSet
- 不重复、无索引、可排序
- 可排序:按照元素的大小默认升序(有小到大)排序
- TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好
- 注意:TreeSet集合是一定要排序的,可以将元素按照指定的规则进行排序
8.3.1 TreeSet集合默认的规则
- 对于数值类型:Integer , Double,官方默认按照大小进行升序排序
- 对于字符串类型:默认按照首字符的编号升序排序
- 对于自定义类型如Student对象,TreeSet无法直接排序
8.3.2 使用TreeSet存储自定义类型,制定排序规则
- 集合自带比较器对象进行规则定制(推荐使用)
- 注意:浮点型建议直接使用Double.compare进行比较
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(o1.getPrice() , o2.getPrice()); // 升序
return Double.compare(o2.getPrice() , o1.getPrice()); // 降序
}
});
Set<Apple> apples1 = new TreeSet<>(( o1, o2) -> Double.compare(o2.getPrice()
, o1.getPrice()) );
apples1.add(new Apple("红富士", "红色", 9.9, 500));
apples1.add(new Apple("青苹果", "绿色", 15.9, 300));
apples1.add(new Apple("绿苹果", "青色", 29.9, 400));
apples1.add(new Apple("黄苹果", "黄色", 9.8, 500));
System.out.println(apples1);
- 类实现Comparable接口,重写比较规则
/**
方式一:类自定义比较规则
o1.compareTo(o2)
* @param o
* @return
*/
@Override
public int compareTo(Apple o) {
// 按照重量进行比较的
return this.weight - o.weight ; // 去重重量重复的元素
// return this.weight - o.weight >= 0 ? 1 : -1; // 保留重量重复的元素
}
Collection体系的特点、使用场景总结
补充知识:可变参数
- 可变参数的格式:数据类型…参数名称
- 可变参数的作用
可以不传输参数
可以传输一个参数
可以传输多个参数
可以传输一个数组
可变参数在方法内部本质上就是一个数组
- 可变参数的注意事项:
一个形参列表中可变参数只能有一个
可变参数必须放在形参列表的最后面
public class MethodDemo {
public static void main(String[] args) {
sum(); // 1、不传参数
sum(10); // 2、可以传输一个参数
sum(10, 20, 30); // 3、可以传输多个参数
sum(new int[]{10, 20, 30, 40, 50}); // 4、可以传输一个数组
}
/**
注意:一个形参列表中只能有一个可变参数,可变参数必须放在形参列表的最后面
* @param nums
*/
public static void sum( int...nums){
// 注意:可变参数在方法内部其实就是一个数组。 nums
System.out.println("元素个数:" + nums.length);
System.out.println("元素内容:" + Arrays.toString(nums));
}
}
补充知识:集合工具类Collections
- 作用:Collections并不属于集合,是用来操作集合的工具类
package com.itheima.d3_collections;
import java.util.*;
public class CollectionsDemo01 {
public static void main(String[] args) {
// 1、给集合对象批量添加元素
List<String> names = new ArrayList<>();
Collections.addAll(names, "楚留香","胡铁花", "张无忌","陆小凤");
System.out.println(names);// [楚留香, 胡铁花, 张无忌, 陆小凤]
// 2、打乱集合顺序。
Collections.shuffle(names);
System.out.println(names);// [张无忌, 胡铁花, 陆小凤, 楚留香]
// 3、将集合中元素按照默认规则排序。(排值特性的元素)
List<Integer> list = new ArrayList<>();
Collections.addAll(list, 12, 23, 2, 4);
System.out.println(list);// [12, 23, 2, 4]
Collections.sort(list);
System.out.println(list);// [2, 4, 12, 23]
}
}
- 自定义类型的比较方法API:Collections
package com.itheima.d3_collections;
import java.util.*;
/**
自定义类型的比较方法API:Collections
- public static <T> void sort(List<T> list,Comparator<? super T> c):
将集合中元素按照指定规则排序,自带比较器
*/
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));
// 方式二: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);
}
}
9 Map集合体系
9.1 Map集合的概述
- Map集合的每个元素的格式:key=value(键值对元素)
- Map集合体系特点
Map集合的特点都是由键决定的
Map集合的键是无序,不重复的,无索引的,值不做要求(可以重复)
Map集合后面重复的键对应的值会覆盖前面重复键的值
Map集合的键值对都可以为null
- Map集合实现类特点
HashMap:元素按照键是无序,不重复,无索引,值不做要求
LinkedHashMap:元素按照键是有序,不重复,无索引,值不做要求
TreeMap:元素按照建是排序,不重复,无索引的,值不做要求
9.2 Map集合常用API
package com.itheima.d6_map_api;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
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();
// 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);// {java3=100, java2=1, java1=1}
System.out.println(map2);// {java3=100, java2=1}
}
}
9.3 Map集合的遍历方式
9.3.1 键值对
package com.itheima.d7_map_traversal;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
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);
// 1、把Map集合转换成Set集合
Set<Map.Entry<String, Integer>> entries = maps.entrySet();
// 2、开始遍历
/**
* huawei====>1000
* 手表====>10
* 生活用品====>10
* iphoneX====>100
* 娃娃====>30
*/
for(Map.Entry<String, Integer> entry : entries){
String key = entry.getKey();
int value = entry.getValue();
System.out.println(key + "====>" + value);
}
}
}
9.3.2 lambda表达式
package com.itheima.d7_map_traversal;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
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);
/**
* huawei--->1000
* 手表--->10
* 生活用品--->10
* iphoneX--->100
* 娃娃--->30
*/
maps.forEach((k, v) -> {
System.out.println(k + "--->" + v);
});
}
}
9.4 Map集合的实现类HashMap
9.5 Map集合的实现类LinkedHashMap
- 由键决定:有序、不重复、无索引
- 原理:底层数据结构是依然哈希表,每个键值对元素多了一个双链表的机制记录存储的顺序
9.6 Map集合的实现类TreeMap
- 由键决定特性:不重复、无索引、可排序
- 可排序:按照键数据的大小默认升序(有小到大)排序。只能对键排序
- 注意:TreeMap集合是一定要排序的,可以默认排序,也可以将键按照指定的规则进行排序
- TreeMap跟TreeSet一样底层原理是一样的
- TreeMap集合自定义排序规则有2种
TreeMap集合自定义排序规则有2种
集合自定义Comparator比较器对象,重写比较规则
package com.itheima.d9_map_impl;
import com.itheima.d1_collection_set.Apple;
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
public class TreeMapDemo3 {
public static void main(String[] args) {
Map<Integer, String> maps1 = new TreeMap<>();
maps1.put(13 , "王麻子");
maps1.put(1 , "张三");
maps1.put(3 , "县长");
// {1=张三, 3=县长, 13=王麻子}:默认升序
System.out.println(maps1);
// TreeMap集合自带排序。 可排序 不重复(只要大小规则一样就认为重复) 无索引
Map<Apple, String> maps2 = new TreeMap<>(new Comparator<Apple>() {
@Override
public int compare(Apple o1, Apple o2) {
// 按照价格降序排序!
return Double.compare(o2.getPrice() , o1.getPrice());
}
});
maps2.put(new Apple("红富士", "红色", 9.9, 500), "山东" );
maps2.put(new Apple("青苹果", "绿色", 15.9, 300), "广州");
maps2.put(new Apple("绿苹果", "青色", 29.9, 400), "江西");
maps2.put(new Apple("黄苹果", "黄色", 9.8, 500), "湖北");
System.out.println(maps2);
}
}
补充知识:集合的嵌套
package com.itheima.d8_map_test;
import java.util.*;
/**
需求:统计投票人数
*/
public class MapTest1 {
public static void main(String[] args) {
// 1、把80个学生选择的数据拿进来。
String[] selects = {"A" , "B", "C", "D"};
StringBuilder sb = new StringBuilder();
Random r = new Random();
for (int i = 0; i < 80; i++) {
sb.append(selects[r.nextInt(selects.length)]);
}
// BDDDDBBDDDDCACADDBABCCDCBCBDBDCADDAABCDBB
// ACBDCCABADACBBBACBBBABAADCBABADABBDACAC
System.out.println(sb);
// 2、定义一个Map集合记录最终统计的结果: A=30 B=20 C=20 D=10 键是景点 值是选择的数量
Map<Character, Integer> infos = new HashMap<>(); //
// 3、遍历80个学生选择的数据
for (int i = 0; i < sb.length(); i++) {
// 4、提取当前选择景点字符
char ch = sb.charAt(i);
// 5、判断Map集合中是否存在这个键
if(infos.containsKey(ch)){
// 让其值 + 1
infos.put(ch , infos.get(ch) + 1);
}else {
// 说明此景点是第一次被选
infos.put(ch , 1);
}
}
// 4、输出集合
// {A=19, B=24, C=16, D=21}
System.out.println(infos);
}
}
10 创建不可变集合
- 集合的数据项在创建的时候提供,并且在整个生命周期中都不可改变
- 不能添加,不能删除,不能修改
package com.itheima.d1_unchange_collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class CollectionDemo {
public static void main(String[] args) {
// 1、不可变的List集合
List<Double> lists = List.of(569.5, 700.5, 523.0, 570.5);
double score = lists.get(1);
System.out.println(score);
// 2、不可变的Set集合
Set<String> names = Set.of("迪丽热巴", "迪丽热九", "马尔扎哈", "卡尔眨巴" );
System.out.println(names);
// 3、不可变的Map集合
Map<String, Integer> maps = Map.of("huawei",2, "Java开发", 1 , "手表", 1);
// maps.put("衣服", 3);
System.out.println(maps);
}
}