java集合框架
集合:把具有相同数据类型的一组变量,汇聚成一个整体,就被称之为集合。
集合框架:为了表示和操作集合而规定的一种统一标准的体系结构。最简单的集合如数组、队列和列表等。任何集合框架一般包含:对外的接口、接口的实现和对集合运算的算法。
- 接口:即表示集合的抽象数据类型(规范)。接口提供了让我们对集合中所表示的内容进行单独操作的方式(标准)。
- 实现:也就是集合框架中接口的具体实现。实际上它们就是那些可复用的数据结构。
- 算法:在一个实现了某个集合框架中的接口对象身上完成了某种有用的计算的方法,例如查找、排序等。
接口的体系结构
Collection接口
对单个元素进行存放的最大接口规范。针对不同的方式它有不同的实现。
List接口
是Collection接口的子接口,主要针对于线性操作来提出的规范。
特点:可重复,有序的集合
Vector实现类
线性队列式结构的一种实现,它是线程安全的,多个线程同时对集合操作时保证了安全。
特点:数组结构、查询方便(只需要下标就行)、线程安全的,插入和删除的效率低下(涉及其后元素的位移)
// 1.如何产生Vector类的对象
// 产生了一个Vector集合,集合中只能存放Integer类型的元素
Vector<Integer> vector = new Vector<>();
// 2.Vector类的常用方法
// 存放到集合中
vector.add(88); // 添加到集合的末尾
vector.add(64);
vector.add(1, 39); // 插入到指定位置之前
System.out.println(vector);
// 获取集合的长度
int len = vector.size();
System.out.println("集合元素个数是:" + len);
// 获取集合中的元素
int v1 = vector.get(1); // 获取指定位置的元素
System.out.println(v1);
// 修改集合指定位置的元素内容
vector.set(2, 93);
System.out.println(vector);
// 删除集合的元素
vector.remove(1); // 删除指定位置的元素
System.out.println(vector);
vector.clear(); // 清空集合
System.out.println(vector);
// 3.如何排序集合
Vector<Integer> vector = new Vector<>();
for (int i = 0; i < 10; i++) {int num = (int) (Math.random() * 100 + 1);vector.add(num);
}
System.out.println("排序之前的内容:");
System.out.println(vector);
// 通过集合框架中提供的一个算法类来完成
Collections.sort(vector); // 对集合进行升序操作
System.out.println("排序之后的内容:");
System.out.println(vector);
// 4.如何查找集合中的元素
Vector<Integer> vector = new Vector<>();
for (int i = 0; i < 10; i++) {int num = (int) (Math.random() * 10 + 1);vector.add(num);
}
System.out.println(vector);
System.out.print("Input:");
int num = scan.nextInt();
// 判断用户输入的这个元素是否存在
boolean b = vector.contains(num);
System.out.println(b ? "存在" : "不存在");
// 获取到用户输入的内容在集合中的位置 如何不存在返回-1
int index = vector.indexOf(num);
System.out.println("集合中的位置:" + index);
int index2 = vector.indexOf(num,4); // 从第五个开始查找指定元素的位置
System.out.println(index2);
Stack实现类
继承于Vector类,它有一些自身特有方法。实现了一个先进后出的堆栈。提供了5个特有的方法,基本的push和pop方法,还有获取到栈顶的peek方法,还可以通过empty方法测试堆栈是否为空,search方法检测一个元素在堆栈的位置。
特点:数组结构、查询方便(只需要下标就行)、线程安全的,有自身特有方法,插入和删除的效率低下(涉及其后元素的位移)
// 1.如何产生集合对象
Stack<String> stack = new Stack<>();
// 2.集合的常用方法 这里只讨论特有方法,继承下来的方法不再重复讲解
// 等同于add(e);操作添加到集合的末尾
stack.push("aa");
stack.push("bb");
stack.push("cc");
stack.push("dd");
System.out.println(stack);
// 获取集合的元素
// 获取到集合中最后位置的元素
String v3 = stack.peek();
String v4 = stack.peek();
System.out.println(v3);
System.out.println(v4);
System.out.println(stack);
// 获取到集合中最后位置的元素 并会删除这个元素
String v1 = stack.pop();
String v2 = stack.pop();
System.out.println(v1);
System.out.println(v2);
System.out.println(stack);
// 检测堆栈是否为空
boolean b = stack.empty();
System.out.println(b);
// 检测堆栈是否存在某个元素
int index = stack.search("aa");
System.out.println("返回的位置是:" + index);
ArrayList实现类🌟🌟🌟🌟
就是传说中的动态数组,也是我们最常用的集合。它允许任何符合规则的元素插入。每一个ArrayList初始容量都是0,当第一次向集合中添加元素时,系统会自动扩充集合的长度,以后每一次添加元素时如果容量不够仍会自动扩容。和Vector类是基本一致的,除过它不是线程安全的。目前的规范标准都推荐使用ArrayList。
特点:数组结构、查询方便(只需要下标就行)、线程不安全的,插入和删除的效率低下(涉及其后元素的位移)
基本数据类型操作
public static void a() {// 1.如何产生对象// ArrayList<String> list = new ArrayList<>();// 一般推荐写法List<String> list = new ArrayList<>();// 2.集合的常用方法list.add("aa");list.add("bb");list.add("cc");list.add(2, "dd");System.out.println(list);String v1 = list.get(1);System.out.println(v1);list.set(2, "ee");System.out.println(list);list.remove(1); // 删除指定位置的元素list.remove("aa"); // 删除指定内容的元素System.out.println(list);list.clear();System.out.println(list);int len = list.size();System.out.println("集合的长度:" + len);boolean b = list.isEmpty();System.out.println("是否是空:" + b);
}// 如何排序常见数据类型的集合
public static void b() {List<Integer> list = new ArrayList<>();for (int i = 0; i < 20; i++) {int num = (int) (Math.random() * 100 + 1);list.add(num);}System.out.println("排序之前的内容:");System.out.println(list);// 通过集合框架中提供的一个算法类来完成Collections.sort(list); // 对集合进行升序操作System.out.println("排序之后的内容:");System.out.println(list);
}private static Scanner scan = new Scanner(System.in);// 查找元素在集合中的位置
public static void c() {System.out.println("Input:");int num = scan.nextInt();List<Integer> list = new ArrayList<>();for (int i = 0; i < 20; i++) {int num1 = (int) (Math.random() * 10 + 1);list.add(num1);}System.out.println(list);boolean b = list.contains(num);System.out.println("是否存在:" + b);int index1 = list.indexOf(num); // 找出第一次出现的位置 不存在返回-1int index2 = list.lastIndexOf(num); // 找出最后一次出现的位置 不存在返回-1System.out.println(index1);System.out.println(index2);
}
复杂数据类型操作
// ArrayList对复杂类型的操作
public class DemoD {// 基本的创建对象,CRUD(增删改查)的方法public static void a() {// 产生了一个集合,这个集合只能存放Person类型的数据List<Person> list = new ArrayList<>();// 产生了四个对象Person p1 = new Person(1, "张三", '男', 23);Person p2 = new Person(2, "李四", '女', 21);Person p3 = new Person(3, "王武", '男', 28);Person p4 = new Person(4, "马六", '男', 25);// 将这四个对象存放到集合中list.add(p1);list.add(p2);list.add(1, p3);list.add(p4);System.out.println(list);// 获取到指定位置的元素Person p5 = list.get(3);System.out.println(p5);// 修改指定位置的元素Person p6 = new Person(5, "大白", '男', 19);list.set(1, p6);System.out.println(list);// 删除元素list.remove(1);list.remove(p1);System.out.println(list);// 清空list.clear();// 获取长度int len = list.size();System.out.println(len);}// 讲解了复杂(自定义)数据类型的排序问题/*1.泛型类必须实现Comparable接口2.泛型类重写compareTo方法,自定义规则3.调用Collections.sort(list);来进行排序*/public static void b() {List<Person> list = new ArrayList<>();Person p1 = new Person(1, "张三", '男', 23);Person p2 = new Person(2, "李四", '女', 21);Person p3 = new Person(3, "王武", '男', 28);Person p4 = new Person(4, "马六", '男', 25);list.add(p1);list.add(p2);list.add(p3);list.add(p4);list.add(p2);System.out.println("排序之前的集合:");for (int i = 0; i < list.size(); i++) {Person p = list.get(i);System.out.println(p);}// 如何对集合排序 要求泛型必须实现一个接口ComparableCollections.sort(list);System.out.println("排序之后的集合:");for (int i = 0; i < list.size(); i++) {Person p = list.get(i);System.out.println(p);}}/*讲解了复杂数据类型的查找问题1.重写equals方法 自定义规则2.调用contains或者indexOf方法*/public static void c() {List<Person> list = new ArrayList();Person p1 = new Person(1, "张三", '男', 26);Person p2 = new Person(2, "李四", '女', 22);Person p3 = new Person(3, "王武", '男', 21);Person p4 = new Person(4, "马六", '男', 27);list.add(p1);list.add(p2);list.add(p3);list.add(p4);int len = list.size();System.out.println("集合长度是:" + len);boolean b1 = list.contains(p1);System.out.println(b1 ? "存在" : "不存在");int index1 = list.indexOf(p1);System.out.println("位置是:" + index1);System.out.println("----------------------------------------------");// 只关心对象的值在集合中的元素是否有重复// 产生了一个新对象Person p5 = new Person(3, "王武", '男', 21);System.out.println("p3和p5的内存地址是否相等?" + (p3 == p5));System.out.println("p3和p5的equals方法返回结果是否相等?" + p3.equals(p5));boolean b2 = list.contains(p5);System.out.println(b2 ? "存在" : "不存在");int index2 = list.indexOf(p5);System.out.println("位置是:" + index2);System.out.println("----------------------------------------------");Person p6 = p3;System.out.println("p3和p6是否相等?" + (p3 == p6));boolean b3 = list.contains(p6);System.out.println(b3 ? "存在" : "不存在");int index3 = list.indexOf(p6);System.out.println("位置是:" + index3);}public static void main(String[] args) {c();}}
LinkedList实现类
是List接口的一种实现,它是一种双向链表结构,在执行查询时效率比较低下,但是插入和删除效率比较高。
/*1.如何产生集合对象2.集合的常用方法3.如何查找和排序4.其他的注意事项*/
public class DemoB {public static void a() {// 产生LinkedList集合对象LinkedList<String> list = new LinkedList<>();// 常用方法list.add("aa");list.add("bb");list.add(1, "cc");// 特有方法list.addFirst("dd"); // 添加到第一个位置list.addLast("ee"); // 就是list.add("ee");System.out.println(list);String v1 = list.get(1);// 特有方法String v2 = list.getFirst();String v3 = list.getLast();System.out.println("获取指定位置元素:" + v1);System.out.println("获取第一个元素:" + v2);System.out.println("获取最后一个元素:" + v3);list.set(2, "ff");System.out.println(list);list.remove(1);list.remove("ff");// 特有方法list.removeFirst();list.removeLast();System.out.println(list);int len = list.size();System.out.println("长度是:" + len);list.clear();boolean b = list.isEmpty();System.out.println(b ? "空的" : "非空的");}private static Scanner scan = new Scanner(System.in);// 基本类型的排序和查找public static void b() {LinkedList<Integer> list = new LinkedList<>();for (int i = 0; i < 10; i++) {int num = (int) (Math.random() * 100 + 1);list.add(num);}System.out.println(list);Collections.sort(list); // List中的四个实现类它们的排序是一样的System.out.println(list);// List中的四个实现类它们的查找也是一样的System.out.println("请输入:");int num = scan.nextInt();boolean b = list.contains(num);int index1 = list.indexOf(num);int index2 = list.lastIndexOf(num);System.out.println("是否存在?" + b);System.out.println("第一次出现的位置:" + index1);System.out.println("最后一次出现的位置:" + index2);}// 自定义类型的排序和查找public static void c() {LinkedList<Person> list = new LinkedList<>();Person p1 = new Person(1, "张三", '男', 21);Person p2 = new Person(2, "李四", '女', 27);Person p3 = new Person(3, "王武", '男', 23);Person p4 = new Person(4, "马六", '男', 25);list.add(p1);list.add(p2);list.add(p3);list.add(p4);// 排序之前:for (Person p : list) {System.out.println(p);}System.out.println("------------------------------------------------");Collections.sort(list); // 同ArrayList的写法// 排序之后:for (Person p : list) {System.out.println(p);}// 查找 和ArrayList写法完全一致System.out.println("------------------------------------------------");Person p5 = new Person(3, "王五", '男', 26);boolean b = list.contains(p5);int index = list.indexOf(p5);System.out.println("是否存在?" + b);System.out.println("第一次出现的位置:" + index);}public static void main(String[] args) {c();}
}
List集合实现类的分析
- ArrayList、Vector和Stack都是动态数组,它们查询速度很快,但是插入删除速度慢。
- LinkedList是双向链表结构,插入删除块,查询速度慢,
- Vector是线程安全的集合,Stack是Vector的子类,它是一个先进后出的结构。
- 一般情况下,我们推荐使用ArrayList。
Set接口
是Collection接口的子接口,它主要针对于散列式接口来提出的规范。
特点:不可重复(不允许存放重复的元素),无序(没有索引下标)。
HashSet实现类🌟🌟🌟
HashSet是Set集合最常用的实现类,经典表现。是按照Hash算法来存储元素的,因此具有很好的存放功能。
特点:
- 不保证元素的顺序
- HashSet不是线程安全的
- 可以存放null值
- 值的内容不允许重复
产生HashSet集合
Set<Integer> set = new HashSet<>();
常见的方法
//添加元素
set.add(99);
set.add(23);
set.add(56);
set.add(78);
/*// 不能存放重复内容,存放时要判断元素在集合中是否存在首先通过循环来和集合中的每一个元素进行比较1.获取到当前元素的hash值(通过调用hashCode()来获取) 和集合中元素的hash值比较相等时2.当前元素和集合中元素的equals()进行比较3.如果都相等 则代表这个元素在集合中存在,否则 不存在
*/
set.add(56);
set.add(null);
System.out.println(set);
// 删除元素
set.remove(23); // 只能删除指定元素 不能删除索引
System.out.println(set);
// 获取元素 没有 无法获取
// 只能通过迭代器来遍历
for (Integer i : set) {System.out.print(i + "\t");
}
System.out.println();
// 修改元素 无法修改int len = set.size();
System.out.println(len);
查找集合中是否存在内容
public static void c() {// 产生了一个集合HashSet<School> set = new HashSet<>();// 产生了四个对象School s1 = new School(1, "清华大学");School s2 = new School(2, "北京大学");School s3 = new School(3, "西安交通大学");School s4 = new School(4, "长安大学");// 存放到了集合中set.add(s1);set.add(s4);set.add(s3);set.add(s2);// 通过迭代器遍历集合for (School s : set) {System.out.println(s);}School s5 = new School(3, "西安交通大学");/*首先通过循环来和集合中的每一个元素进行比较1.获取到当前元素的hash值(通过调用hashCode()来获取) 和集合中元素的hash值比较相等时2.当前元素和集合中元素的equals()进行比较3.如果都相等 则代表这个元素在集合中存在,否则 不存在*/boolean b = set.contains(s5);System.out.println(b ? "存在" : "不存在");set.add(s5); // HashSet集合在存放内容时 会判断这个内容是否在集合中存在 存放不进去System.out.println(set.size());
}
// 学校类
public class School {private Integer id;private String name;/*还需要重写hashCode方法*/public int hashCode(){return id; // 返回的hash值设置为id}/*重写equals方法 设置自己的规则自定义规则为:只要id一致就行*/public boolean equals(Object o) {int myId = this.getId();School s = (School) o;int itId = s.getId();return myId == itId;}
}
TreeSet实现类
TreeSet是一个红黑树的结构,录入数据后能够进行排序,如果是自定义数据类型必须实现Comparable接口。
- 无序:它不是一个线性结构,不知道在队列中的位置
- 有序:在存放时会比较大的在右子节点,小的在左子节点
⚠️ 注意
- TreeSet不能存放null值。
- TreeSet强制排序,泛型类必须实现Comparable接口。
产生对象和常见方法
TreeSet<Integer> set = new TreeSet<>();
set.add(28);
set.add(99);
set.add(19);
set.add(33);
set.add(61);
set.add(33); // Set集合中不能存放重复数据
// set.add(null); ❌ TreeSet集合不能存放null
System.out.println(set);
set.remove(19);
System.out.println(set);
int len = set.size();
System.out.println(len);
boolean b = set.contains(19);
System.out.println(b);
int first = set.first(); // 获取到根结点
System.out.println(first);
// 通过迭代器来进行遍历
for (Integer i : set) {System.out.print(i + "\t");
}
复杂类型在TreeSet的使用
TreeSet<Lover> set = new TreeSet<>();
Lover l1 = new Lover(1, "杨超越", "💃");
Lover l2 = new Lover(2, "羽生结弦", "⛸️");
Lover l3 = new Lover(3, "谷爱凌", "🎿");
// 也需要判断元素在集合中是否存在
set.add(l1);
set.add(l2);
set.add(l3);
System.out.println(set);
// 产生了一个新对象
Lover l4 = new Lover(3, "谷爱凌", "🎿");
// TreeSet在判断是否存在时 通过compareTo方法来进行 如果返回的是0 则认为是一个对象
boolean b = set.contains(l4);
System.out.println(b ? "存在" : "不存在");
LinkedHashSet实现类
LinkedHashSet是HashSet的子类,具有HashSet的特性,也是根据元素的HashCode值来判断元素。但它使用链表维护元素的次序,元素的顺序与添加顺序一致。由于LinkedHashSet需要维护元素的插入顺序,因此性能上要低于HashSet。
Set集合实现类的分析
- HashSet的性能是最好的,因为TreeSet要排序,LinkedHashSet需要维护次序。大部分情况下我们都应使用HashSet。
- LinkedHashSet是HashSet的子类,具有HashSet的特性,又维护了插入顺序。
- 如果我们需要对Set集合进行排序,那么推荐使用TreeSet。
Queue接口
是Collection接口的子接口,先进先出的一种规范体现。在实际应用中使用较少。就是队列结构,它只允许在队列的前端进行删除操作,而在队列的后端进行插入操作。
// LinkedList也实现了队列接口
Queue<String> queue = new LinkedList<>();
queue.offer("aa"); // 同add(e);
queue.offer("bb");
queue.offer("cc");
System.out.println(queue);
String v1 = queue.poll(); // 获取到队首的内容 取出来
System.out.println(v1);
System.out.println(queue);
String v2 = queue.element(); // 获取到队首的内容
System.out.println(v2);
System.out.println(queue);
Map接口
Map接口采用键值对的方式来存储数据,将一个值(Value)存放到集合中,给它又起了一个别名(Key),保存具有映射关系的数据,因此,Map集合中存放两组数据,一组用于存放值,另一组用于存放键,其中值允许重复,键不允许重复。
HashMap实现类🌟🌟🌟
底层数据结构jdk1.8之后 数组+链表+红黑树
HashMap是基于Hash算法实现的一个集合,是map中最常用的一个实现类。值可以重复,健不能重复,如果重复会覆盖掉原有内容。HashMap的键和值都允许存放null。键是Hash结构的。
产生HashMap集合
// 产生HashMap对象 存放的内容只能是int类型,起的名字(Key)是String类型
Map<String, Integer> map = new HashMap<>();
HashMap的常用方法
// 常用方法
// 添加和修改
map.put("a", 87); // 将87存放到map集合中起了一个名字a
map.put("b", 97);
map.put("c", 97);
map.put("d", 91);
map.put("d", 33); // 修改 Key重复会覆盖原有内容
map.put("e", null);
map.put(null, 87);
System.out.println(map);
// 获取
int v1 = map.get("a"); // 通过键获取到值
//int v2 = map.get("m"); // 不存在报错
System.out.println(v1);
//System.out.println(v2);
// 删除
map.remove("d"); // 根据Key删除指定元素
System.out.println(map);
// 获取长度
int len = map.size();
System.out.println("长度:" + len);
排序和查找
// HashMap集合和HashSet集合一致都不能排序
// HashMap集合和HashSet集合查找方式一致
public static void b(){Map<String,Integer> map = new HashMap<>();map.put("a",33);map.put("b",46);boolean b1 = map.containsValue(46); // 判断46是否在值集合中boolean b2 = map.containsKey("a");// 判断a是否在键集合中System.out.println(b1);System.out.println(b2);
}
特有的几个方法
Map<String, Integer> map = new HashMap<>();
map.put("a", 87); // 将87存放到map集合中起了一个名字a
map.put("b", 97);
map.put("c", 97);
map.put("d", 91);
// 获取到键集合
Set<String> keys = map.keySet(); // 获取到所有的键
// 获取到值集合
Collection<Integer> values = map.values(); // 获取到所有的值
System.out.println(keys);
System.out.println(values);
TreeMap实现类
TreeMap也是一个红黑树结构,它的Key集是红黑树结构。也就是说在存放数据时会按照Key进行排序。
Hashtable实现类
Hashtable是基于Hash算法实现的一个集合,值可以重复,健不能重复,如果重复会覆盖掉原有内容。Key集是Hash结构的。它是一个线程安全的集合。方法和HashMap一致。
ConcurrentHashMap实现类
ConcurrentHashMap是基于Hash算法实现的一个集合,值可以重复,健不能重复,如果重复会覆盖掉原有内容。Key集是Hash结构的。它是一个分段锁的操作,把集合中的数据分别上锁,影响的面就比较小一些。
LinkedHashMap实现类
LinkedHashMap使用双向链表来维护键值对,维护了插入的顺序,保证了迭代顺序。仍然是以Key集来维护的。
Map实现类对于null的操作
// key和value都能存放null
HashMap<String, String> map1 = new HashMap<>();
map1.put(null, null);
System.out.println(map1);
// key不能为null,value可以是null
TreeMap<String, String> map2 = new TreeMap<>();
//map2.put(null,"a");
System.out.println(map2);
// key和value都不允许为null
Hashtable<String, String> map3 = new Hashtable<>();
//map3.put(null, "a");
System.out.println(map3);
// key和value都能存放null
LinkedHashMap<String, String> map4 = new LinkedHashMap<>();
map4.put(null, null);
System.out.println(map4);
// key和value都不允许为null
ConcurrentHashMap<String, String> map5 = new ConcurrentHashMap<>();
map5.put(null, "a");
System.out.println(map5);
值传递和引用传递问题
public class DemoC {// 原始类型 存放的是值public static void a() {int a = 1;List<Integer> list = new ArrayList<>();list.add(a); // 理解成集合中存放的是1 相当于 list.add(1);System.out.println(list);a = 4; // 重新创建了一个变量 也起了一个名字叫aSystem.out.println(list);}// 集合中存放的是对象的内存地址public static void b() {Person p = new Person(1, "张三", '男', 23);List<Person> list = new ArrayList<>();list.add(p); // 存放的是内存地址(引用)System.out.println(list);p.setName("李四"); // 修改了p 集合也会发生改变System.out.println(list);}// 集合中存放常量问题(String问题)public static void c() {String s = "aaa"; // 常量字符串 值不能发生改变的List<String> list = new ArrayList<>();list.add(s);System.out.println(list);s = "abc"; // 重新创建一个变量(对象)System.out.println(list);}// 重新创建对象问题public static void d() {Person p = new Person(1, "张三", '男', 24);List<Person> list = new ArrayList<>();list.add(p);System.out.println(list);// 新创建一个对象 还起了一个名字 另一内存地址p = new Person(2, "王五", '女', 22);System.out.println(list);}public static void main(String[] args) {d();}
}
println(list);p.setName("李四"); // 修改了p 集合也会发生改变System.out.println(list);}// 集合中存放常量问题(String问题)public static void c() {String s = "aaa"; // 常量字符串 值不能发生改变的List<String> list = new ArrayList<>();list.add(s);System.out.println(list);s = "abc"; // 重新创建一个变量(对象)System.out.println(list);}// 重新创建对象问题public static void d() {Person p = new Person(1, "张三", '男', 24);List<Person> list = new ArrayList<>();list.add(p);System.out.println(list);// 新创建一个对象 还起了一个名字 另一内存地址p = new Person(2, "王五", '女', 22);System.out.println(list);}public static void main(String[] args) {d();}
}