当前位置: 首页 > news >正文

Java入门级教程16——集合

Java集合分类:

1. Collection接口

1.1 List接口 可以重复元素

具体的实现类:

1.1.1 ArrayList实现类 底层是用数组实现的 线程非同步的
package com.hy.demo3;import java.util.ArrayList;
import java.util.Collection;
import java.util.List;public class Test {public static void main(String[] args) {// 创建一个ArrayListCollection c1 = new ArrayList();List c2 = new ArrayList();ArrayList lists = new ArrayList();// 添加lists.add(10);lists.add("20");lists.add(true);lists.add(true);// 大小System.out.println("lists长度为:" + lists.size());// 包含System.out.println("是否包含true:" + lists.contains(true));System.out.println("是否包含20:" + lists.contains(20));// String类的方法String s = new String("我养了一只小鸟🐦");System.out.println("是否包含我:" + s.contains("我"));System.out.println("------------------------------");// 遍历System.out.println("遍历结果为:");for(int i = 0;i < lists.size();i++) {System.out.println(lists.get(i));}System.out.println("------------------------------");// 打印数组[0]System.out.println("lists.get(0)的值为:" + lists.get(0));// 更新lists.set(0, "张三");System.out.println("------------------------------");// 遍历System.out.println("遍历结果为:");for (int i = 0; i < lists.size(); i++) {System.out.println(lists.get(i));}System.out.println("------------------------------");// 删除lists.remove(0);// 遍历System.out.println("遍历结果为:");for (int i = 0; i < lists.size(); i++) {System.out.println(lists.get(i));}System.out.println("------------------------------");// 按值查找对应下标System.out.println(lists.indexOf("20")); // 0// 判断是否为空System.out.println(lists.isEmpty());// 清空lists.clear();// 判断是否为空System.out.println(lists.isEmpty());}}

输出结果:

lists长度为:4
是否包含true:true
是否包含20:false
是否包含我:true
------------------------------
遍历结果为:
10
20
true
true
------------------------------
lists.get(0)的值为:10
------------------------------
遍历结果为:
张三
20
true
true
------------------------------
遍历结果为:
20
true
true
------------------------------
0
false
true

// 解决线程非同步问题

package com.hy.demo18;import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;public class Test implements Runnable {CountDownLatch cd;static ArrayList list = new ArrayList();// static LinkedList list = new LinkedList();static List list1;public Test(CountDownLatch cd) {this.cd = cd;// 利用Collections的synchronizedList方法// 通过工具类将非线程安全的 list 包装为线程安全的列表// (所有方法加同步锁,避免多线程并发修改异常)list1 = Collections.synchronizedList(list);}public void run() {for (int i = 0; i < 100; i++) {// synchronized或Lock
//			synchronized (this) {
//				list.add(i);
//			}list1.add(i);}cd.countDown();}public static void main(String[] args) {CountDownLatch cd = new CountDownLatch(4);Test t = new Test(cd);new Thread(t).start();new Thread(t).start();new Thread(t).start();new Thread(t).start();try {cd.await();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("这个集合的大小为:" + list.size()); // 400}}

//

package com.hy.demo17;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;public class Test {public static void main(String[] args) {// 泛型<引用类型>ArrayList lists = new ArrayList();lists.add(10);lists.add(20);lists.add(5);// 反转集合// System.out.println(lists); // [10, 20, 5]// Collections.reverse(lists);// System.out.println(lists); // [5, 20, 10]// Arrays.asList(lists);// System.out.println(lists); // 不变// Collections.reverseOrder();// System.out.println(lists); // 不变// 最大值// System.out.println(Collections.max(lists));// 排序// Collections.sort(lists);// System.out.println(lists);// 将集合lists转换为Object数组,然后对数组进行「降序排序」// 排序的是数组,而非原集合Object[] objs = lists.toArray();Arrays.sort(objs, Collections.reverseOrder());System.out.println(lists);for(Object obj : objs){System.out.println(obj);}}
}

1.创建集合并添加元素:
创建了一个未指定泛型的 ArrayList(原始类型),添加了三个整数 10、20、5。此时集合内容为:[10, 20, 5]。
反转集合(Collections.reverse()):
调用 Collections.reverse(lists) 对集合元素进行反转,反转后集合内容变为:[5, 20, 10]。
因此这一步输出:[5, 20, 10]。
2.Arrays.asList(lists) 调用:
Arrays.asList() 方法的作用是将数组转换为固定大小的 List,但这里传入的参数是一个 ArrayList 对象(集合),而非数组。
此时该方法会将整个 lists 集合作为单个元素,放入一个新的 List 中(新集合内容为 [[5, 20, 10]]),但原 lists 集合本身不会被修改。
因此这一步输出 lists 仍为:[5, 20, 10]。
3.Collections.reverseOrder() 调用:
该方法的作用是返回一个 “反转自然排序” 的比较器(Comparator),但它不会直接修改集合,只是提供一个用于排序的规则对象。
由于没有用这个比较器对集合进行排序操作,lists 内容保持不变,仍为:[5, 20, 10]。
因此这一步输出:[5, 20, 10]。
4.获取集合最大值(Collections.max()):
Collections.max(lists) 会根据元素的自然排序(这里是整数的大小)找出集合中的最大元素。
集合中元素为 5、20、10,最大值是 20,因此输出:20。

1.1.2 Vector 实现类 底层是用数组实现的 线程同步的

// 1. ArrayList是非线程同步安全,Vector是线程同步安全

package com.hy.demo2;import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Vector;public class Test {public static void main(String[] args) {// List接口下面有具体的实现类:ArrayList、Vector、LinkedList的实现类// ArrayList、Vector底层都是用数组实现的// ArrayList是非线程同步安全ArrayList  lists1  =   new ArrayList();lists1.add(20);// Vector是线程同步安全,方法是用synchronized修饰的Vector  v1  = new Vector();v1.add(20);//LinkedList底层都是用链表实现的LinkedList  list2  = new LinkedList();list2.addFirst(1);}
}
package com.hy.demo4;import java.util.ArrayList;
import java.util.Vector;
import java.util.concurrent.CountDownLatch;public class Test implements Runnable {// 闭锁CountDownLatch cd;//ArrayList是线程非安全// static ArrayList lists = new ArrayList();static Vector lists = new Vector();public Test(CountDownLatch cd) {this.cd = cd;}public void run() {for (int i = 0; i < 100; i++) {lists.add(i); // 若是ArrayList则大概率报错,抛出数组下标越界异常// 解决方式1:// synchronized (this) {// 	lists.add(i);// }// 解决方式2:// 使用Vector}cd.countDown();}public static void main(String[] args) {CountDownLatch cd = new CountDownLatch(3);Test t = new Test(cd);new Thread(t).start();new Thread(t).start();new Thread(t).start();try {// cd.await():是 CountDownLatch 类的核心方法,// 作用是:让当前线程进入阻塞状态,等待 CountDownLatch 的计数器减为 0 后再继续执行cd.await();System.out.println("最后三个线程的总和为:" + lists.size());} catch (InterruptedException e) {e.printStackTrace();}}}

// 2. ArrayList与Vector对比

package com.hy.demo5;import java.util.ArrayList;
import java.util.Vector;public class Test {public static void main(String[] args) {/*** ArrayList和Vector区别* * 共同点是:底层是用数组来实现的。* * 不同点是:* * 1.初始化的大小不一样,ArrayList是空的,Vector是默认大小是10;* * 2. ArrayList是线程非安全的,Vector是线程安全的。* * */ArrayList  lists   = new ArrayList();Vector  v  = new Vector();}}
1.1.3 LinkedList实现类 底层是用链表来实现的

// 

package com.hy.demo6;import java.util.LinkedList;public class Test {public static void main(String[] args) {LinkedList lists = new LinkedList();lists.add(10);lists.add(20);lists.add(30);System.out.println(lists.get(1));}}

// LinkedList也可以使用栈的数据数据结构特点

package com.hy.demo6;import java.util.LinkedList;public class Test1 {public static void main(String[] args) {// LinkedList也可以使用栈的数据结构特点// 栈:先进后出,后进先出LinkedList lists = new LinkedList();lists.push(10);lists.push(20);lists.push(30);while (!lists.isEmpty()) {System.out.println(lists.pop());}}}
package com.hy.demo2;import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Vector;public class Test {public static void main(String[] args) {//List接口下面有具体的实现类:ArrayList、Vector、LinkedList的实现类//ArrayList、Vector底层都是用数组实现的ArrayList  lists1  =   new ArrayList();lists1.add(20);Vector  v1  = new Vector();v1.add(20);//LinkedList底层都是用链表实现的LinkedList  list2  = new LinkedList();list2.addFirst(1);}
}

// ArrayList与LinkedList性能测试对比

package com.hy.demo7;import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;public class Test {// ArrayList底层的数据结构是数组,数组的数据结构特点:遍历快,增删改慢// [1,2,3,4,5,6,7]// LinkedList底层的数据结构是链表,链表的数据结构特点:遍历慢,增删改快// 前索引1-元素1-后索引1 前索引2-元素2-后索引 2 前索引3-元素3-后索引 3public static void addData(List lists) {// 性能测试long startTime = System.currentTimeMillis();int i = 0;while (i < 200000) {Object obj = new Object();lists.add(0, obj);i++;}long endTime = System.currentTimeMillis();System.out.println("花费的时间为:" + (endTime - startTime));}public static void main(String[] args) {//addData(new ArrayList());//花费的时间为:2603addData(new LinkedList()); //花费的时间为:6}}

// ArrayList与LinkedList遍历时长对比

package com.hy.demo8;import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;public class Test {public static void queryData(List lists) {long startTime = System.currentTimeMillis();for (int i = 0; i < lists.size(); i++) {// 获取lists.get(i);}long endTime = System.currentTimeMillis();System.out.println("遍历花费的时间为:" + (endTime - startTime));}public static void main(String[] args) {// ArrayList lists = new ArrayList(); // 6LinkedList lists = new LinkedList(); // 时间很长for (int i = 0; i < 500000; i++) {lists.add(i);}queryData(lists);// ArrayList的6毫秒 //遍历花费的时间为:85212/1000 = 85.212}}

// 与迭代器遍历对比

package com.hy.demo8;import java.util.Iterator;
import java.util.LinkedList;public class Test1 {public static void queryData1(LinkedList lists) {long startTime = System.currentTimeMillis();for (int i = 0; i < lists.size(); i++) {// 获取lists.get(i);}long endTime = System.currentTimeMillis();System.out.println("遍历花费的时间为:" + (endTime - startTime));}public static void queryData2(LinkedList lists) {long startTime = System.currentTimeMillis();//迭代器遍历Iterator  its = lists.iterator();while(its.hasNext()){its.next();}long endTime = System.currentTimeMillis();System.out.println("遍历花费的时间为:" + (endTime - startTime));}public static void main(String[] args) {LinkedList lists1 = new LinkedList();for (int i = 0; i < 200000; i++) {lists1.add(i);}// queryData1(lists1);// //一般for遍历花费的时间为:12060/1000 =12秒queryData2(lists1);//迭代器遍历 遍历花费的时间为:4毫秒}}

// List本身不能过滤重复,那该如何过滤重复?

package com.hy.demo20;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;public class Test {public static void main(String[] args) {List<String> lists = new ArrayList<String>();lists.add("张三");lists.add("李四");lists.add("张三");// List集合是不能过滤重复的,现在要求你过滤重复List<String> lists1 = new ArrayList<String>();for (String s : lists) {if (!lists1.contains(s)) {lists1.add(s);}}System.out.println(lists1); // [张三, 李四]}}

1.2 Set接口 不可以重复元素

HashSet TreeSet LinkedHashSet

package com.hy.demo1;import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;public class Test1 {public static void main(String[] args) {// Collection接口下面有两个子类接口:// 1. List接口,可以有重复元素// 2. Set接口 不能有重复元素List lists = new ArrayList();lists.add(10);lists.add(20);lists.add(10);System.out.println(lists.size()); // 3System.out.println("------------");HashSet set = new HashSet();set.add(10);set.add(20);set.add(10);System.out.println(set.size()); // 2}}

1.3 学完后运用于数据库

package com.hy.demo9;// JavaBean,Java数据模型,对应数据库中的表
public class Stu {private int sid;private String sname;private String saddress;public int getSid() {return sid;}public void setSid(int sid) {this.sid = sid;}public String getSname() {return sname;}public void setSname(String sname) {this.sname = sname;}public String getSaddress() {return saddress;}public void setSaddress(String saddress) {this.saddress = saddress;}
}

// 不同的Dao

package com.hy.demo9;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;public class Dao {Connection conn; public Dao() {try {Class.forName("com.mysql.cj.jdbc.Driver");conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/jk202508","root","Hy61573166!!!");} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException e) {e.printStackTrace();}}public Object[][] queryDatas(String tableName) {Object[][] datas = null; // 用于存放最终查询结果的二维数组// 第一个SQL:查询表的记录总数(用于初始化二维数组的行数)String sql = "select count(*) from " + tableName;try {// 创建PreparedStatement对象(用于执行SQL语句)PreparedStatement pstmt = conn.prepareStatement(sql);// 执行查询,返回结果集(ResultSet)ResultSet rs = pstmt.executeQuery();int count = 0; // 记录总数while (rs.next()) { // 遍历结果集(此处只有一条记录,即总数)count = rs.getInt(1); // 获取第一列的值(即count(*)的结果)}System.out.println("count为:" + count); // 打印记录总数// datas = new Object[count][];// 数据库差异性,oracle这个rs结合集是默认不可滚动,mysql的rs的结果集是可滚动// rs.beforeFirst();// 第二个SQL:查询表中所有记录String sql1 = "select * from " + tableName;PreparedStatement pstmt1 = conn.prepareStatement(sql1);ResultSet rs1 = pstmt1.executeQuery(); // 执行查询,获取所有记录的结果集// 获取结果集元数据(用于获取列数)ResultSetMetaData rsmd = rs1.getMetaData();int columns = rsmd.getColumnCount(); // 获取表的列数System.out.println("列数为:" + columns); // 打印列数// 初始化二维数组:行数=记录总数(count),列数=表的列数(columns)datas = new Object[count][columns];// 把数据赋值给二维数组int row = 0; // 记录当前行索引(从0开始)// 遍历所有记录(rs1.next()为true表示有下一条记录)while (rs1.next()) {// 遍历每一列(i从0到columns-1)for (int i = 0; i < columns; i++) {// 获取第i+1列的值(JDBC列索引从1开始),存入datas[row][i]datas[row][i] = rs1.getObject(i + 1);}row++; // 移动到下一行}} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {// 关闭数据库连接(避免资源泄漏)if (null != conn) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}return datas; // 返回填充好数据的二维数组}public static void main(String[] args) {Dao dao = new Dao();Object[][] datas = dao.queryDatas("t_emp");for (int i = 0; i < datas.length; i++) {for (int j = 0; j < datas[i].length; j++) {System.out.print(datas[i][j] + ",");}System.out.println();}}}
package com.hy.demo9;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.LinkedList;public class Dao1 {Connection conn;public Dao1() {try {Class.forName("com.mysql.cj.jdbc.Driver");conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/jk202508", "root", "Hy61573166!!!");} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException e) {e.printStackTrace();}}public LinkedList<Stu> queryDatas(String tableName) {String sql = "select * from " + tableName;// <类型>,同一个类型,映射数据库中表的字段LinkedList<Stu> lists = new LinkedList<Stu>();try {PreparedStatement pstmt = this.conn.prepareStatement(sql);ResultSet rs = pstmt.executeQuery();while (rs.next()) {Stu s = new Stu();s.setSid(rs.getInt(1));s.setSname(rs.getString(2));s.setSaddress(rs.getString(3));lists.add(s);}} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {if (null != conn) {try {conn.close();} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}return lists;}public static void main(String[] args) {Dao1 dao = new Dao1();LinkedList<Stu> lists = dao.queryDatas("t_stus");for (Stu s : lists) {System.out.println(s.getSid() + "," + s.getSname() + "," + s.getSaddress());}}}

 * 数据存放都是采用数组,原始的数组构建来说,不适合现在项目存放数据的要求。固定机制

* 数据存放都采用集合,动态机制,自动扩容机制,扩容大小不一样。 1.5/2

//可以过滤重复

package com.hy.demo19;import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;public class Test implements Runnable {CountDownLatch cd;static HashSet set = new HashSet();static Set set1;public Test(CountDownLatch cd) {this.cd = cd;set1 = Collections.synchronizedSet(set);}public void run() {for (int i = 0; i < 100; i++) {// synchronized或Lock
//			synchronized (this) {
//				list.add(i);
//			}set1.add(i);}cd.countDown();}public static void main(String[] args) {CountDownLatch cd = new CountDownLatch(4);Test t = new Test(cd);new Thread(t).start();new Thread(t).start();new Thread(t).start();new Thread(t).start();try {cd.await();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("这个集合的大小为:" + set1.size()); // 10}}

//HashSet 不保证元素的存储 / 迭代顺序(既不是插入顺序,也不是自然排序),其顺序由元素的哈希值(hashCode())和哈希表的结构决定。

package com.hy.demo21;import java.util.HashSet;
import java.util.LinkedHashSet;public class Test {public static void main(String[] args) {LinkedHashSet<String> set = new LinkedHashSet<String>();set.add("one");set.add("two");set.add("three");set.add("four");System.out.println(set);System.out.println("-------------");HashSet<String> set1 = new HashSet<String>();set1.add("one");set1.add("two");set1.add("three");set1.add("four");System.out.println(set1);}}

输出结果:

[one, two, three, four]
-------------
[four, one, two, three]

2. Map接口

2.1 具体实现类:

HashMap, 线程非同步的

Hashtable,线程同步的 (全部锁住)

concurrentHashMap;线程同步的(分段锁机制),性能优化角度

// 1.

package com.hy.demo22;import java.util.HashMap;
import java.util.Hashtable;
import java.util.concurrent.ConcurrentHashMap;/*** Map接口* *   一个键值对的接口:key:value*   *    直接实现子类:*    HashMap、Hashtable   一个线程非安全,一个是线程安全(全部加锁,锁定范围太大,降低了性能)*    ConcurrentHashMap  一个是线程安全(分段锁,拆分了锁的范围,提高了性能)* @author hy**/public class Test {public static void main(String[] args) {HashMap map1 = new HashMap();// HashMap键和值可以为空map1.put(null, null);System.out.println(map1.get(null));System.out.println("---------------");// Hashtable键和值都不能为空Hashtable map2 = new Hashtable();map2.put(null, null);System.out.println(map2.get(null));System.out.println("---------------");ConcurrentHashMap map3 = new ConcurrentHashMap();// ConcurrentHashMap键和值都不能为空map3.put(null, null);System.out.println(map3.get(null));}}

// 2.

核心区别:元素顺序的维护

特性HashSetLinkedHashSet
继承关系直接实现 Set 接口继承 HashSet,间接实现 Set 接口
底层结构仅基于哈希表(数组 + 链表 / 红黑树)哈希表 + 双向链表(额外维护插入顺序)
元素顺序不保证任何顺序保证插入顺序(迭代时与添加顺序一致)
性能插入 / 查询效率略高(无链表维护开销)性能略低于 HashSet(需维护链表结构)

3. 队列

暂不讲解

4.Collections

Collection和Collections是啥关系?

Collection是一个集合的接口,是一个父类接口。

Collections是集合的工具类,是一个具体的实现类,工具类提供了大量的工具方法


文章转载自:

http://shC8HrvM.wLdtp.cn
http://1nBnRRTD.wLdtp.cn
http://qZVmjGX0.wLdtp.cn
http://0KdWYVja.wLdtp.cn
http://GlM9MJou.wLdtp.cn
http://ZAtUD6FV.wLdtp.cn
http://PS19ksKq.wLdtp.cn
http://hZlSdYPJ.wLdtp.cn
http://nHDXdcSq.wLdtp.cn
http://KgDYuOL1.wLdtp.cn
http://xLkbLWYF.wLdtp.cn
http://DhGPAiKZ.wLdtp.cn
http://HWhoRWZy.wLdtp.cn
http://YzqCwqaP.wLdtp.cn
http://ygTNXoWr.wLdtp.cn
http://4NXI1EJa.wLdtp.cn
http://1rAPcEtt.wLdtp.cn
http://iVFxO7st.wLdtp.cn
http://fmLkVXrz.wLdtp.cn
http://U6ieT9Q3.wLdtp.cn
http://IL96MXVK.wLdtp.cn
http://AtvErWZf.wLdtp.cn
http://5bt9p5V7.wLdtp.cn
http://qLjE3ngv.wLdtp.cn
http://Odo1GjCE.wLdtp.cn
http://rcXSi76S.wLdtp.cn
http://rUyNAtCG.wLdtp.cn
http://o2NS2X6R.wLdtp.cn
http://mGmLhlq7.wLdtp.cn
http://rxhdM2dU.wLdtp.cn
http://www.dtcms.com/a/376878.html

相关文章:

  • docker桌面版 镜像配置
  • JVM 全面详解:深入理解 Java 的核心运行机制
  • JVM分代收集:原理与调优策略
  • 使用.NET标准库实现多任务并行处理的详细过程
  • 软件测试:功能测试详解
  • 数字图像处理-图像编码
  • 基于RDMA 通信的可负载均衡高性能服务架构
  • java多线程场景3-并发处理和异步请求
  • <uniapp><指针组件>基于uniapp,编写一个自定义箭头指针组件
  • 新手向:中文语言识别的进化之路
  • Jakarta EE 课程 --- 微型资料投递与分发(Mini Drop-off Box)
  • 【船类】监控录像下船舶类别检测识别数据集:近7k图像,6类,yolo标注
  • 《UE5_C++多人TPS完整教程》学习笔记51 ——《P52 使用我们的瞄准偏移(Using Our Aim Offsets)》
  • 腾讯云远程桌面连接不上?5步排查法解决RDP连接失败
  • ffplay播放pcm
  • 计算机毕业设计 基于Hadoop的B站数据分析可视化系统的设计与实现 Python 大数据毕业设计 Hadoop毕业设计选题【附源码+文档报告+安装调试】
  • 【Halcon 】Halcon 裁剪尺寸的像素陷阱全解析:为什么要 -0.5,为什么要 -1,而圆却不用?
  • 机器视觉质检数据融合PLM:产品缺陷根因分析新范式
  • 【超详细图文教程】2025年最新Win10 系统安装 MySQL 教程
  • 医疗行业面临的网络安全挑战及应对策略
  • JVM CMS垃圾回收器深度解析
  • 鸿蒙Next ArkWeb进程解析:多进程架构如何提升Web体验
  • Credo发布专为低功耗、高带宽与超低时延的AI网络打造的Bluebird 1.6T光DSP芯片
  • Shell 循环语句与函数全解析
  • Zookeeper核心知识全解:节点类型、集群架构与选举机制
  • Android 项目中 Gradle 配置实战:多渠道打包、签名配置、版本管理
  • 新手向:实现验证码程序
  • 【小程序】微信小程序隐私协议
  • LeetCode 刷题【71. 简化路径】
  • 【LeetCode 每日一题】1493. 删掉一个元素以后全为 1 的最长子数组——(解法一)预处理