1.java集合的概述
1.1 前言概述
1.这是我第二次看文档看视频带着分析去学习的记录,主要针对的不是入门,所以方法就不具体介绍了,但是会有演示代码,当然也有注解。只是都是初学时记录,希望大家可以指正。
2.主要记录的是一些原理,可能不够具体,所以也希望各位谏言之。
1.2 集合简要概述
(1)集合主要有两组(单列集合和双列集合)
(2)集合实际上就是一个容器,也是一个载体,可以容纳其他的类型数据
(3)集合中不能存基本数据类型,也不能存对象,它存的是对象的地址
(4)集合在java中本身是一个容器,是一个对象,是一个引用
(5)下面两张图看看继承实现情况
PS:Collection集合
PS:Map集合
1.3集合和数组的区别
(1)首先是长度区别:数组长度大小固定,集合可变
(2)存储的类型:数组可以存基本类型,也能存引用类型。而集合只能存引用类型
(3)存储的元素内容:数组只能存储同一种类型,也就是一旦创建初始化,那就只能往里面添加定义好的类型。而集合可以存储不同的类型
PS:其实本质上来说,集合存储的也是同一种类型。因为它存的是对象的地址。
2.Collection集合的方法
2.1 迭代器方法
(1)别的方法我就不一一介绍了,主要是理解一些它的注意点
(2)当我们使用iterator迭代器遍历数据的时候,有两点需要注意:
1.在遍历之前需要先判断还有没有下一个元素即:iterator.hasNext
2.在遍历完之后,如果还想再遍历一次,要记得重置即:再获取一遍迭代器即可:iterator = collection.iterator();
PS:看图深入理解一下
PS:先科普一点,增强for循环底层原理其实就是迭代器。
3.List集合
3.1 List集合的简述
(1)先了解一点,因为List集合继承了Collection集合,所以Collection集合有的方法List也有。比如迭代器方法
(2)List集合是一个有序集合(即先进先出),可重复的。
(3)list集合方法代码演示
package com.shallow.collection.list;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@SuppressWarnings({"all"})
public class List集合方法演示 {
public static void main(String[] args) {
// 因为List类是一个接口类,所以依赖于它的子类创建对象
List list = new ArrayList(); // 创建list集合
List list1 = new ArrayList(); // 创建list1集合
// 1.添加元素
list.add("aaa");
list.add(41); // 突出与数组的区别
list.add("123");
System.out.println(list); // 打印:[aaa, 41, 123]
// 2.在指定位置插入元素
list.add(1, 16); // [aaa, 16, 41, 123]
System.out.println(list);
// 3.将list1集合的元素添加至list集合
list1.add("李白"); // 先给list1集合添加元素
list1.add("杜甫");
list.addAll(list1);
System.out.println(list); // [aaa, 16, 41, 123, 李白, 杜甫]
// 4. 在list集合指定位置插入list1集合全部的元素
list.addAll(2, list1);
System.out.println(list); // [aaa, 16, 李白, 杜甫, 41, 123, 李白, 杜甫]
// 5. 返回list集合指定索引位置的元素
Object o = list.get(3);
System.out.println(o); //杜甫
// 6. 返回list集合中第一次出现“李白”对象的索引位置
int i = list.indexOf("李清照");
System.out.println(i); // 2 补充,如果没有该元素,则返回 -1
// 7. 删除指定索引的对象
list.remove(2);
System.out.println(list); // [aaa, 16, 杜甫, 41, 123, 李白, 杜甫]
// 8. 更改指定索引位置的元素
list.set(0, "李白");
System.out.println(list); // [李白, 16, 杜甫, 41, 123, 李白, 杜甫]
// 9. 返回指定开始到结束的所有元素
List list2 = list.subList(1, 5);
System.out.println(list2); // [16, 杜甫, 41, 123]
// 10. 迭代器遍历
Iterator iterator = list.iterator(); // 获取 Iterator对象
while (iterator.hasNext()) { // 判断是否还有下一个元素
Object next = iterator.next(); // 如果有指针移动并返回数据
System.out.println(next);
}
// 11. 增强for循环遍历
for (Object o1 : list) {
System.out.println(o1);
}
// 12.普通for循环遍历
for (int j = 0; j < list.size(); j++) {
System.out.println(list.get(j));
}
}
}
3.2 List的实现子类
3.2.1 ArrayList集合
1)底层采用数组这种数据结构:ArrayList中维护了一个Object类型的数组elementData
2)当创建ArrayList对象时,如果采用的是无参构造器,则初始化elementData容量为0,第1次添加,则扩容elementData为10,如需再次扩容,则扩容elementData为1.5倍.
3)如果使用的是指定大小的构造器,则elementData容量为指定的大小,如需再次扩容,则扩容elementData为1.5倍.
4)增删效率低,改查效率高
5)ArrayList集合是非线程安全的
6)补充知识点:transient关键字修饰的属性或者成员被它修饰了,表示该属性或者成员不会被序列化
3.2.2 LinkedList集合
1)底层采用双向链表数据结构:它维护了两个属性,first(首节点)和last(尾节点),每个节点(Node对象)里面又维护了prev,next,item三个属性,其中通过prev指向前一个,通过next指向后一个节点,最终实现双向链表
2)LinkedList元素的添加和删除,不是通过数组完成的,相对来说效率较高
3)因为它实现了List接口,所以它的元素也可以重复,包括null值
4)增删效率高,改查效率低
5)线程不安全
3.2.3 Vector集合
1)底层采用数组这种数据结构:ArrayList中维护了一个Object类型的数组elementData
2)增删效率低,改查效率高
3)Vector是线程安全的
4)当创建Vector对象时,如果采用的是无参构造器,底层调用会默认给个初始值10,如需再次扩容,则扩容elementData为2倍.
5)如果使用的是指定大小的构造器,则elementData容量为指定的大小,如需再次扩容,则扩容elementData为2倍.
4. Set集合
4.1 Set集合的简述
(1)无序:即添加和取出的顺序不一样,没有索引
(2)不允许重复值,所以最多也只能包含一个null值
(3)Set因为没有索引,所以它遍历不能使用普通for循环
(4)虽然它是无序的,但是一旦你取出一次的顺序出来了,然后你怎么取都是这个顺序
4.2 Set集合的实现子类
4.2.1HashSet集合
1)HashSet底层其实实际上是一个HashMap
2)HashSet可以放null,但是不能放多个null,即不可能重复
3)HashSet不保证元素是有序的,取决于Hash后,再确认索引结果
4)添加元素时,先得到hash值,会转成索引值,找到存储数据表table,看这个索引位置是否有元素,如果没有,则直接加入。如果有元素,即调用equals比较(记得要重写equals),如果相同,就放弃添加,如果不相同,就将其放到最后。如果添加时发现容量不够,开始扩容
5)查询元素时(map.get(key)):先调用k的hashCode()方法得出哈希值,通过哈希算法转化成数组下标,通过数组下标快速定位到某个位置上,如果这个位置上什么都没有,返回null,如果这个位置上有单向链表,那么会拿着参数k和单向链表上的每个节点中的k进行equals,如果所有equals都返回false,那么get方法返回null,只要其中有一个节点的k和参数kequals返回true,那么此时这个节点的value就是我们要找的value,get方法最终返回这个要的value
6)HashSet的底层是HashMap,所以在第一次添加的时候,table数组扩容到16,临界值时160.75(即12)。如果table数组使用到了临界值12,就会扩容到162(即32),然后新的临界值就是32*0.75(即24).依此类推。
7)在JDK8中,如果一条链表的元素个数到达(默认8),并且table的大小大于等于(默认64),就会进化成红黑树
4.2.2 TreeSet集合
1)TreeSet跟TreeMap是一样的
2)可以按照元素的大小自定排序称为可排序集合
3)TreeSet去重机制:如果你传入的一个Comparator匿名对象,就使用实现的comparator去重,如果方法返回0,就认为是相同的元素,就不添加,如果你没有传入一个Comparator匿名对象,则以你添加的对象实现的Comparable接口里的compareTo方法去重
PS:看代码理解第三个说明
package com.shallow.collection.set;
import sun.reflect.generics.tree.Tree;
import java.util.Comparator;
import java.util.Objects;
import java.util.TreeSet;
@SuppressWarnings({"all"})
public class Tree_Set {
public static void main(String[] args) {
// 默认无参构造器 默认升序排序
// TreeSet treeSet = new TreeSet();
// treeSet.add("a");
// treeSet.add("abc");
// treeSet.add("shallow");
// treeSet.add("zte");
// treeSet.add("tom");
// treeSet.add("jack");
// treeSet.add("smith");
// System.out.println(treeSet);
// 当使用对象的时候,排序方式需要自己定义,不然报错。自己定义的方式有多种
// 1.使用匿名内部类定义
// TreeSet<User> treeSet = new TreeSet<User>(new Comparator<User>() {
// @Override
// public int compare(User user1, User user2) {
// // 按分数(score)排序
return user1.getScore() - user2.getScore();
// // 按年龄排序
return user1.getAge() - user2.getAge();
// // 按年龄排序
return user1.getName().compareTo(user2.getName());
// // 按名字长度排序
// return user1.getName().length() - user2.getName().length();
// }
// });
//
// User user01 = new User("libai", 18, 98);
// User user02 = new User("liqingzhao", 16, 96);
// User user03 = new User("dufu", 20, 92);
// // 当按名字长度排序的时候,长度一样即为相同,即不会添加进入TreeSet集合
// // 别的元素相同也不会加入,同理
// User user04 = new User("dufu", 18, 98); // 不会加入
// treeSet.add(user01);
// treeSet.add(user02);
// treeSet.add(user03);
// treeSet.add(user04);
// System.out.println(treeSet);
// 当使用对象的时候,排序方式需要自己定义,不然报错。自己定义的方式有多种
// 2.定义的类先实现Comparable<T>接口,然后直接在定义的类里重写比较方法
// User user01 = new User("libai", 18, 98);
// User user02 = new User("liqingzhao", 16, 96);
// User user03 = new User("dufu", 20, 92);
// // 分数相同,不会加入,所以只有三条数据
// User user04 = new User("dufu", 18, 98);
// TreeSet<User> treeSet = new TreeSet<>();
// treeSet.add(user01);
// treeSet.add(user02);
// treeSet.add(user03);
// treeSet.add(user04);
// System.out.println(treeSet);
// 当使用对象的时候,排序方式需要自己定义,不然报错。自己定义的方式有多种
// 3.TreeSet构造方法中传入比较器接口实现类的对象,使用Lambda表达式
// User user01 = new User("libai", 18, 98);
// User user02 = new User("liqingzhao", 16, 96);
// User user03 = new User("dufu", 20, 92);
// // 分数相同,不会加入,所以只有三条数据
// User user04 = new User("dufu", 18, 98);
//
// // 按成绩进行排序
// TreeSet<User> treeSet = new TreeSet<>((user1, user2) ->
// (user1.getScore() - user2.getScore()));
// treeSet.add(user01);
// treeSet.add(user02);
// treeSet.add(user03);
// treeSet.add(user04);
// System.out.println(treeSet);
// 当使用对象的时候,排序方式需要自己定义,不然报错。自己定义的方式有多种
// 4.方法引用法 即在当前类写一个排序的方法
User user01 = new User("libai", 18, 98);
User user02 = new User("liqingzhao", 16, 96);
User user03 = new User("dufu", 20, 92);
// 分数相同,不会加入,所以只有三条数据
User user04 = new User("dufu", 18, 98);
TreeSet<User> treeSet = new TreeSet<>(Tree_Set::compare);
treeSet.add(user01);
treeSet.add(user02);
treeSet.add(user03);
treeSet.add(user04);
System.out.println(treeSet);
}
// 排序的方法(静态)方便调用
public static int compare(User user1, User user2) {
return user1.getScore() - user2.getScore();
}
}
class User implements Comparable<User>{
private String name;
private Integer age;
private Integer score;
public User() {
}
public User(String name, Integer age, Integer score) {
this.name = name;
this.age = age;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getScore() {
return score;
}
public void setScore(Integer score) {
this.score = score;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(name, user.name) &&
Objects.equals(age, user.age) &&
Objects.equals(score, user.score);
}
@Override
public int hashCode() {
return Objects.hash(name, age, score);
}
// 重写比较方法
@Override
public int compareTo(User o) {
// 按分数进行升序排序
return this.score - o.score;
}
}
4.2.3 LinkedHashSet集合
1)它其实是HashSet集合的子类
2)它的底层是一个LinkedHashMap,底层维护了一个数组 + 双向链表,每一个节点都有before和after属性,这样可以形成双向链表,再添加一个元素时,先求hash值,再求索引,确定该元素在table的位置,然后将添加的元素加入到双向链表,如果已经存在,则不添加。其实原则还是和hashSet一样。
3)LinkedHashSet根据元素的hashcode值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的
4)LinkedHashSet不允许添加重复元素
5.Map集合
5.1 Map集合的简述
(1)Map和collection并列存在,用于保存具有映射关系的数据:key-value(双列元素)
(2)Map中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中
(3)Map中的key不可重复,原因和HashSet一样
(4)Map中的value可以重复
(5)Map中的key和value可以为null,但是key只允许存在一个null,value可以有多个null。
(6)常用String类型数据作为map的key
(7)Key和value之间存在单向一对一关系,即通过key找到对应的value
(8)一对key-value是放在一个HashMap$Node中,又因为Node实现了Entry接口,也可以叫key-value为Entry
(9)一对key-value是放在一个HashMap$Node中,但是为了方便管理,它会将每一个HashMap$NodeNode封装成Entry,然后再把这个Entry放到一个entrySet这个集合中。
(10)当我们想单独的存放key取key的话,我们可以将这个由HashMap$Node封装成的Entry放到Set集合中(存放的方法就是调用Map里的keySet方法),想单独取value的话,同理,只是将Entry放到一个Collection集合中(存放的方法就是调用Map里的values方法)。最后遍历获取即可。
PS:这些方法只是做一个指向,并不是创建一个全新的数据。
5.2 Map集合的实现子类
5.2.1 HashMap集合
1)Hash Map是Map接口使用频率最高的实现类
2)HashMap是以key-value键值对的方式来存储数据的
3)Key不能重复,但是值可以重复,允许使用null,但是键的null值只能存在一次
4)如果添加相同的key,则会覆盖原有的值
5)与HashSet一样,不能保证映射的顺序,因为底层是以hash表的方式存储的
6)HashMap没有实现线程同步,因此是线程不安全的
7)它的扩容机制和hashSet相同
5.2.2 HashTable集合
1)跟HashMap大致一样,但是它是线程安全的
2)最大的区别在于它的键和值都不能为null
3)初始化大小为11,到临界值 11 * 0.75即8,然后扩容,扩容机制为原先11 * 2 + 1
5.2.3 Properties集合
1)Properties继承自HashTable类并实现Map接口,也是以键值对形式保存数据
2)使用特点和HashTable类似
3)Properties还可以用于从xxx.properties文件中,加载数据到Properties类对象,并进行读取和修改
6.工具类Collections
6.1 Collections简述
(1)Collections是操作Set,List,Map等集合的工具类
(2)Collections中提供了一系列静态的方法对集合元素进行排序,查找和修改等操作(排序操作均为static方法)
(3)代码演示
package com.shallow.collections;
import java.util.ArrayList;
import java.util.Comparator;
@SuppressWarnings({"all"})
public class Collections {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add("libai");
arrayList.add("liqingzhao");
arrayList.add("dufu");
arrayList.add("smith");
arrayList.add("libai");
// 使用Collections工具类操作
// 1.反转list中的元素
// java.util.Collections.reverse(arrayList);
// System.out.println(arrayList);
for (int i = 0; i < 5; i++) {
// 2.对list集合随机排序
java.util.Collections.shuffle(arrayList);
System.out.println(arrayList);
}
// 3.根据元素的自然顺序对指定list集合元素按升序排序
java.util.Collections.sort(arrayList);
System.out.println(arrayList);
// 4.将指定list集合中的元素进行交换
java.util.Collections.swap(arrayList,0 , 2);
System.out.println(arrayList);
// 5.根据元素的自然顺序,返回给定集合中的最大元素
System.out.println(java.util.Collections.max(arrayList));
// 6.根据Comparator指定的顺序,返回给定集合中的最大元素
Object max = java.util.Collections.max(arrayList, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ((String) o1).length() - ((String) o2).length();
}
});
System.out.println(max);
// 7.返回指定集合中指定元素的出现次数
System.out.println("libai出现的次数:" +
java.util.Collections.frequency(arrayList, "libai"));
// 8.将arrayList的内容复制到list中
/**
* 这里需要注意的点就是,我们先要给新建的list集合给定跟要复制的arrayList集合
* 大小一样的空间
*/
ArrayList list = new ArrayList();
for (int i = 0; i < arrayList.size(); i++) {
list.add("");
}
// 再进行拷贝
java.util.Collections.copy(list, arrayList);
System.out.println(list);
// 9.使用新值替换list对象的所有旧值
}
}
7.总结
7.1 java集合的总结
引入话题:在开发中选择什么集合实现类:主要取决于业务操作特点。然后根据集合实现类特性进行选择
1.先判断存储的类型:即单列集合还是双列集合(键值对形式的集合)
2.单列集合:Collection集合接口
2.1 允许重复:List集合
(1)增删多:LinkedList【底层是双向链表】
(2)改查多:ArrayList【底层是数组】
2.2 不允许重复:Set
(1)无序:HashSet【底层是HashMap】
(2)排序:TreeSet
(3)有序:LinkedHashSet【底层是数组 + 双向链表】
3.双列集合:Map
3.1 键无序:HashMap【底层是哈希表 JDK8:数组 + 链表 + 红黑树】
3.2 键排序:TreeMap
3.3 有序:LinkedHashMap
3.4 读取文件:Properties