菜鸟之路Day11-12一一集合进阶(四)
作者:blue
时间:2025.1.29-1.30
0.概述
内容学习自黑马程序员,BV1yW4y1Y7Ms,过年了,精神上有些懈怠,效率有所下降,得迅速调整一下。
1.可变参数
方法形参的个数是可以发生变化的,0 1 2 3 ……
格式:属性类型……名字
有关可变参数的小细节:
1.在方法的形参中最多只能写一个可变参数
2.在方法的形参当中,如果出现了可变参数以外,还有其他形参,那么可变参数要写在最后
例子:
public class Test {
public static void main(String[] args) {
System.out.println(getSum(1,1,2,3,4,5,5));
System.out.println(getSum(1,2,3));
}
public static int getSum(int...args){
//可变参数,本质是一个数组
int sum=0;
for(int i:args){
sum+=i;
}
return sum;
}
}
2.Collections
java.util.Collections:是集合工具类
作用:Collections不是集合,而是集合的工具类
Collections常用的API代码演示:
package Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
public class CollectionsTest {
public static void main(String[] args) {
//1.public static <T> boolean addAll(Collection<T> c,T... elements) 批量添加元素
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,1,2,3,4,5,6,7,8,9,10);
System.out.println(list);
System.out.println("================================");
//2.public static void shuffle(List<?> list) 打乱List集合元素的顺序,注意,这个Set是不能用的
Collections.shuffle(list);
System.out.println(list);
System.out.println("================================");
//3.public static <T> void sort(List<T> list) 排序
Collections.sort(list);
System.out.println(list);
System.out.println("================================");
//4.public static <T> void sort(List<T> list,Comparator<T> c) 根据指定的规则进行排序
Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
});
System.out.println(list);
System.out.println("================================");
//5.public static <T> int binarySearch(List<T> list,T key) 用二分查找法查找元素,注意二分查找List必须得是有序的
Collections.sort(list);//先变成有序的
int index = Collections.binarySearch(list,2);//查找2的位置
System.out.println(index);//2的索引是1
System.out.println("================================");
//6.public static <T> int fill(List<T> list,T obj) 使用指定的元素填充集合
//Collections.fill(list,3);
//System.out.println(list);
//7.public static <T> void max/min(Collection<T> coll) 根据默认的自然排序
System.out.println(Collections.max(list));
System.out.println("================================");
//8.public static <T> void swap(List<?> list,int i,int j) 交换集合中指定位置的元素
Collections.swap(list,1,8);
System.out.println(list);
}
}
3.综合练习
练习一:
随机点名,学生属性:姓名,年龄,性别
public class Test1 {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<>();
Student s1 = new Student("zhangsan",18,"男");
Student s2 = new Student("lisi",21,"男");
Student s3 = new Student("wangwu",18,"男");
Student s4 = new Student("zhaoliu",16,"女");
Student s5 = new Student("xiaoqi",18,"男");
Student s6 = new Student("laoba",17,"女");
Student s7 = new Student("huangjiu",18,"男");
Collections.addAll(list,s1,s2,s3,s4,s5,s6,s7);
Random rd = new Random();
int index = rd.nextInt(list.size());
System.out.println(list.get(index));
}
}
练习2:
带概率的随机点名,要求有70%概率随机到男生,有30%的概率随机到女生
public class Test2 {
public static void main(String[] args) {
//创建一个集合,存储7个1,和3个0,1代表抽男生,0代表抽女生,这样就随机出了概率
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,1,1,1,1,1,1,1,0,0,0);
Collections.shuffle(list);//打乱
Random rd = new Random();
int index = rd.nextInt(list.size());//随机抽取数字,产生概率0.7和0.3的过程
int number = list.get(index);//1代表抽男生,0代表抽女生
ArrayList<String> boyList = new ArrayList<>();
Collections.addAll(boyList,"ggboy","zhangsan","kevin","james","zero","888","666");
ArrayList<String> girlList = new ArrayList<>();
Collections.addAll(girlList,"刘亦菲","章若楠","田曦微");
if(number==1){
int boyindex = rd.nextInt(boyList.size());
System.out.println(boyList.get(boyindex));
}
else
{
int girlindex = rd.nextInt(girlList.size());
System.out.println(girlList.get(girlindex));
}
}
}
练习3:
要求:被点到的学生不会再被点到,但是全部点完了,就得重开一把
public class Test3 {
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>();
Collections.addAll(list1,"ggboy","zhangsan","kevin","james","zero","888","666","刘亦菲","章若楠","田曦微");
int cnt = list1.size();
Random rd = new Random();
for(int j=1;j<=10;j++){
System.out.println("================="+j+"轮===================");
for(int i=1;i<=cnt;i++){
int index = rd.nextInt(list1.size());
String res = list1.remove(index); //抽到它就把他删掉,保持,单轮次内的唯一性
list2.add(res);
System.out.println(res);
}
//做一个交替滚动
list1.addAll(list2);
list2.clear();
}
}
}
练习四:
需求:定义一个Map集合,键用省份名称province,值表示市city,但是市会有多个。添加完毕后,遍历结果格式如下:
江苏省 = 南京市,扬州市,苏州市,无锡市,常州市
湖北省 = 武汉市,孝感市,十堰市,宜昌市,鄂州市
河北省 = 石家庄市,唐山市,邢台市,保定市,张家口市
public class Test4 {
public static void main(String[] args) {
HashMap<String, ArrayList<String>> mp = new HashMap<>();
ArrayList<String> list1 = new ArrayList<>();
Collections.addAll(list1,"南京市","扬州市","苏州市","无锡市","常州市");
ArrayList<String> list2 = new ArrayList<>();
Collections.addAll(list2,"武汉市","孝感市","十堰市","宜昌市","鄂州市");
ArrayList<String> list3 = new ArrayList<>();
Collections.addAll(list3,"石家庄市","唐山市","邢台市","保定市","张家口市");
mp.put("江苏省",list1);
mp.put("湖北省",list2);
mp.put("河北省",list3);
mp.forEach(new BiConsumer<String, ArrayList<String>>() {
@Override
public void accept(String s, ArrayList<String> strings) {
System.out.print(s+" "+"="+" ");
for (int i = 0; i < strings.size(); i++) {
if(i!=strings.size()-1){
System.out.print(strings.get(i)+","+" ");
}
else System.out.print(strings.get(i));
}
System.out.println();
}
});
}
}
4.不可变的集合
应用场景:1.如果某个数据不能被修改,把他防御性的拷贝到不可变集合中是个很好的实践
2.当集合对象被不可信的库调用时,不可变形式是安全的。
简单理解:不想让别人修改集合中的内容
创建不可变集合的书写格式
在List,Set,Map接口中,都存在静态的of方法,可以获取一个不可变的集合
注意:这个集合不能添加,不能删除,不能修改
public class Day11Test1 {
public static void main(String[] args) {
//static <E> List<E> of(E...elements) 创建一个具有指定元素的List集合对象
List<Integer> list = List.of(1,2,3,4,5,6);
/*
list.add(1);
list.remove(0);
这时各种试图改变集合的操作都是不被允许的
*/
//static <E> Set<E> of(E...elements) 创建一个具有指定元素的Set集合对象
//Set<Integer> set = Set.of(1,1,2,3,4,5,6);注意集合元素是不可重复的,创建不可变集合,输入重复的内容,也是不被允许的
Set<Integer> set = Set.of(1,2,3,4,5,6);
//static <K,V> Map<K,V> of(E...elements) 创建一个具有指定元素的Map集合对象
//注意:创建时,键不能重复
//注意:最多只能添加10个键值对!!!
Map<String,Integer> mp = Map.of("zhangsan",14,"lisi",15);
//如果要添加元素的数量多余10个,可以用以下方法来解决,它的思想是将键与值视为整体,键值对为Map.Entry对象,这样子我们就只有一个可变参数,就可以不去限制长度
//步骤:
//1.创建一个普通的Map集合
HashMap<String,String> hm = new HashMap<>();
hm.put("zhangsan","nanjing");
hm.put("wangwu","shantou");
hm.put("xiaomu","qingdao");
hm.put("chener","jiaxin");
//2.利用上面的数据来创建一个不可变的集合
/*
//获取到所有键值对对象(Entry对象)
Set<Map.Entry<String,String>> entries = hm.entrySet();
//把entries变成一个数组
Map.Entry[] arr = new Map.Entry[0];
//toArray方法会在底层比较集合长度跟数组长度两者的大小
//如果集合长度 > 数组长度 :数据在数组中放不下,此时会根据实际数据的个数,重新创建数组
//如果集合长度 <= 数组的长度 :数据在数组中放的下,此时不会创建新数组,而是直接用
//所以那里的参数0,是最节约空间的方法
Map.Entry[] arr2 = entries.toArray(arr);
//这样就创建了一个不可变集合
Map map = Map.ofEntries(arr2);
*/
//以上方法麻烦,jdk10以后
Map<String,String> map = Map.copyOf(hm);//这样创建出来就是一个不可变map,这样长度就不会被限定了
//map.put("aaa","bbb"); Error
}
}
5.Stream流
Stream流初体验:
public class StreamTest1 {
public static void main(String[] args) {
/*
创建集合添加元素,完成以下需求:
1.把所有“张”开头的元素存储到新集合中
2.把"张"开头的,长度为3的元素再存储到新集合中
3.遍历打印最终结果
*/
ArrayList<String> list1 = new ArrayList<>();
Collections.addAll(list1,"张无忌","周芷若","赵敏","赵强","张三丰");
list1.stream().filter(name->name.startsWith("张")).filter(name -> name.length() == 3 ).forEach(System.out::println);
}
}
Stream流的作用:结合了Lambda表达式,简化集合,数组的操作
Stream流的使用步骤:
①先得到一条Stream流(流水线),并把数据放上去;
获取方式 | 方法名 | 说明 |
---|---|---|
单列集合 | default Stream stream() | Collection中的默认方法 |
双列集合 | 无 | 无法直接使用stream流 |
数组 | public static Stream stream(T[] array) | Arrays工具类中的静态方法 |
一堆零散数据 | public static Stream of(T...values) | Stream接口中的静态方法 |
单列集合
public class StreamTest2 {
public static void main(String[] args) {
//1.单列集合获取Stream流
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"a","b","c","d","e","f");
//Stream<String> stream1 = list.stream();获取到一条流水线
//使用终结方法打印一下流水线上的所有数据
list.stream().forEach(s -> System.out.println(s));
}
}
双列集合
public class StreamTest3 {
public static void main(String[] args) {
//1.创建双列集合
HashMap<String,Integer> hm = new HashMap<>();
//2.添加数据
hm.put("aaa",111);
hm.put("bbb",222);
hm.put("ccc",333);
hm.put("ddd",444);
//3.第一种获取stream流
//用keySet方法获取它所有的键的单列集合
hm.keySet().stream().forEach(s-> System.out.println(s));
//4.第二种方法获取stream流
//获取键值对集合,再获取其Stream流
hm.entrySet().stream().forEach(s-> System.out.println(s));
}
}
数组
public class StreamTest4 {
public static void main(String[] args) {
int[] arr = {
1,2,3,4,5,6,7,8};
//利用工具类Arrays中的方法获取Stream流
Arrays.stream(arr).forEach(s -> System.out.println(s));
}
}
一堆零散的数据
import java.util.stream.Stream;
public class StreamTest5 {
public static void main(String[] args) {
Stream.of(1,2,3,4,5).forEach(s -> System.out.println(s));
}
}
//注意:
//Stream接口中静态方法of的细节
//方法的形参是一个可变参数,可以传递一堆零散数据,也可以传递数组
//但是数组必须是引用数据的,如果传递基本数据类型,是会把整个数组当做一个元素,放到Stream当中
②使用中间方法对流水线上的数据进行操作;
注意1:中间方法,返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程
注意2:修改Stream流中的数据,不会影响原来集合或者数组中的数据
public class StreamTest6 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰","张翠山","张良","赵敏","谢逊");
//1.filter 过滤 把张开头的留下,其余的数据过滤不要
/*list.stream().filter(new Predicate<String>() {
@Override
public boolean test(String s) {
//如果返回值为true,表示当前的数据留下
//如果返回值为false,表示当前数据舍弃不要
return s.startsWith("张");
}
}).forEach(s -> System.out.println(s));*/
list.stream().filter(s -> s.startsWith("张")).forEach(s -> System.out.print(s));
System.out.println();
System.out.println("==========================================");
//2.limit 获取前几个元素
list.stream().limit(3).forEach(s -> System.out.print(s));
System.out.println();
System.out.println("==========================================");
//3.skip 跳过前几个元素
list.stream().skip(4).forEach(s -> System.out.print(s));
System.out.println();
System.out.println("==========================================");
//4.distinct 元素去重,依赖(hashCode和equals方法)
list.add("张无忌");
list.add("张无忌");
list.stream().distinct().forEach(s-> System.out.print(s));
System.out.println();
System.out.println("==========================================");
//5.concat 合并a,b两个流为一个流
ArrayList<String> list2 = new ArrayList<>();
Collections.addAll(list2,"你好","你不好");
Stream.concat(list.stream(),list2.stream()).forEach(s -> System.out.print(s));
System.out.println();
System.out.println("==========================================");
//6.map 转换流中的数据类型
ArrayList<String> list3 = new ArrayList<>();
//后面的数字代表年龄
Collections.addAll(list3,"小张-15","小赵-16","小风-17","小良-18","小王-19");
//需求:只获取里面年龄并进行打印
//String -> int
/*list3.stream().map(new Function<String, Integer>() {
@Override
public Integer apply(String s){
String[] arr = s.split("-");
String ageString = arr[1];
int age = Integer.parseInt(ageString);
return age;
}
}).forEach(s -> System.out.print(s+" "));*/
list3.stream().map(s -> Integer.parseInt(s.split("-")[1])).forEach(s-> System.out.print(s+" "));
System.out.println();
System.out.println("==========================================");
}
}
③使用终结方法对流水线上的数据进行操作
/*
void forEach(Consumer action) 遍历
long count() 统计
toArray() 收集流中的数据,放到数组中
*/
public class StreamTest7 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"aaa","bbb","ccc","ddd","eee","fff","ggg");
System.out.println(list.stream().count());
//toArray() 收集流中的数据,放到数组中
//Object[] arr1 = list.stream().toArray();
//System.out.println(Arrays.toString(arr1));
//指定数组类型
/*String[] arr = list.stream().toArray(new IntFunction<String[]>() {
@Override
public String[] apply(int value) {
return new String[value];
}
});*/
String[] arr = list.stream().toArray(value -> new String[value]);
System.out.println(Arrays.toString(arr));
}
}