1.认识集合
public class CollectionDemo2 {
public static void main(String[] args) {
// 目标:搞清楚Collection提供的通用集合功能。
Collection<String> list = new ArrayList<>();
// 添加元素
list.add("张三");
list.add("李四");
list.add("王五");
System.out.println(list); // [张三, 李四, 王五]
// 获取集合的元素个数
System.out.println(list.size());
// 删除集合元素
list.remove("李四");
System.out.println(list);
// 判断集合是否为空
System.out.println(list.isEmpty()); // false
// 清空集合
// list.clear();
// System.out.println(list);
// 判断集合中是否存在某个数据
System.out.println(list.contains("张三"));
// 把集合转换成数组
Object[] arr = list.toArray();
System.out.println(Arrays.toString(arr));
// 把集合转换成字符串数组(拓展)
String[] arr2 = list.toArray(String[]::new);
}
}
2.集合遍历方式
2.1迭代器遍历
public class CollectionTraversalDemo3 {
public static void main(String[] args) {
// 目标:掌握Collection的遍历方式一:迭代器遍历
ArrayList<String> names = new ArrayList<>();
names.add("张无忌");
names.add("玄冥二老");
names.add("宋青书");
// names.add("殷素素");
System.out.println(names); // [张无忌, 玄冥二老, 宋青书]
// it
// 1、得到这个集合的迭代器对象
Iterator<String> it = names.iterator();
// System.out.println(it.next());
// 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 name = it.next();
System.out.println(name);
}
}
}
2.2增强for循环
public class CollectionTraversalDemo4 {
public static void main(String[] args) {
// 目标:掌握Collection的遍历方式二:增强for
Collection<String> names = new ArrayList<>();
names.add("张无忌");
names.add("玄冥二老");
names.add("宋青书");
names.add("殷素素");
for (String name : names) {
System.out.println(name);
}
String[] users = {"张无忌", "玄冥二老", "宋青书", "殷素素"};
for (String user : users) {
System.out.println(user);
}
}
}
2.3Lambda表达式
public class CollectionTraversalDemo5 {
public static void main(String[] args) {
// 目标:掌握Collection的遍历方式三:lambda
Collection<String> names = new ArrayList<>();
names.add("张无忌");
names.add("玄冥二老");
names.add("宋青书");
names.add("殷素素");
// names.forEach(new Consumer<String>() {
// @Override
// public void accept(String s) {
// System.out.println(s);
// }
// });
// names.forEach(s -> System.out.println(s));
names.forEach(System.out::println);
}
}
2.4三种遍历方式的区别(重点)
首先观察下面这段代码,会出现并发修改问题,当把宁夏枸杞删掉时,黑枸杞会补上来,这样就删不到黑枸杞了
后面的数据补上来会存在跨位问题
// 目标:认识并发修改异常问题,搞清楚每种遍历的区别
ArrayList<String> list = new ArrayList<>();
list.add("Java入门");
list.add("宁夏枸杞");
list.add("黑枸杞");
list.add("人字拖");
list.add("特级枸杞");
list.add("枸杞子");
list.add("西洋参");
System.out.println(list);
// 需求1:删除全部枸杞
for (int i = 0; i < list.size(); i++) {
String name = list.get(i);
if(name.contains("枸杞")){
list.remove(name);
}
}
System.out.println(list); //出现并发修改异常问题。
// [Java入门, 宁夏枸杞, 黑枸杞, 人字拖, 特级枸杞, 枸杞子, 西洋参]
// [Java入门, 黑枸杞, 人字拖, 枸杞子, 西洋参]
// i
// [Java入门, 黑枸杞, 人字拖, 枸杞子, 西洋参]
解决方案一
删除完,做一个i--操作
ArrayList<String> list2 = new ArrayList<>();
list2.add("Java入门");
list2.add("宁夏枸杞");
list2.add("黑枸杞");
list2.add("人字拖");
list2.add("特级枸杞");
list2.add("枸杞子");
list2.add("西洋参");
System.out.println(list2);
// 需求1:删除全部枸杞
for (int i = 0; i < list2.size(); i++) {
String name = list2.get(i);
if(name.contains("枸杞")){
list2.remove(name);
i--; // 解决方案1:删除数据后做一步i--操作 (前提是支持索引)
}
}
解决方案二
倒着遍历并删除(前提是支持索引)
ArrayList<String> list3 = new ArrayList<>();
list3.add("Java入门");
list3.add("宁夏枸杞");
list3.add("黑枸杞");
list3.add("人字拖");
list3.add("特级枸杞");
list3.add("枸杞子");
list3.add("西洋参");
System.out.println(list3);
// 需求1:删除全部枸杞
// 解决方案2:倒着遍历并删除(前提是支持索引)
for (int i = list3.size() - 1; i >= 0; i--) {
String name = list3.get(i);
if(name.contains("枸杞")){
list3.remove(name);
}
}
// [Java入门, 人字拖, 西洋参]
// i
System.out.println(list3);
解决方案三
当用set集合时,是没有索引的,就要想一下这三种方式能不能解决这个问题了
当使用迭代器时
System.out.println("=====================================================");
ArrayList<String> list4 = new ArrayList<>();
list4.add("Java入门");
list4.add("宁夏枸杞");
list4.add("黑枸杞");
list4.add("人字拖");
list4.add("特级枸杞");
list4.add("枸杞子");
list4.add("西洋参");
System.out.println(list4);
// 需求1:删除全部枸杞
// 方案一:迭代器遍历并删除默认也存在并发修改异常问题。
// 可以解决,解决方案3:使用迭代器自己的方法来删除
Iterator<String> it = list4.iterator();
while(it.hasNext()){
String name = it.next();
if(name.contains("枸杞")){
list4.remove(name);
}
}
System.out.println(list4);
此时运行,会报错,但是可以解决,只需
使用迭代器自己的方法来删除当前数据(底层是可以记索引的)
if(name.contains("枸杞")){
it.remove(); // 可以解决 解决方案3:使用迭代器自己的方法来删除当前数据
}
先说结论,
增强for循环和lambda表达式都无法解决这个问题
首先,增强for循环无法拿到迭代器对象,lambda表达式底层也是增强for循环,同理也拿不到迭代器对象.
2.4.1总结
如果又要遍历又要操作数据,且集合没有索引,只能用迭代器了
增强for和Lambda只适合做遍历,不适合做遍历并修改操作
3.List集合
小试牛刀
public class ListDemo1 {
public static void main(String[] args) {
// 目标:掌握List系列集合独有的功能。
List<String> names = new ArrayList<>(); //实现类对象给到父接口
// 添加数据
names.add("张三");
names.add("李四");
names.add("王五");
names.add("赵六");
System.out.println(names); // [张三, 李四, 王五, 赵六]
// 给第三个位置插入一个数据:赵敏
names.add(2, "赵敏");
System.out.println(names);
// 删除李四
System.out.println(names.remove(1)); // 根据下标删除,返回删除的数据
System.out.println(names);
// 把王五修改成:金毛
System.out.println(names.set(2, "金毛")); // 根据下标修改,返回修改前的数据
System.out.println(names);
// 获取张三
System.out.println(names.get(0));
System.out.println("-----------四种遍历演示---------------");
// 1、for循环
for (int i = 0; i < names.size(); i++) {
System.out.println(names.get(i));
}
// 2、迭代器
Iterator<String> it = names.iterator();
while (it.hasNext()) {
String name = it.next();
System.out.println(name);
}
// 3、增强for
for (String name : names) {
System.out.println(name);
}
// 4、lambda表达式
names.forEach(name -> System.out.println(name) );
System.out.println(15 >> 1);
}
}
3.1ArrayList底层原理
第一次加数据的时候才创建数组(扩容),扩容成10,加满之后,会扩容成原来的1.5倍
3.2LinkedList底层原理
而我们的LinkedList是基于双链表实现的
对收尾元素查询基本是O(0)
可以用来设计队列,因为只是在首尾增删元素
也可以用来设计栈
小试牛刀
public class ListDemo2 {
public static void main(String[] args) {
// 目标:用LinkedList做一个队列对象。
LinkedList<String> queue = new LinkedList<>();
// 入队
queue.addLast("赵敏");
queue.addLast("西门吹雪");
queue.addLast("陆小凤");
queue.addLast("石观音");
System.out.println(queue); // [赵敏, 西门吹雪, 陆小凤, 石观音]
// 出队
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue);
System.out.println("-------------------------------------------------");
// 做一个栈
LinkedList<String> stack = new LinkedList<>();
// 压栈
stack.push("第1颗子弹");
stack.push("第2颗子弹");
stack.push("第3颗子弹");
stack.push("第4颗子弹");
System.out.println(stack); // [第4颗子弹, 第3颗子弹, 第2颗子弹, 第1颗子弹]
// 出栈
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack);
}
}
简答题
1.ArrayList的大小是如何自动增加的?
当有人试图在arraylist中增加一个对象的时候,Java会去检查arraylist,以确保已存在的数组中有足够的容量来存储这个新的对象。如果没有足够容量的话,那么就会新建一个长度更长的数组,旧的数组就会使用Arrays.copyOf方法被复制到新的数组中去,现有的数组引用指向了新的数组。
2.ArrayList和LinkedList的区别?
a.List是接口类,ArrayList和LinkedList是List的实现类。
b.ArrayList是动态数组(顺序表)的数据结构。顺序表的存储地址是连续的,所以在查找比较快,但是在插入和删除时,由于需要把其它的元素顺序向后移动(或向前移动),所以比较熬时。
c.LinkedList是链表的数据结构。链表的存储地址是不连续的,每个存储地址通过指针指向,在查找时需要进行通过指针遍历元素,所以在查找时比较慢。由于链表插入时不需移动其它元素,所以在插入和删除时比较快。