时间:2023-03-20 10:28:05 | 栏目:JAVA代码 | 点击:次
呼~,历过好几天的奋战终于把集合框架肝完了,b站某马老师讲的是真的非常详细而且动听,原理给你分析得明明白白的,此前也找了许多关于集合这一大章节的视频,发现更多的是针对于使用,原理讲的并不是很多,这就导致我在练习或者回顾时还是一知半解。以下是我结合视频以及个人的一些理解和体会做的笔记总结。路漫漫其修远兮,吾将上下而求索,希望这篇总结对你有些许帮助,嘻嘻!
集合:Java中提供的一种容器,可以用来存储多个数据。java集合大致可以分为Set,List,Queue和Map四种体系。
数组和集合的区别:
单列集合体系结构:
Collection接口是所有单列集合的父接口,因此在单列集合中定义的List和set通用的一些方法,这些方法可以操作所有的单列集合。方法如下:
【参考代码】
package Collection; import java.util.ArrayList; import java.util.Collection; /* Collection集合常用方法 boolean add(E e); 向集合中添加元素 boolean remove(E e); 删除集合中的某个元素 void clear(); 清空集合中所有的元素 boolean contains(); 判断集合中是否含有xxx元素 boolean isEmpty(); 判断集合是否为空 int size(); 计算集合的长度 Object[] toArray(); 将集合转成一个数组 */ public class Test { public static void main(String[] args) { //创建集合对象 , 可以多态使用 Collection<String>col = new ArrayList<>(); // Collection<String>col = new HashSet<>(); 下面的功能照样能实现:共性方法 col.add("小明"); // 添加元素 col.add("小红"); col.add("小蓝"); col.add("小绿"); System.out.println(col); //[小明, 小红, 小蓝, 小绿] //boolean remove(E e); 删除集合中的某个元素 // boolean ans = col.remove("小明"); // System.out.println(ans);//true // System.out.println(col);//[小红, 小蓝, 小绿] //void clear(); 清空集合中所有的元素 // col.clear(); // System.out.println(col);//[] //boolean contains(); 判断集合中是否含有xxx元素 // boolean result = col.contains("小明"); // System.out.println(result);//true //boolean isEmpty(); 判断集合是否为空 // boolean result = col.isEmpty(); // System.out.println(result);// 不为空false //int size(); 计算集合的长度 // int len = col.size(); // System.out.println(len);// 4 //Object[] toArray(); 将集合转成一个数组 Object[] arr = col.toArray(); // 遍历数组 // for (int i = 0; i < arr.length; i++) { // System.out.println(arr[i]); // } } }
引入:由于集合有多种,每种集合存储跟读取的方式都不一样,好比衣柜、水瓶、药瓶,你存和取的方式肯定不一样。如果每种集合都定义一种遍历方式那将十分的繁琐。
迭代器(Iterator):它不是一个容器而是接口,它是一种用于访问容器的方法,可用于迭代 List、Set和Map等容器。
迭代:即Collection集合的通用获取方式。再获取元素之前先要判断集合中是否有元素,如果有就将这个元素去取出来,继续再判断,直到集合所有元素被取出来为止。即:一个一个的往外拿。
作用:帮我们遍历或者拿到容器里边的数据。
迭代器常用操作:
迭代器的使用步骤:
【参考代码】
package Iterator; import javax.swing.text.html.parser.Entity; import java.util.*; public class Test { public static void main(String[] args) { //创建一个集合对象 Collection<String>col = new ArrayList(); //添加元素 col.add("小明"); col.add("小红"); col.add("小蓝"); col.add("小绿"); /* 1.使用集合的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态) 注意: Iterator接口也是有泛型的,迭代器的泛型跟集合走,集合是什么泛型,迭代器就是什么泛型 */ // 多态 接口 实现类对象 Iterator<String>it = col.iterator(); // 2.使用 Iterator接口中的hashNext方法判断是否还有下一个元素 while(it.hasNext());{ // 3.使用 Iterator接口中的next方法取出集合的下一个元素 String str = it.next(); System.out.println(str); } } }
增强for循环(for each循环)是JDk1.5之后的一个高循环,专门用来遍历数组和集合的,所有的数组跟单列集合都可以使用。它的内部原理就是一个迭代器Iterator,所以在遍历过程中,不能对集合元素进行增删操作。
语法:
for(类型 变量 : 数组/集合){// 数组或者集合里的每一项赋值给这个变量 // 循环体 }
【参考代码】
String[] student = {"小明","小红","小蓝"}; // // 传统遍历方式 // for (int i = 0; i < student.length; i++) { // System.out.println(student[i]); // } // 增强for for(String c : student){ System.out.println(c); } -------------------------------- List<Integer>list = new ArrayList<Integer>(); list.add(123); list.add(234); list.add(456); for(Integer n : list){ System.out.println(n); }
注:增强for必须有被遍历的目标。目标只能是数组或者Collection,而它仅仅作为遍历操作实现
在前面学习集合时,我们知道集合时可以存放任意对象的,只要把对象存储集合后,它们都会被向上转型提升为Object类型。当我们要取出这些对象时必须进行类型强制转换,由Object类型变为原来的类型。
不使用泛型:
- 好处:集合默认类型是Object类,可以存储任意类型的数据
- 弊端:不安全,会引发异常,需要强转。
public static void main(String[] args) { List list = new ArrayList(); list.add("小明"); list.add("小红"); for (int i = 0; i < list.size(); i++) { String s= (String)list.get(i) // 强转 System.out.println(s); } }
使用泛型:
public static void main(String[] args) { List<String> list = new ArrayList();// 规范了数据类型,只能放字符串! list.add("小明"); list.add("小红"); //stringList.add(123);// 除了字符串以外的类型不能加,报错! for (int i = 0; i < list.size(); i++) { String s = list.get(i); // 不用再强转了 System.out.println(s); } }
在上述的实例中,我们只能添加String类型的数据,否则编译器会报错。
泛型类
定义格式:
修饰符 class 类名<泛型变量>{ } // 注:泛型变量建议使用E、T、K、V
例如:
public class Box<T> { private T t; public void add(T t) { this.t = t; } public T get() { return t; }
参考示例:
注:在创建对象时确定泛型的类型
定义格式:
修饰符 <泛型变量> 返回值的类型 方法名称(形参列表){ //方法体 }
注:含有泛型的方法,在调用的时候确定泛型的数据类型
传递什么类型的参数,泛型就是什么类型
参考示例:
定义格式:
public interface 接口名<泛型类型> { }
使用方式1:定义接口的实现类,实现接口,并且指定接口的泛型
使用方式2:接口使用什么泛型,实现类就使用什么泛型,类跟着接口走。
就相当于定义了一个含有泛型的类,创建对象的时候确定泛型的类型。
下图接口同上图接口
当使用泛型类或接口时,传递数据中,泛型类型不确定,可以通过通配符表示<?>表示。但一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。
通配符的基本使用
泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用 ? ,?表示未知通配符
此时只能接收数据,不能往集合中存储数据。
【参考代码】
package FanXing; import javax.swing.text.html.HTMLDocument; import java.util.ArrayList; import java.util.Iterator; /* 泛型的通配符: ?:代表数据类型 使用方式: 不能在创建对象时使用 只能作为方法的传递参数使用 */ public class Generic { public static void main(String[] args) { ArrayList<Integer>list01 = new ArrayList<>(); list01.add(123); list01.add(456); ArrayList<String>list02 = new ArrayList<>(); list02.add("小明"); list02.add("小红"); // ......还有很多其它类型 printArray(list01); printArray(list02); /* 定义一个方法,能遍历所有类型的ArrayList集合 这时候我们不知道ArrayList集合使用的是什么类型,可以使用泛型的通配符:?来代表数据类型 注意:泛型没有继承的概念 */ } public static void printArray(ArrayList<?>list){ // 使用迭代器遍历集合 Iterator<?> it = list.iterator(); while(it.hasNext()){ Object obj = it.next();//it.next()取出的元素是Object类。Object类 可以接收任意的数据类型 System.out.println(obj); } } }
之前设置泛型的时候,实际上是可以可以任意设置的,只要是类就可以设置。但在Java的泛型中可以指定一个泛型的上限和下限。
泛型的上限:
泛型的下限:
比如:Object类、String类、Number类、Integer类,其中Number类是Integer的父类。
集合是基于数据结构做出来的,不同的集合底层采用不同的数据结构。不同的数据结构,功能和作用是不一样的。
数据结构是指数据以什么方式组织在一起。不同的数据结构,增删查的性能是不一样的。
栈:stack,又称堆栈,它是运算受限的线性表,只能在栈的受限一端进行插入和删除操作。
特点:先进后出
队列:queue,简称队,它同栈由于也是运算受限的线性表,只能在表的一端进行插入操作,而在表的另一端进行删除操作。
特点:先进先出
数组:Array,是个有序的元素序列,数组在内存中开辟一段连续的空间。
特点:
查询快:随机存取,通过索引可以快速访问元素
增删慢:静态分配内存,数组的长度是固定,存在空间闲置或者溢出现象;不适合进行插入和删除操作,需要移动大量元素。
链表:linked list,由一系列结点node组成,结点可以在运行时动态产生。每个节点包含两个部分:数据域(data)和指向下一个节点的指针域(next)。链表包括单链表和双向链表。
单链表:链表中只有一条链子,不能保证元素的顺序(存储和取出的顺序可能不一致)
双向链表:链表中只有两条链子,有一条链子专门记录元素的顺序,是一个有序的集合。
特点:
查询慢:链表的地址不是连续的,每次查询都要从头到尾进行遍历。
增删快:动态分派内存,增/删一个节点对于链表整体结构没有影响,增删操作效率高。
红黑树:R-B Tree,全称是Red-Black Tree,又称为“红黑树”,它一种特殊的二叉查找树。红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black),它是一种弱平衡二叉树(Weak AVL)。
特点:
(1)每个节点或者是黑色,或者是红色。 (2)根节点是黑色。 (3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!] (4)如果一个节点是红色的,则它的子节点必须是黑色的。 (5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
注:以上数据结构可以结合所学过c语言数据结构
List集合体系:添加元素,是有序,可重复,有索引的,大小可变。实际开发中常用的是ArrayList集合。List集合体系包括以下几种:
List集合继承了Collection集合的全部功能,同时因为List集合系列有索引,所以多了很多按照索引操作元素的方法:
add(int index, E element) 根据索引添加元素 get(int index) 根据索引获取元素 remove(int index) 根据索引删除元素 set(int index, E element) 根据索引修改该位置上的元素 contains(E element)判断容器是否含有XXX东西 clear() 清空集合中的元素 size()计算集合的大小
【参考代码】
package Collection; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; public class TestList { public static void main(String[] args) { List<String>list = new ArrayList(); // 换成Linkedist 下面的操作都能一样实现 list.add("小明"); list.add("小红"); list.add("小蓝"); list.add("小绿"); list.add("小明"); // // 在某个索引位置往集合中添加元素 // list.add(2,"哈哈哈哈"); // System.out.println(list); // // 删除集合中某个元素 // list.remove("小蓝"); // // 根据索引获取元素 // System.out.println(list.get(0)); // // 修改索引位置处的元素 // list.set(0,"小明很明白!"); // System.out.println(list.get(0));//小明很明白! // // 计算列表的大小(长度): // System.out.println(list.size()); // //判断列表中是否有xxx false // System.out.println(list.contains("小蓝")); } }
for循环
// 遍历列表 for (int i = 0; i < list.size(); i++) { String str = list.get(i); System.out.println(str); }
迭代器
Iterator<String>it = list.iterator(); // 创建一个List的迭代器 while(it.hasNext()){// 判断有没有下一个元素 String s = it.next(); System.out.println(s); }
增强for
List<String>list = new ArrayList<>(); for(String s : list){ System.out.println(s); }
Lambda表达式(了解)
list.foreach(s -> { System.out.println(s); });
ArrayList集合存储的结构是数组结构,元素增删慢,查询快。最常用。
LinkedList集合存储的结构是链表结构,方便元素的添加、删除操作。LinkedList是一个双向链表
LinkedList的特点:
底层是一个链表结构:查询慢,增删快
里边含有大量操作首尾元素的方法
注:使用LinkedList集合特有的方法,不能使用多态,命名要注意了!
实际开发中对一个集合的添加、删除操作经常涉及首尾操作,LinkedList提供了很多操作首尾元素方法
public void addFirst(E e); 将指定的元素插到列表开头。 public void addLat(E e); 将指定的元素插到列表结尾。 此方法等效于add()方法 public void push(E e); 将元素推入此列表所示的堆栈。 此方法等效于addFirst()方法 public E getFirst(); 返回此列表的第一个元素 public E getLast(); 返回此列表的最后一个元素 public E removeFirst(); 移除并返回此列表的第一个元素 public E removeLast(); 移除并返回此列表的最后一个元素 public E pop(E e); 入此列表所示的堆栈中弹出一个元素。 public boolean isEmpty(); 如果列表为空 返回true
【参考代码】
package Collection; /* public void addFirst(E e); 将指定的元素插到列表开头。 public void addLast(E e); 将指定的元素插到列表结尾。 public void push(E e); 将元素推入此列表所示的堆栈。 public E getFrist(); 返回此列表的第一个元素 public E getLast(); 返回此列表的最后一个元素 public E removeFrist(); 移除并返回此列表的第一个元素 public E removeLast(); 移除并返回此列表的最后一个元素 public E pop(E e); 入此列表所示的堆栈中弹出一个元素。 public boolean isEmpty(); 如果列表为空 返回true */ import java.util.LinkedList; import java.util.List; public class TestLinkedList { public static void main(String[] args) { show01(); show02(); show03(); } /* public void addFirst(E e); 将指定的元素插到列表开头。 public void addLast(E e); 将指定的元素插到列表结尾。 public void push(E e); 将元素推入此列表所示的堆栈 */ public static void show01(){ // 注:LinkedList特有的方法不能使用多态! // List<String> list = new LinkedList<>(); 是不对的 LinkedList<String>list = new LinkedList<>(); // add()添加元素 list.add("a"); list.add("b"); list.add("c"); System.out.println(list);//[a, b, c] list.addFirst("hhh"); //public void push(E e); 将元素推入此列表所示的堆栈。 等效于addFirst() list.push("hhh"); System.out.println(list); //public void lastFrist(E e); 将指定的元素插到列表结尾。 等效于add() list.addLast("com"); System.out.println(list); } /* public E getFrist(); 返回此列表的第一个元素 public E getLast(); 返回此列表的最后一个元素 */ public static void show02(){ LinkedList<String>list = new LinkedList<>(); // add()添加元素 list.add("a"); list.add("b"); list.add("c"); // list.clear(); // 清空集合中所有元素 if(! list.isEmpty()){ System.out.println(list.getFirst());//a System.out.println(list.getLast());//c } } /* public E removeFrist(); 移除并返回此列表的第一个元素 public E removeLast(); 移除并返回此列表的最后一个元素 public E pop(E e); 入此列表所示的堆栈中弹出一个元素。 */ public static void show03(){ LinkedList<String>list = new LinkedList<>(); // add()添加元素 list.add("a"); list.add("b"); list.add("c"); System.out.println(list.pop()); //public E pop(E e); 入此列表所示的堆栈中弹出一个元素。 等效于 removefirst() //System.out.println(list.pop()); System.out.println(list.removeFirst());//a System.out.println(list.removeLast());//c System.out.println(list);//[b] } }
注:使用LinkedList集合特有的方法,不能使用多态。
数组结构实现,查询快,增删慢;
JDK1.0版本,运行效率慢、线程安全
【参考代码】
package Collection; import javax.swing.text.html.HTMLDocument; import java.util.Enumeration; import java.util.Iterator; import java.util.Vector; /* Vector集合的使用 存储结构:数组 */ public class VectorTest { public static void main(String[] args) { // 创建集合 Vector<String>vector = new Vector<>(); // 添加元素 vector.add("小明"); vector.add("小红"); vector.add("小蓝"); System.out.println("元素个数"+ vector.size()); // 判断 System.out.println(vector.contains("小明")); System.out.println(vector.isEmpty()); //删除 vector.remove("小红"); System.out.println(vector); //清空 clear(); vector.clear(); // 遍历 Iterator<String> it = vector.iterator(); while (it.hasNext()){ String str = it.next(); System.out.println(str); } //vector独有的遍历 使用枚举器 // Enumeration<String>en = vector.elements(); // while (en.hasMoreElements()){ // String s = en.nextElement(); // System.out.println(s); // } } }
Set系列集合:添加的元素,是无序的,不重复的,无索引的(索引的操作不能用)。
――HashSet:添加的元素,是无序的,不重复的,无索引的。
――LinkedHashSet:添加的元素,是有序的,不重复的,无索引的。
――TreeSet:不重复,无索引,按照大小默认升序排序!!(可排序集合)
遍历方式:由于Set集合五索引,故没有for循环遍历,只有三种遍历。
注:存储的字符串,Integer等类型的数据,它们是Java已经定义好了类,它们都重写了hashCode方法和equals方法,保证了元素的唯一性!
HashSet 保证元素唯一性的原理
我们使用 Set 集合都是需要去掉重复元素的, 如果在存储的时候逐个 equals() 比较, 效率较低,哈希算法提高了去重复的效率, 降低了使用 equals() 方法的次数。
当 HashSet 调用 add() 方法存储对象的时候, 先调用对象的 hashCode() 方法得到一个哈希值, 然后在集合中查找是否有哈希值相同的对象,如果没有哈希值相同的对象就直接存入集合。如果有哈希值相同的对象, 就和哈希值相同的对象逐个进行 equals() 比较,比较结果为 false 就存入, true 则不存。存储元素必需要重写HashCode方法和equals方法
给HashSet中存放自定义的类型时,必需要重写HashCode方法和equals方法,建立自己的比较方式,才能保证HashSet集合中对象的唯一性!
【参考代码】
Person类:
package Collection; import java.util.Objects; public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } // 用于打印 @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } // 重写hashCode方法和equals方法 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return age == person.age && Objects.equals(name, person.name); } @Override public int hashCode() { return Objects.hash(name, age); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
// 主控制台 package Collection; import java.util.HashSet; import java.util.Set; /* HashSet存储自定义类型的元素 Set集合保证元素唯一性: 存储的元素(String Integer,...Student,Person...) 必须重写hashCode方法和equals方法 要求: 同名且同年龄视为同一个人噢 */ public class TestHaxhSet { public static void main(String[] args) { // 创建hashSet集合存储Person Set<Person>set = new HashSet<>(); //集合类存放对象的! // 创建对象(人) /* // 在没有重写hashCode方法和equals方法前,它们的哈希值都是不一样的,equals也为false 故没有重复 Person p1 = new Person("小明",18); Person p2 = new Person("小明",19); Person p3 = new Person("小明",18); System.out.println(p1.hashCode());// 460141958 System.out.println(p2.hashCode());// 1163157884 System.out.println(p3.hashCode());// 1956725890 System.out.println(p1.equals(p2));// false set.add(p1); set.add(p2); set.add(p3); System.out.println(set);// [Person{name='小明', age=18}, Person{name='小明', age=19}, Person{name='小明', age=18}] */ // 重写hashCode方法和equals方法之后set对象就唯一性了 Person p1 = new Person("小明",18); Person p2 = new Person("小明",19); Person p3 = new Person("小明",18); set.add(p1); set.add(p2); set.add(p3); System.out.println(set);// [Person{name='小明', age=19}, Person{name='小明', age=18}] } }
我们知道HashSet保证元素的唯一性,但存放进去的元素是无序的,那我们要保证有序,该怎么办好呢?
在HashSet下面的一个子类Java.util.LinkedHashSet。它是链表和哈希表组合的一个数据结构。
LinkedHashSet集合的特点:
底层是一个哈希表(数组+链表/红黑树)+链表:多了一条链表(记录元素的存储顺序),保证元素有序
具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现,即按照将元素插入到 set 中的顺序(插入顺序)进行迭代。
HashSet与LinkedHashSet的区别:
【参考代码】
package Collection; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; public class TestHashSet { public static void main(String[] args) { Set<String>set = new HashSet<>(); set.add("kkk"); set.add("abc"); set.add("abc"); set.add("afterglow"); System.out.println(set);//[afterglow, abc, kkk] 无序,不重复 Set<String>Linkset = new LinkedHashSet<>(); Linkset.add("kkk"); Linkset.add("abc"); Linkset.add("abc"); Linkset.add("afterglow"); System.out.println(Linkset);//[kkk, abc, afterglow] 有序,不重复 } }
使用前提:
如果我们定义一个方法需要接收多个参数,并且多个参数类型一致,我们可以对其做如下格式的简化:
修饰符 返回值类型 方法名(参数类型... 形参名){ }
这个写法完全等价于:
修饰符 返回值类型 方法名(参数类型[] 形参名){ } ,
后者在调用时必须传递数组,而前者可以直接传递数据类型。
可变参数原理:
可变参数底层是一个数组,根据参数个数不同,会创建不同长度的数组来存储这些参数。传递参数的个数,可以是0~n个
【参考代码】
package Collection; public class KeBiancanShu { public static void main(String[] args) { int i =add(1,2,3,4); System.out.println(i); } // // 两个数的和 // public static int add(int a, int b){ // return a + b; // } // // 三个数的和,要是多个一直往下写,很麻烦! // public static int add(int a, int b, int c){ // return a + b +c; // } /* 求0~n个整数的和 数据类型已经确定:int 参数个数不确定,可以使用可变参数 */ public static int add(int...arr){ // System.out.println(arr);// [I@1b6d3586 底层是一个数组 // System.out.println(arr.length);// 可变数组的长度,却决于你添加的个数 int sum = 0; for (int i : arr){ sum += i; } return sum; } }
注意事项:
【示例代码】
/* 可变参数注意事项: 一个方法的参数列表,只能有一个可变参数 如果方法的参数有多个,那么可变参数必须写在参数列表的末尾! */ //一个方法的参数列表,只能有一个可变参数 // public static void method01(int...a,String...b){ 报错! // } //如果方法的参数有多个,那么可变参数必须写在参数列表的末尾! public static void method02(String b, double c, int...a){ }
max() / min()
:求集合的最大 / 小值public static <T> boolenan addAll(Collect<T> c , T. . . elements)
:往集合中添加一些元素public static void shuffle(List<?> list)
:打乱集合顺序public static void sort(List<T> list)
:将集合按照默认规则(升序)进行排序public static void sort(List<T> list , Comparator<? super T >)
;将集合按照指定的规则进行排序【参考代码】
public class Test { public static void main(String[] args) { List<Integer>list = new ArrayList<Integer>(); list.add(120); list.add(20); list.add(220); // 求最值 Integer max = Collections.max(list); System.out.println(max); Integer min = Collections.min(list); System.out.println(min); // 排序 Collections.sort(list); System.out.println(list); // 打乱顺序 Collections.shuffle(list); // 斗地主发牌 System.out.println(list); // 不定参数添加 Collections.addAll(list,456,789); System.out.println(list);//[220, 20, 120, 456, 789] } }
sort(List < T > list)使用
注意:
sort(List<T> list)使用前提:
排序的集合里边存储的元素,必须实现Comparable接口,重写接口中的方法compareTo定义排序的规则。在Java中Integer、String等等数据类型已经帮我们实现Comparable接口并重写接口中的方法compareTo了。如果要对自己定义的类进行排序,我们就要自己实现接口并重写compareTo然后进行自定义排序规则。
Comparable接口的排序规则:
自己(this) - 参数:升序,反之降序
【示例参考】:比较自定义类型
输出结果:
[Student{name='小明', age=18}, Student{name='小红', age=20}, Student{name='小蓝', age=19}] [Student{name='小明', age=18}, Student{name='小蓝', age=19}, Student{name='小红', age=20}]
Comparator的比较规则:
o1 - o2 升序
【参考代码】
public class TestComparator { public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(2); list.add(1); list.add(3); Collections.sort(list, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1 - o2;// 升序 // return o2 - o1;// 降序 } }); System.out.println(list);// [1, 2, 3] } }
【示例参考】:比较自定义类型
Comparator和Comparable的区别
Map集合的特点
注:映射由Map<K,V>
接口的实例表示,它不是继承自Collection
接口。
Map系列集合,常用子类的包括:
――HashMap
――LinkedHashMap
【HashMap集合】
java.util.HashMap<k , v >
集合implements Map<k , v>
接口.
HashMap集合的特点:
HashMap底层是哈希表:查询速度特别快
JDK1.8之前:数组 + 单项链表
JDK1.8之后:数组 + 单项链表/红黑树(链表长度超过8):提高查询速
HashMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致
【LinkedHashMap集合】
java.util.LinkedHashMap<k , v >
集合extends HashMap<k , v>
集合。
LinkedHashMap集合的特点:
LinkedHashMap底层是哈希表 + 链表(保证迭代的顺序)
HashMap集合是一个有序的集合,存储元素和取出元素的顺序是一致的
8.3Map接口中的常用方法
Map接口中定义了很多方法,常见如下:
public V put(K key , V value)
:把指定的键(key)和指定的值(value)添加到Map集合中public V remove(Object key)
:把指定的key所对应的value从Map集合中删除,返回被删元素的值public V get(Object key)
:在集合中获取指定key对应value的元素boolean containsKey(Object key)
:判断集合中是否含有xxxkeyboolean containsValue(Object key)
:判断集合中是否含有xxxvaluepublic Set<K> KeySet()
:把Map中所有的key打包成(存储到)set集合返回public Set< Map.Entry<K,V> > entrySet()
:获取Map中所有key和value对象的集合(Entry)存储在集合Set中【参考代码】
package Map; import java.util.HashMap; import java.util.Map; public class Test { public static void main(String[] args) { // 创建Map集合对象,多态 Map<Integer,String>map = new HashMap(); map.put(11,"小明"); map.put(22,"小红"); map.put(33,"小蓝"); map.put(44,"小绿"); System.out.println(map);// {33=小蓝, 22=小红, 11=小明, 44=小绿} HashMap无序的 map.remove(44);// 删除 System.out.println(map);// {33=小蓝, 22=小红, 11=小明} System.out.println(map.size()); //大小 3 System.out.println(map.containsKey(33)); //true System.out.println(map.containsValue("小蓝")); //true map.put(22,"小芳"); // {33=小蓝, 22=小芳, 11=小明} 若出现重复的key原来的数据会被顶替 System.out.println(map); // map.put(55,"小明"); // System.out.println(map);//是否被顶替却决于key,key映射value,而不是value映射key {33=小蓝, 22=小芳, 55=小明, 11=小明} System.out.println(map.keySet()); // [33, 22, 11] 把map中的key打包成Set集合的形式 System.out.println(map.get(33));// 小蓝 通过key查询value } }
方法一:通过键找值的方式
【参考代码】
package Map; import javax.swing.text.html.HTMLDocument; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class Test { public static void main(String[] args) { // 创建Map集合对象 Map<String,Integer>map = new HashMap<>(); map.put("小明",18); map.put("小红",18); map.put("小蓝",19); map.put("小绿",20); //1. 使用Map集合中的方法keySet(),把Map集合里所有的key取出来,存放到一个Set集合中\ Set<String> set = map.keySet(); //2.遍历set集合,获取Map集合中的每一个key /* 使用while遍历 */ Iterator <String> it = set.iterator(); while (it.hasNext()){ String key = it.next(); //3.通过Map集合中的get(key)方法,找到value Integer value = map.get(key); System.out.println(key+"="+value); } System.out.println("-----------------------"); /* 使用增强for遍历 */ for(String key : set){ //3.通过Map集合中的get(key)方法,找到value Integer value = map.get(key); System.out.println(key+"="+value); } } }
【总结】:
while――迭代器遍历:
Set<String> set = map.keySet(); Iterator <String> it = set.iterator(); while (it.hasNext()){ String key = it.next(); Integer value = map.get(key); System.out.println(key+"="+value); }
增强for遍历:
Set<String> set = map.keySet(); for(String key : set){ //3.通过Map集合中的get(key)方法,找到value Integer value = map.get(key); System.out.println(key+"="+value); }
方法二:键值对的方式遍历(更加面向对象)
把键值对当成一个整体遍历,增强for无法遍历,这个整体不是类型,因此Java提供了方法:
Map集合通过代码Set<Map.Entry<K,V>> ,将键值对元素转成了一个实体类型,此时得到的是一个Entry对象,类型是:Map.Entry<K,V>
【参考代码】
package Map; import javax.swing.text.html.HTMLDocument; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class Test { public static void main(String[] args) { // 创建Map集合对象 Map<String,Integer>map = new HashMap<>(); map.put("小明",18); map.put("小红",18); map.put("小蓝",19); map.put("小绿",20); //1.通过Map集合中的entrySet()方法,把Map集合中的多个Entry对象取出来,存储到一个Set集合中 Set<Map.Entry<String,Integer>> set = map.entrySet(); //遍历set集合,获取每一个Entry对象 //使用迭代器遍历set集合 Iterator <Map.Entry<String,Integer>> it = set.iterator(); while (it.hasNext()){ Map.Entry<String,Integer>entry = it.next(); // 使用Entry对象中的getKey()和getValue()方法获取键和值 String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(key+"="+value); } System.out.println("-----------"); //增强for for(Map.Entry<String,Integer> entry : set){ // 使用Entry对象中的getKey()和getValue()方法获取键和值 String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(key+"="+value); } } }
【总结】:
while――迭代器遍历:
Set<Map.Entry<String,Integer>> set = map.entrySet(); //遍历set集合,获取每一个Entry对象 //使用迭代器遍历set集合 Iterator <Map.Entry<String,Integer>> it = set.iterator(); while (it.hasNext()){ Map.Entry<String,Integer>entry = it.next(); // 使用Entry对象中的getKey()和getValue()方法获取键和值 String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(key+"="+value); }
增强for遍历:
//增强for for(Map.Entry<String,Integer> entry : set){ // 使用Entry对象中的getKey()和getValue()方法获取键和值 String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(key+"="+value); }
Entry:表示一个key和value,它提供了获取对应key和value的方法:
public K getKey()
:获取Entry中的key
public V getValue()
:获取Entry中的value
方法二图解:
8.5HashMap存储自定义类型键值
练习:每位学生(姓名,年龄)都有自己的家庭住址。那么,既然有对应关系,则将学生对象和家庭住址存储到map集合中,学生作为键,地址为值。
注:学生姓名、年龄相同则视为同一人
package Map; /* hashMap存储自定义类型键值: Map集合保证key是唯一的: 作为key元素,必须重写hashMap方法和equals方法,以保证key唯一 */ import java.util.HashMap; import java.util.Set; public class HashMapSavePerson { public static void main(String[] args) { show01(); /* 上海-->HashMapSavePerson{name='小蓝', age=18} 深圳-->HashMapSavePerson{name='小绿', age=18} 北京-->HashMapSavePerson{name='小红', age=18} key唯一 */ } /* hashMap存储自定义类型键值: key:String类型 String类重写hashCode方法和equals方法,可以保证key唯一 value:Person类型 value可以重复(同名同年龄视为重复) */ public static void show01(){ // 创造HashMap集合 HashMap<String,Person> map = new HashMap<>(); //往集合中添加元素 map.put("深圳",new Person("小明",18)); map.put("上海",new Person("小蓝",18)); map.put("北京",new Person("小红",18)); map.put("深圳",new Person("小绿",18)); // 使用keySet()增强for遍历map集合 Set<String> set = map.keySet(); for(String key:set){ Person value = map.get(key); System.out.println(key+"-->"+value); // 因为字符串类(Java帮我们的)重写了hashCode方法和equals方法,所以键(key)是不能重复的 } } }
Person类:
下面这个是我们自己定义的key的类型,Person类,上面例子的是String类:
package Map; /* hashMap存储自定义类型键值: Map集合保证key是唯一的: 作为key元素,必须重写hashMap方法和equals方法,以保证key唯一 */ import javax.swing.text.html.HTMLDocument; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class HashMapSavePerson { public static void main(String[] args) { show02(); } /* hashMap存储自定义类型键值: key:Person类型 Person就必须类重写hashCode方法和equals方法,来保证key唯一 value:String类型 value可以重复(同名同年龄视为重复) */ public static void show02(){ // 创造HashMap集合 HashMap<Person,String> map02 = new HashMap<>(); // 往集合中添加元素 map02.put(new Person("张三",18),"法外狂徒"); map02.put(new Person("黄老板",18),"英国"); map02.put(new Person("陈奕迅",18),"中国"); map02.put(new Person("张三",18),"法外狂徒"); // 使用迭代器遍历set集合中的Entry对象 Set<Map.Entry<Person,String>> set = map02.entrySet(); Iterator<Map.Entry<Person,String>> it = set.iterator(); while(it.hasNext()){ Map.Entry<Person,String> entry = it.next(); Person key = entry.getKey(); String value = entry.getValue(); System.out.println(key+"--->"+value); } } }
这里再介绍一下本例中Entry对象遍历的图解,再次加深印象:
8.6LinkedHashMap集合
我们知道HashMap保证key唯一,并且查询速度快,可是成对元素存放进去是没有顺序的(存和取的顺序可能不一致),那我们要如何保证顺序呢?
在HashMap下面有个LinkedHashMap(继承关系),它是链表(记录元素的顺序)和哈希表组合的一个数据存储结构,是个有序的集合
【参考代码】
package Map; import javax.swing.text.html.HTMLDocument; import java.util.*; public class Test { public static void main(String[] args) { HashMap<String,String> map = new LinkedHashMap<>(); map.put("a","a"); map.put("c","c"); map.put("b","b"); map.put("d","d"); System.out.println(map);//{a=a, c=c, b=b, d=d} } }
输出结果:(存储和取出的顺序是一样的)
{a=a, c=c, b=b, d=d}
看到这里,相信各位小伙伴们对Java集合这一章节的知识有了进一步的理解,尤其是一些在之前学习时可能没有注意到的知识或者原理,没关系,这次都帮你总结在一起了。最后,感谢看到这里的你!愿你韶华不负,青春无悔!
注: 由于自己刚刚开始学习Java不久,语言文字描述、技术等各方面还不是很好,如果文章有任何错误和建议,请各位大佬尽情评论留言!如果这篇文章对你有些许帮助,希望可爱亲切的您点个赞推荐一手,非常感谢啦