20220806 第五组 罗大禹 学习笔记
20220806 第五小组 罗大禹 学习笔记
Java 集合
学习重点
1.比较接口
2.集合
学习内容
Java 集合
比较接口:
Comparable接口:自然排序,排序规则是固定。
实现Comparable接口,重写compareTo方法
compareTo方法返回值代表什么?
返回值类型为int
代表排序结果
负数-1:比较的两个值,调用者小
0:两个值相等
正数1:比较的两个值,调用者大
举例说明
public class Person implements Comparable<Person> {
private String name;
private Integer id;
@Override
public int compareTo(Person p) {
if(this.id > p.id){
return -1;
}
if(this.id.equals(p.id)){
return 0;
}
return 1;
}
}
Comparator接口:临时排序
举例说明:
// 用的时候重写sort方法传入的实参,传入的实参应该为Comparator对象
list.sort((o1,o2) -> {
if(o1.getId() < o2.getId()){
return -1;
}
if(o1.getId().equals(o2.getId())){
return 0;
}
return 1;
});
集合框架(重要)
集合:
容器,存放数据的一个容器
使用集合的目的:
更方便的存储和操作数据,CRUD。
集合继承结构:
1.Collection:(泛型接口)
存放单值的最大父接口
List(泛型接口):
和数组类似,List可以动态增长,查找元素效率高。插入删除元素的效率低,因为会引起其他元素位置的改变。
有顺序,元素可以重复,顺序指的是添加的先后顺序
Set(泛型接口):
检索元素效率低,删除和插入的效率高,插入和删除不会引起元素移位。
没有顺序,元素不可以重复,顺序指的是添加的先后顺序。
Set其实是有顺序,内部有一个专门排序的算法。
- 所谓的无序不等于随机
- 所谓的无序指的是没有按照添加的先后顺序,其实内部是做了排序的。
2.Map<K,V>:
存放对值的最大父接口
Map(映射):用于保存具有映射关系的数据,Map保存着两组数据:key和value。key和value都可以是任意的引用数据类型,但key不能重复。
注意:List,Set继承自Collection,Map不是
ArrayList:内部结构是一个数组
默认长度为10
实现了List
举例说明:
package com.jsoft.afternoon;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Ch02 {
public static void main(String[] args) {
// 创建了一个ArrayList集合
// 开发中,一般情况下,使用多态创建集合
// 向上转型
List<Integer> l1 = new ArrayList<>();
// 集合的新增
l1.add(1);
l1.add(2);
l1.add(3);
l1.add(4);
l1.add(5);
l1.add(1,-1);
List<Integer> l2 = new ArrayList<>();
l2.add(-1);
l2.add(-2);
l2.add(-3);
l2.add(-4);
l2.add(-5);
// 在指定位置添加数据
l2.add(1,100);
// 将l2集合添加至l1集合尾部
l1.addAll(l2);
// 清空集合
// 清空之后,集合中没有数据size==0,集合为null
l1.clear();
// 删除指定数据
l1.remove(Integer.valueOf(100));
// 取出指定位置的元素
l1.get(0);
// 用指定元素替换指定位置的元素
l1.set(0,200);
// 判断集合是否为空
l1.isEmpty();
// 判断集合中是否有该元素,返回值为boolean类型
l1.contains(1);
// 查找数据,返回下标
l1.indexOf(2);
// 打印输出集合长度
System.out.println(l1.size());
// 集合和数组之间的转换
// 集合--->数组
// 将l1集合转为数组
Object[] objects = l1.toArray();
// 数组--->集合
// 将数组存进集合
int [] arr = new int[]{1,2,3,4,5};
List<int[]> arr1 = List.of(arr);
// 打印输出arr第0位数据
System.out.println(arr1.get(0)[0]);
// 将数据存进集合
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> integers1 = List.of(1, 23, 4, 5);
}
}
数组和集合之间的转换,建议自己封装一个工具类
import java.util.ArrayList;
import java.util.List;
public class ArrayConvertUtil {
// 传入集合,将集合转为数组
public static Object[] toArrayConvert(List list) {
Object[] objects = new Object[list.size()];
for (int i = 0; i < list.size(); i++) {
objects[i] = list.get(i);
}
return objects;
}
// 传入数组,将数组转为集合
public static List toListConvert(Object [] arr) {
List list = new ArrayList();
for (Object o : arr) {
list.add(o);
}
return list;
}
public static void main(String[] args) {
List<Integer> l1 = new ArrayList<>();
l1.add(1);
l1.add(2);
l1.add(3);
l1.add(4);
l1.add(5);
l1.add(1,-1);
Object[] objects = ArrayConvertUtil.toArrayConvert(l1);
System.out.println(objects[0]);
}
}
注意:
- 如果初始化集合尽量指定初始化容量,如果确定不了,默认指定为16
- 使用泛型时候,一定要使用引用数据类型。
面试题:
-
List和Set的区别
List:有顺序,元素可以重复,顺序指的是添加的先后顺序
Set:没有顺序,元素不可以重复,顺序指的是添加的先后顺序。
-
HashSet(内部结构Hash表)和LinkedHashSet的区别和联系
package com.jsoft.afternoon;
import java.util.HashSet;
import java.util.Set;
public class Ch02 {
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
set.add(Integer.valueOf(200));
set.add(4);
set.add(15);
set.add(38);
set.add(Integer.valueOf(200));
System.out.println(set);
}
}
List
数据是有顺序(添加的先后顺序)的,数据是可以重复。
ArrayList:内部结构是数组。比较适合做高频率的查找,遍历。
LinkedList:内部结构是双向链表。比较适合做高频率的新增和删除。
Vector:和ArrayList几乎一模一样。
面试题:
-
Collection和Map接口的区别
Collection:存储单值的最大父接口
Map:存储对值的最大父接口
-
ArrayList和LinkedList的区别
ArrayList:内部结构是数组。比较适合做高频率的查找,遍历。
LinkedList:双向链表。比较适合做高频率的新增和删除。 -
ArrayList和Vector的区别
ArrayList是线程异步的,线程不安全,效率高
Vector是线程同步的,线程安全,效率低
举例说明:
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
public class Ch01 {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
// 若我们在创建对象的时候用的是多态
// 父类对象---子类引用
// 那么我们创建出来的对象只能够调用父类和子类中都有的方法
// LinkedList中的方法
list.addFirst("z");
list.addLast("x");
list.removeFirst();
list.removeLast();
// 根据下标索引获取指定位置的元素
System.out.println(list.get(2));
System.out.println(list);
}
}
Set
没有顺序,元素不可以重复,顺序指的是添加的先后顺序
若传入的是引用数据类型,默认情况下比较地址
Set集合如何确保数据的不重复?
引用数据类型的类要重写hashCode和equals方法。
- HashSet
- TreeSet
- LinkedHashSet
举例说明
package com.jsoft.afternoon;
public class Person implements Comparable<Person> {
private String name;
private Integer id;
public Person(String name, Integer id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (!name.equals(person.name)) return false;
return id.equals(person.id);
}
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + id.hashCode();
return result;
}
}
HashSet
无法get,没有输入先后顺序
- HashSet集合底层采用了哈希表这种数据结构
- HashSet在实例化时,底层实际实例化了个HashMap
- 往HashSet集合中存储元素,实际存放到了HashMap集合的key部分
TreeSet
排序规则:要排序的对象的类必须实现Comparable接口
- 无序 不可重复 可自动排序(从小到大排序,字符串根据ACSII码从小大排序)
- TreeSet集合底层采用了二叉树这种数据结构
- TreeSet在实例化时,底层实际实例化了个TreeMap
- 往TreeSet集合中存储元素,实际时存放到了TreeMap集合的key部分
LinkedHashSet:
由链表和Hash表实现
继承HashSet,有输入先后顺序,但也不能get,在添加数据的同时维护数据的添加顺序,效率要比HashSet略低一些。
Map(泛型接口)
put根据key来取value
- 存储对值K-V
- key不能重复,value是可以重复的
- 没有顺序(添加的先后顺序)
简单介绍几个子类:
- HashMap
- Hashtable
- Properties
HashMap内部存储结构
HashMap集合底层采用了哈希表这种数据结构
HashMap内部存储结构:
jdk1.7之前:链表 + 二叉树
jdk1.8及之后:链表 + 数组 + 红黑树
HashMap基本上面试上90%会问原理
举例说明:
import java.util.HashMap;
import java.util.Map;
public class Ch07 {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("1001","张岳");
map.put("1002","赵红兵");
map.put("1003","小北京");
map.put("1004","李四");
System.out.println(map);
}
}
Hashtable实现类:(唯一一个没有驼峰的类)
Hashtable和HashMap几乎一模一样
Hashtable集合底层采用了哈希表这种数据结构
面试题:Hashtable和HashMap区别?
1.HashMap是线程异步的,线程不安全的
Hashtable是线程同步的,线程安全的
2.HashMap的key可以为null的,
Hashtable的key是不可以为null的
Properties:属性
Properties是Hashtable的子类,更多地是用来操作属性文件,用IO流读取属性文件
其他的集合:
- LinkedHashMap,在HashMap的基础上维护了一个双向链表。
- TreeMap:天然支持排序
- Collections:Collections是一个工具类
遍历
List集合的遍历
举例说明:
package com.jsoft.afternoon;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Ch10 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
// 1.for循环
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
// 2.foreach语句
for (String s : list) {
System.out.println(s);
}
// 3.迭代器
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) {
String s = iterator.next();
iterator.remove();
System.out.println(s);
}
}
}
Set集合的 遍历
举例说明:
package com.jsoft.afternoon;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Ch11 {
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
set.add(4);
set.add(15);
set.add(38);
// 2.迭代器
Iterator<Integer> iterator = set.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
// 1.增强for
for (Integer integer : set) {
System.out.println(integer);
}
}
}
Map集合的遍历
举例说明:
import java.util.*;
public class Ch12 {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1001","张岳");
map.put("1002","赵红兵");
map.put("1003","小北京");
map.put("1004","李四");
map.put("1005","张浩然");
// 4.迭代器
Set<String> strings = map.keySet();
Iterator<String> iterator = strings.iterator();
while(iterator.hasNext()) {
String s = iterator.next();
System.out.println(s + "->" + map.get(s));
}
// 3.增强for循环
// Entry是hashmap的一个内部类
// 每一组键值对就是一个Entry对象
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
System.out.print(entry.getKey() + "->");
System.out.println(entry.getValue());
}
// 2.增强for循环
Set<String> strings = map.keySet();
Collection<String> values = map.values();
// 1.for循环
Set<String> strings = map.keySet();
for (String s : strings) {
System.out.println(s + "->" + map.get(s));
}
}
}
迭代中删除元素
举例说明:
package com.jsoft.afternoon;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
public class Ch13 {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("tom");
names.add("lucy");
names.add("lucy");
names.add("lucy");
names.add("jerry");
// 1.for循环
for (int i = 0; i < names.size(); i++) {
if(Objects.equals(names.get(i),"lucy")){
names.remove(i);
// 1.回调指针
i--;
}
if("lucy".equals(names.get(i))){
}
}
// 2.逆序遍历
for (int i = names.size() - 1; i >= 0; i--) {
if(Objects.equals(names.get(i),"lucy")){
names.remove(i);
}
}
// 3.使用迭代器(推荐)万无一失
Iterator<String> iterator = names.iterator();
while(iterator.hasNext()) {
String s = iterator.next();
if(Objects.equals(s,"lucy")){
iterator.remove();
}
}
// 4.增强for循环
for (String s : names) {
if(Objects.equals(s,"lucy")){
names.remove(names.indexOf(s));
}
}
System.out.println(names);
}
}
面试题
线程安全的问题
并发修改异常
用普通方法遍历,但是用迭代器的方法删除元素,普通方法还在遍历,不知道新增或者删除了元素,相当于多线程操作,会出现并发修改异常
反之一样
foreach循环底层是迭代器方法,不能用普通方法删除元素
迭代器是依赖于集合存在,判断成功以后,如果是新增或删除了元素,但是迭代器不知道,所以报错了
解决:
1.迭代器遍历元素,迭代器删除元素
2.普通for循环遍历,集合删除
集合目前需要掌握的:
-
如何创建需要的集合:主要用多态
主要用List,Map
-
各种区别(面试题)
-
各种集合方法
-
两个比较接口
-
各种集合的特点:从接口的层面到实现类的层面
-
各种集合API的调用
-
重点集合的内部结构。ArrayList、HashSet、HashMap
-
各种集合的遍历
-
并发问题
-
接口、实现类的方法要去看,有个印象
最重要的集合:
ArrayList和HashMap
积压的问题
- synchronize原理
- ReentrantLock原理
- ArrayList原理
- LinkedList原理
- HashMap原理:重点,面试重灾区
- HashSet原理:底层先是HashMap