目录

List
什么是List?
List是有序集合,用户可以精准控制每个元素在列表中插入的位置。用户可以通过索引(下标)在列表中访问这些元素,并可以在列表中搜索。
List集合可以存储重复的元素。
List分为ArrayList和LinkedList。
总结:List :有序 ,可重复,有索引。
可用方法:
add(index,element);//插入元素
remove(index);//删除该索引(index)处元素
set(index,element);//修改指定索引处元素,返回被修改元素
get(index);//返回指定索引处元素
indexOf(element);//返回指定元素第一次出现处的索引
ArrayList
性质:1.底层使用数组,所以特性接近数组(实际用起来就是不用手动设置的大小的数组)
2.储存的元素有序,可重复,通过索引(下标)可实现元素的增删改查
优劣:1.查询快以及修改 2. 增删慢 3.有索引
原因:根据索引可以快速找到指定元素并且修改,但是增加或删除有可能需要改变元素位置,导致扩容或者缩容。
方法使用:
public static void main(String[] args) {
List<String> list =new ArrayList<>();//创建ArrayList,梦的开始
//直接添加元素
list.add("jl");
list.add("jy");
list.add("sj");
System.out.println(list);
//在指定索引处添加元素
list.add(1,"新人");
System.out.println(list);
//输入元素查询索引
System.out.println(list.indexOf("jl"));//如果不存在该元素,返回-1
//获取指定索引处元素
System.out.println(list.get(1));
//删除指定索引处元素
System.out.println(list.remove(0));
//修改指定索引处元素
System.out.println(list.set(2,"小刘"));
//底层是数组,可以使用for循环
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
ArrayList扩容底层原理:
1.集合在创建好,添加第一个元素时,创建一个长度默认为10的数组
2.如果存满,扩容1.5倍
3.如果一次添加多个元素,1.5倍不够用,就会按照实际元素长度创建数组大小
适用场景:
适用于多查询,少新增与删除的商务场景
LinkedList
性质:1.使用双向链表来存储元素而不是数组。每个元素都是第一个节点,节点之间通过指针相连接
2.在LinkedList中,每个节点包含了元素本身以及指向前一个节点和后一个节点的引用。这使得在LinkedList中插入和删除元素的操作更加高效,因为不需要像数组一样移动元素位置,只需要修改节点的引用,但是查询速度却慢了,因为是链表,需要从头开始(或者从尾部开始)一个一个查,运气不好需要遍历一整个链表。
优劣:查询慢,增删快 ,链表结构
方法使用:大部分与ArrayList相同,但是有针对首尾特有方法
addFirst(element)//将指定元素插入到列表开头
addLast(element)//将指定元素插入到列表尾部
getFirst()//获取列表中第一个元素
getLast()//获取列表中最后一个元素
removeFirst()//删除并返回列表中第一个元素
removeLast()//删除并返回列表中最后一个元素
适用场景:
1.队列(先进先出,后进后出)
public static void main(String[] args) {
LinkedList queue = new LinkedList();
//尾部入队
queue.addLast("01-jl");//从尾部添加元素
queue.addLast("02-jy");
queue.addLast("03-sj");
queue.addLast("04-小王");
System.out.println(queue);
//头部出队
for (int i = 0; i < 3 ; i++) {
System.out.println(queue.removeFirst());//删除列表首个元素
}
System.out.println(queue);
}
2.栈(先入后出)
public static void main(String[] args) {
LinkedList stack = new LinkedList<>();
//压栈push
stack.addFirst("01-jl");
stack.addFirst("02-jy");
stack.addFirst("03-sj");
stack.addFirst("04-小王");
System.out.println(stack);
//出栈pop
for (int i = 0; i < 3; i++) {
System.out.println(stack.removeFirst());
}
System.out.println(stack);
}
但是其实在LinkedList里面给我们封装好了push和pop方法可以直接使用:
public static void main(String[] args) {
LinkedList stack = new LinkedList<>();
//压栈push
stack.push("01-jl");
stack.push("02-jy");
stack.push("03-sj");
stack.push("04-小王");
System.out.println(stack);
//出栈pop
for (int i = 0; i < 3; i++) {
System.out.println(stack.pop());
}
System.out.println(stack);
}
迭代器与增强for
迭代器
什么是迭代器?
迭代器是用来遍历集合的专用方式
为什么不用for遍历?
不用for循环遍历的原理是因为集合只有List支持索引
方法:
Iterator<E> iterator()//返回集合中的迭代器对象
boolean hasNext()//询问当前位置是否有元素,存在true,否则为false
E next()//返回集合中的下一个元素
迭代器原理:
1.迭代器(的指针)默认指向当前集合的第一个元素之前
2.next()方法可以移动迭代器获取下一个元素
3.但是next()方法一旦超过集合范围,就会出现异常
使用:
public static void main(String[] args) {
Collection<String> c =new ArrayList<>();
c.add("01");
c.add("02");
c.add("03");
c.add("04");
System.out.println(c);
//获取迭代器对象
Iterator<String> it = c.iterator();
/* System.out.println(it.next());得到一个元素
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());*/
//如果数量庞大,我们不可能一个一个去写
//通过循环+迭代器方式(能确定循环次数采用for循环,此处并不能)
while(it.hasNext()) { //判断是否还有下一个元素,没有就break
String a = it.next();
System.out.println(a);
}
}
增强for
什么是增强for?
本质是简化版迭代器,是遍历集合的一种手段,也可以遍历数组,关键要记住格式。
格式:
for(元素的数据类型 变量名:数组或集合){
}
public static void main(String[] args) {
Collection<String> c =new ArrayList<>();
c.add("01");
c.add("02");
for (String n : c) {
System.out.println(n);
}
}
Set
什么是Set?
1.Set是一个接口,用于存储一组不重复的元素。
2.Set接口继承自Collection接口,因此它具有Collection接口定义的所有方法,例如添加,删除和迭代等。
3.无序 不重复 无索引
方法:
Set基本没有自己特有的方法,基本上所有的方法都来自Collection
HashSet
性质:无序 不重复 无索引
public static void main(String[] args) {
Collection set = new HashSet();//最常用 无序、添加顺序与获取顺序不同、不重复
set.add(111);
set.add(222);
set.add(333);
set.add(111);
set.add(444);
set.add(444);
System.out.println(set);
}
观察结果发现重复的都去掉了,并且输出结果顺序与输入顺序不同
HashSet原理
在了解HashSet原理前先了解下什么是Hash值
什么是Hash值?
1.本质上是一个int类型的整数,Java的每个对象都有一个哈希值
2.Java中所有对象都可以调用Object提供的hashCode方法,返回自己对象的哈希值
Hash值特点:
1.同一个对象多次调用hashCode(),返回的哈希值相同
2.不同的对象,一般情况哈希值不同,但也有可能相同,这种情况称为哈希碰撞
(为什么会碰撞?超过int的最大范围(-21亿~21亿)就会出现重复)
通俗的讲,hash值是每一个对象自己的标识
HashSet原理:
1.基于哈希表(散列表)实现。哈希表是一种高效的数据结构,用于快速存储和检索数据,哈希表=数组+链表+红黑树
2.哈希表会默认创建一个长度为16的数组,默认加载因子为0.75,如果我想存入元素,需要使用该元素的哈希值对数组长度取余(取整数),计算对应存放位置,并判断此位置是否为null,为null直接存入。
如果不为null,则进行equals比较:相等,则不存入;不相等,则存入。需要注意,不想当并且存入时,并不是新元素替换旧元素,而是在挂在旧元素数组位置下方,组成链表(相同的hash值并且equals不相等就会形成链表)。
如果底层数组满了怎么办?
若默认底层数组存储数据达到12个(16*0.75),那么底层数组会进行扩容,一般扩一倍以上。
如果链表长度过长影响效率怎么办?
当链表长度>8,且数组长度>=64时,自动将链表转为红黑树,提高查询效率
tips:
哈希表性能好就是因为根据每个对象的哈希值可以快速定位元素位置
LinkedHashSet
性质:有序 不重复 无索引
public static void main(String[] args) {
Collection set = new LinkedHashSet();
set.add(111);
set.add(222);
set.add(333);
set.add(111);
set.add(555);
set.add(444);
set.add(444);
System.out.println(set);
}
发现还是不重复,但是与输入顺序有关
TreeSet
性质:排序 不重复 无索引(当存入自定义对象的时候需要手动排序)
public static void main(String[] args) {
Collection set = new TreeSet();
set.add(111);
set.add(222);
set.add(333);
set.add(111);
set.add(555);
set.add(444);
set.add(444);
System.out.println(set);
}
}
观察结果发现不重复,并且输出结果排序输出
如果使用自定义对象
public class Person {
int age;
String name;
public Person() {
}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
Set<Person> set = new TreeSet<>();
set.add(new Person(18,"jl"));
set.add(new Person(19,"jy"));
set.add(new Person(17,"sj"));
set.add(new Person(14,"小王"));
System.out.println(set);
会直接报错,因为自定义对象的排序规则TreeSet并不知道,有两种解决办法:
1.实现Comparable接口:
让自定义的类实现Comparable接口重写其中的compareTo方法来指定比较规则
public class Person implements Comparable<Person>{
int age;
String name;
public Person() {
}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
/**
* 让当前对象对比的元素-o.具体属性
* 返回值为正,升序
* 返回值为负,降序
* 返回值为0,说明相等,只会保留一个元素
* @param o the object to be compared.
* @return
*/
@Override
public int compareTo(Person o) {
return this.age-o.age;
}
}
此时结果正常输出并按照年龄排序。
2.设置Comparator对象:
通过调用TreeSet集合的有参构造,设置Comparator对象,此对象为比较器对象,用于指定比较规则
public static void main(String[] args) {
Set<Person> set = new TreeSet<>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge()- o2.getAge();
}
});
set.add(new Person(18,"jl"));
set.add(new Person(19,"jy"));
set.add(new Person(17,"sj"));
set.add(new Person(14,"小王"));
System.out.println(set);
}
Map
Map是什么?
Map集合是一个双列无序集合,以键值对的形式存在。一组键值称之为一个元素,如果出现相同的键,新的值会覆盖老的值,每个键只能映射一个值,每个键不能重复。如图所示,
常用方法:
V put(K key,V value)//添加元素,如果键第一次出现返回null,否则新的值替换旧的值,返回旧的值
void clear()//移除所有的键值对元素
V remove(Object key) //根据键删除值,并返回值
boolean containsKey(Object key)//判断集合是否包含指定键
boolean containsValue(Object value)//判断集合是否包含指定值
boolean isEmpty()//判断集合是否为空
Set<Map.Entry<K,V>> entrySet()//获取所有的键值对
V get(Object key) //根据键获取值
Set<K> keySet()//获取集合中所有键的集合
Collection<V> values()//获取集合中所有值的集合
int size()//返回集合中键值对个数
方法使用:
public static void main(String[] args) {
//map集合的其他实现类几乎没有特有方法,也就是说以下(HashMap可以换成TreeMap等)通用
Map<String,Integer> map = new HashMap<>();//最常用
//添加元素
map.put("jy",18);
map.put("jl",19);
map.put("sj",17);
map.put("小王",18);
map.put("小明",20);
System.out.println(map);
//判断键是否存在
boolean flag =map.containsKey("jl");
System.out.println(flag);
//判断值是否存在 省略写法
System.out.println(map.containsValue("18"));
//判断集合是否为空
System.out.println(map.isEmpty());
//根据键获取值(常用)
System.out.println(map.get("jl"));
//获取集合元素个数
System.out.println(map.size());
//获取集合中的所有键值对
//entry对象表示一组键值对
Set<Map.Entry<String,Integer>> entrySet = map.entrySet();
System.out.println(entrySet);
//获取集合中所有键
Set<String> keySet =map.keySet();
System.out.println(keySet);
//获取集合中所有值
Collection<Integer> valuesSet =map.values();
System.out.println(valuesSet);
}
Map如何遍历?
1.先获取map集合中的所有键,再通过遍历键获取对应值
Map<String,Integer> map = new HashMap<>();
map.put("jy",18);
map.put("jl",19);
map.put("sj",14);
map.put("小孙",22);
map.put("小徐",16);
//第一种遍历方式
//获取所有键
Set<String> keys=map.keySet();
//根据所有键获取对应值
for (String key : keys) {
Integer value=map.get(key);
System.out.println(key+":"+value);
2.直接遍历整个元素的键值对,整体进行遍历
//第二种遍历方式
//获取所有的键值对
Set<Map.Entry<String,Integer>> entrySet = map.entrySet();
for (Map.Entry<String, Integer> stringIntegerEntry : entrySet) {
System.out.println(stringIntegerEntry.getKey()+":"+stringIntegerEntry.getValue());
}
HashMap
性质:无序 不重复 无索引 是最常用的实现类
HashSet的底层就是HashMap
那么为什么HashSet是单列集合,但是map是双列集合呢?
因为HashSet所存储的是HashMap的键,它只拿键。
LinkedHashMap
性质:根据键来进行排序 有序 不重复 无索引
TreeMap
性质:默认根据键升序排序 不重复 无索引
自定义排序规则:
1.实现Comparable接口:
让自定义的类实现Comparable接口重写其中的compareTo方法来指定比较规则
public class Person implements Comparable<Person>{
int age;
String name;
public Person() {
}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
/**
* 让当前对象对比的元素-o.具体属性
* 返回值为正,升序
* 返回值为负,降序
* 返回值为0,说明相等,只会保留一个元素
* @param o the object to be compared.
* @return
*/
@Override
public int compareTo(Person o) {
return this.age-o.age;
}
}
public static void main(String[] args) {
Map<Person,String> map =new TreeMap<>();
map.put(new Person(18,"jl"),"CN");
map.put(new Person(20,"jy"),"CN");
map.put(new Person(16,"james"),"US");
map.put(new Person(30,"王桑"),"JP");
System.out.println(map);
}
2.设置Comparator对象:
通过调用TreeSet集合的有参构造,设置Comparator对象,此对象为比较器对象,用于指定比较规则
public static void main(String[] args) {
Map<Person,String> map =new TreeMap<>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge()-o2.getAge();
}
});
map.put(new Person(18,"jl"),"CN");
map.put(new Person(20,"jy"),"CN");
map.put(new Person(16,"james"),"US");
map.put(new Person(30,"王桑"),"JP");
System.out.println(map);
}