集合(上)
集合类的特点:提供一种存储空间可变的存储模型,存储的数据容量可以随时发生改变。
集合类体系结构:
注意:在学习的时候,首先会学习接口,而在使用的时候是使用实现类。为什么要先学习接口?假如说,我们先学习了Collection接口,那么以后在学习List或Set接口的时候,我们只需学习它的特有功能就可以了,同理,之后学习ArrayList、LinkedList的时候,也只需学习它的特有功能就可以了。而在使用的时候,要使用具体的实现类,原因很简单,因为接口是不能直接创建对象并实例化的,它必须通过具体的实现类来创建对象并实例化
一、Collection集合
-
它是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素。
-
JDK不提供此接口的任何直接实现,但它提供更具体的子接口(如Set和List)实现
创建Collection集合的对象
- 多态的方式
- 具体的实现类ArrayList
public static void main(String[] args) {
//创建Collection 集合的对象
Collection<String> c = new ArrayList<>();
//添加元素:boolean add(E e) E为<>中的类型
c.add("Hello");
c.add("World");
//输出集合对象
System.out.println(c);
}
/*
[Hello, World]
可以看出,集合中重写了toString方法(没有重写的话是输出包名,类名xxx@xxx)
*/
Collection集合常用方法
方法名 | 作用 |
---|---|
boolean add(E e) | 添加元素 |
boolean remove(Object o) | 从集合中移除指定的元素 |
void clear() | 清空集合中的元素 |
boolean contains(Object o) | 判断集合中是否存在指定的元素 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中元素的个数 |
public static void main(String[] args) {
//创建集合对象
Collection<String> c = new ArrayList<>();
//boolean add(E e):添加元素
c.add("hello");
c.add("world");
c.add("JAVA");
c.add("Here");
System.out.println(c); // [hello, world, JAVA, Here]
//boolean remove(Object o):从集合中移除指定的元素
System.out.println(c.remove("world")); // true
System.out.println(c.remove("Javaee")); // false
System.out.println(c); // [hello, JAVA, Here]
//void clear():清空集合中的元素
// c.clear();
// System.out.println(c);
//boolean contains(Object o):判断集合中是否存在指定的元素
System.out.println(c.contains("hello")); // true
System.out.println(c.contains("Javaee")); // false
//boolean isEmpty():判断集合是否为空
System.out.println(c.isEmpty()); // false
//int size():集合的长度,也就是集合中元素的个数
System.out.println(c.size()); // 3
}
Collection集合的遍历
Iterator中的常用方法
方法名 | 作用 |
---|---|
E nex() | 返回迭代中的下一个元素 |
boolean hasNext() | 如果迭代具有更多元素,则返回true(判断迭代器中是否有元素) |
public static void main(String[] args) {
//创建对象
Collection<String> c = new ArrayList<>();
//添加元素
c.add("hello");
c.add("world");
c.add("JAVA");
//Iterator<E> iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到
Iterator<String> it = c.iterator();
/*
//E nex():返回迭代中的下一个元素
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next()); //NoSuchElementException 表示被请求的元素不存在。 怎么解决?看下面
*/
/*
//boolean hasNext():如果迭代具有更多元素,则返回true(判断迭代器中是否有元素)
if (it.hasNext()){
System.out.println(it.next());
}
if (it.hasNext()){
System.out.println(it.next());
}
if (it.hasNext()){
System.out.println(it.next());
}
if (it.hasNext()){
System.out.println(it.next());
}
//这样就能解决 NoSuchElementException 异常了,那么可不可以在优化一下这四个if语句呢?用while循环试试
*/
while (it.hasNext()){
String s = it.next();
System.out.println(s);
}
/*
hello
world
JAVA
*/
}
集合的使用步骤
public static void main(String[] args) {
Collection<String> c = new ArrayList<>(); //步骤1、创建集合对象
c.add("hello"); //步骤2、添加元素到集合
c.add("world");
//步骤3、遍历集合
Iterator<String> it = c.iterator(); // 3.1、通过集合对象获取迭代器对象
while (it.hasNext()){ // 3.2、通过迭代器对象的hasNext()方法判断是否还有元素
String s = it.next(); // 3.3、通过迭代器对象的next()方法获取下一个元素
System.out.println(s);
}
}
案例:用Collection集合存储学生对象并遍历
需求:创建一个存储学生对象的集合,存储三个学生对小,使用程序实现在控制台遍历集合
思路:
1、定义学生类
2、创建Collection集合对象
3、创建学生对象
4、把学生添加到集合中
5、遍历集合(迭代器方式)
/*
1、定义学生类
定义私有属性:name,age,address,以及它们各自的get/set方法
定义有参、无参构造方法
重写toString方法
*/
public class Student {
private String name;
private int age;
private String address;
public Student() {
}
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
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;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
public class CollectionDM {
public static void main(String[] args) {
//2、创建集合对象
Collection<Student> c = new ArrayList<>();
//3、创建学生对象
Student s1 = new Student("张三",23,"北京");
Student s2 = new Student("李四",24,"上海");
Student s3 = new Student("王五",25,"广州");
//4、把学生添加到集合
c.add(s1);
c.add(s2);
c.add(s3);
//5、遍历集合(迭代器)
Iterator<Student> it = c.iterator();
while (it.hasNext()){
Student s = it.next();
System.out.println(s);
}
}
}
/*
结果输出:
Student{name='张三', age=23, address='北京'}
Student{name='李四', age=24, address='上海'}
Student{name='王五', age=25, address='广州'}
*/
二、List集合
List集合概述和特点
List集合概述
1、有序集合(也称为序列 )。 该界面的用户可以精确控制列表中每个元素的插入位置。
用户可以通过整数索引(列表中的位置)访问元素,并搜索列表中的元素。
2、与Set集合不同,列表通常允许重复的元素
List集合特点:
有序:存储和取出的元素顺序一致
可重复:存储的元素可以重复
public static void main(String[] args) {
//创建对象
List<String> list = new ArrayList<>();
//添加元素(List集合可以重复添加元素)
list.add("hello");
list.add("world");
list.add("hello");
list.add("world");
System.out.println(list); //[hello, world, hello, world]
//遍历集合(使用构造器)
Iterator<String> it = list.iterator();
while (it.hasNext()){
String s = it.next();
System.out.println(s);
}
/*
hello
world
hello
world
*/
}
List集合特有方法
方法名 | 作用 |
---|---|
void add(int index,E element) | 将指定的元素插入此列表中的指定位置。 |
E remove(int index) | 删除该列表中指定位置的元素。 |
E set(int index, E element) | 修改指定索引处的元素,返回被修改的元素。 |
E get(int index) | 返回指定索引处的元素。 |
public static void main(String[] args) {
//创建集合对象
List<String> list = new ArrayList<>();
//添加元素
list.add("hello");
list.add("nihao");
list.add("world");
//void add(int index,E element);将指定的元素插入此列表中的指定位置。
// list.add(4,"javaee"); // IndexOutOfBoundsException
list.add(1,"world");
System.out.println(list); // [hello, world, nihao, world]
//E remove(int index);删除该列表中指定位置的元素。
// list.remove(4); // IndexOutOfBoundsException
list.remove(1);
System.out.println(list); // [hello, nihao, world]
//E set(int index, E element);修改指定索引处的元素,返回被修改的元素。
// list.set(4,"Javaee"); // IndexOutOfBoundsException
list.set(2,"Java");
System.out.println(list); // [hello, nihao, Java]
// E get(int index);返回指定索引处的元素。
String s =list.get(1);
System.out.println(s); // nihao
}
案例:用List集合存储学生对象并遍历
需求:创建一个存储学生对象的集合,存储三个学生对小,使用程序实现在控制台遍历集合
思路:
1、定义学生类
2、创建List集合对象
3、创建学生对象
4、把学生添加到集合中
5、遍历集合(迭代器方式、for循环方式)
/*
1、定义学生类
定义私有属性:name,age,address,以及它们各自的get/set方法
定义有参、无参构造方法
重写toString方法
*/
(具体代码与上一节中的案例相同)
public static void main(String[] args) {
//创建List集合对象
List<Student> c = new ArrayList<>();
//创建学生对象
Student s1 = new Student("张三",23,"北京");
Student s2 = new Student("李四",24,"上海");
Student s3 = new Student("王五",25,"广州");
//把学生添加到集合中
c.add(s1);
c.add(s2);
c.add(s3);
//遍历集合:迭代器方式
Iterator<Student> it = c.iterator();
while (it.hasNext()) {
Student s = it.next();
System.out.println(s);
}
System.out.println("===========");
//遍历集合:for循环方式
for (int i = 0; i < c.size(); i++) {
Student ss = c.get(i);
System.out.println(ss);
}
}
/*
输出结果:
Student{name='张三', age=23, address='北京'}
Student{name='李四', age=24, address='上海'}
Student{name='王五', age=25, address='广州'}
===========
Student{name='张三', age=23, address='北京'}
Student{name='李四', age=24, address='上海'}
Student{name='王五', age=25, address='广州'}
*/
该main()方法与Collection案例中的main()方法的区别:
创建集合对象的不同,一个创建Collection 集合的对象,一个创建List集合对象,但他们都是在ArrayList的基础上创建的(这里用到了多态的知识)。其次另一个不同点是List案例用了普通for循环来遍历集合
并发修改异常
有一个集合:List list = new ArrayList<>();
里面有三个元素:
list.add(“hello”);
list.add(“world”);
list.add(“java”);
需求:遍历集合,得到每一个元素,看有没“world”这个元素,如果有,就添加个“javaee”元素。
public static void main(String[] args) {
//创建集合对象
List<String> list = new ArrayList<>();
//添加元素
list.add("hello");
list.add("world");
list.add("java");
//遍历集合:得到每一个元素,看有没“world”这个元素,如果有,就添加哟个“javaee”元素。
/*
Iterator<String> it = list.iterator();
while (it.hasNext()){
String s = it.next();
if (s.equals("world")){
list.add("javaee"); //通过集合来添加元素,不能用Iterator来添加元素,因为里面没有添加方法
}
}
// ConcurrentModificationException:当不允许这样的修改时,可以通过检测到对象的并发修改的方法来抛出此异常。
*/
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
if (s.equals("world")){
list.add("javaee");
}
}
}
小总结:
并发修改异常
ConcurrentModificationException
产生原因:
迭代器遍历的过程中,通过集合对象修改了几何元素中的长度,造成了迭代器获取元素中判断预期修改值和实际值不一致
解决方案:
用for循环遍历,然后用集合对象做对应的操作
ListIterator
ListIterator:列表迭代器
通过List集合的listIterator()方法得到,所以说他是List集合特有的迭代器
用于允许程序猿沿任一方向遍历列表的列表迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置
ListIterator中的常用方法:
方法名 | 作用 |
---|---|
E next() | 返回列表中的下一个元素,并且前进光标位置。 |
boolean hasNext() | 如果列表迭代器在向前方向遍历列表时有多个元素,则返回true。 |
E previous() | 返回列表中的上一个元素,并向后移动光标位置。 |
boolean hasPrevious() | 如果列表迭代器在相反方向遍历列表时有多个元素,则返回true。 |
void add(E e) | 将指定的元素插入列表。 |
public static void main(String[] args) {
//创建集合对象
List<String> list = new ArrayList<>();
//添加集合元素
list.add("hello");
list.add("world");
list.add("java");
/*
//通过List集合的listIterator()方法得到,所以说他是List集合特有的迭代器
ListIterator<String> lit = list.listIterator();
// 正向遍历:
// E next();返回列表中的下一个元素,并且前进光标位置。
// boolean hasNext();如果列表迭代器在向前方向遍历列表时有多个元素,则返回true。
while (lit.hasNext()){
String s = lit.next();
System.out.println(s);
}
System.out.println("================");
// 反向遍历:
// E previous();返回列表中的上一个元素,并向后移动光标位置。
// boolean hasPrevious();如果列表迭代器在相反方向遍历列表时有多个元素,则返回true。
while (lit.hasPrevious()){
String ss = lit.previous();
System.out.println(ss);
}
这个遍历方法了解即可,一般都使用Iterator迭代器进行正向遍历.
下面重点学习 void add(E e); 再与上一节学习的并发修改异常作比较.
*/
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()){
String s = lit.next();
if (s.equals("world")){
lit.add("javaee"); // 可以用ListIterator迭代器添加元素(该迭代器中有add方法),不能用Iterator来添加元素,
// 因为里面没有添加方法,因此在上一节用集合来添加元素
}
}
System.out.println(list); //[hello, world, javaee, java]
}
此案例中的代码与上一节并发修改异常中的代码大同小异,但此案例却可以通过迭代器实现集合的遍历,这是为啥?
原因在于ListIterator迭代器中有add方法,因此可以直接添加元素。而出现并发修改异常的代码中是借助ArrayList集合中add方法来添加。
//并发修改异常
Iterator<String> it = list.iterator();
while (it.hasNext()){
String s = it.next();
if (s.equals("world")){
list.add("javaee");
}
}
//ListIterator迭代器
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()){
String s = lit.next();
if (s.equals("world")){
lit.add("javaee");
}
}
增强for循环
增强For:简化数组和Collection集合的遍历
实现Iterator接口的类允许其对象成为增强for语句的目标
它是JDK5之后出现的,其内部原理是一个Iterator迭代器
增强for的格式
for(元素数据类型 变量名 :该数组或Collection集合){
//在此处使用变量即可,该变量就是元素
}
public static void main(String[] args) {
//遍历 int 数组
int[] arr = {1,2,3,4,5};
for(int i: arr){
System.out.println(i);
}
System.out.println("=============");
//遍历 String 数组
String[] s = {"hello","wolrd","java"};
for (String a :s){
System.out.println(a);
}
System.out.println("=============");
//遍历集合
List<String> l = new ArrayList<>();
l.add("hello");
l.add("world");
l.add("java");
for (String aa : l){
System.out.println(aa);
}
}
/*
输出结果:
1
2
3
4
5
=============
hello
wolrd
java
=============
hello
world
java
*/
如何验证加强for内部是一个Iterator迭代器?
进行以下操作,如果返回的是一个ConcurrentModificationException,则其内部是一个Iterator迭代器?
for (String ss : l){
if (ss.equals("world")){
l.add("javaee"); //ConcurrentModificationException
}
}
案例:用List集合存储学生对象并遍历。(与上一个案例大同小异)
(略)
数据结构
栈:
**队列 **:
数组:
查询数据通过索引定位,查询任意数据耗时相同,**查询效率高 **。(数组是一种查询快的模型)
删除数据时,要将原始数据删除,同时后面每个数据前移,删除效率低。(数组是一种增删慢的模型)
添加数据时,添加位置后的每个数据后移,再添加元素,添加效率极低。
链表:
链表是一种增删快的模型(对比数组)
链表是一种查询慢的模型(对比数组)
List集合子类特点
List集合常用子类: ArrayList、LinkedList
ArrayList:底层数据结构是数组,查询快、增删慢。
LinkedList:底层数据结构是链表,查询慢、增删快。
练习:
分别使用ArrayList和LinkedList完成存储字符串并遍历
public static void main(String[] args) {
//创建ArrayList集合对象
ArrayList<String> a = new ArrayList<>();
//添加集合元素
a.add("hello");
a.add("world");
a.add("java");
//遍历集合元素:增强for遍历
for (String s : a ){
System.out.println(s);
}
System.out.println("---------------");
//遍历集合元素:普通for遍历
for (int i = 0; i < a.size(); i++) {
String s1 = a.get(i);
System.out.println(s1);
}
System.out.println("---------------");
//遍历集合元素:迭代器遍历
Iterator<String> s2 = a.iterator();
while (s2.hasNext()){
System.out.println(s2.next());
}
System.out.println("=============");
//创建LinkedList集合对象
LinkedList<String> l = new LinkedList<>();
//添加集合元素
l.add("hello");
l.add("world");
l.add("java");
//遍历集合元素:增强for遍历
for (String ss : l){
System.out.println(ss);
}
System.out.println("---------------");
//遍历集合元素:普通for遍历
for (int i = 0; i < l.size(); i++) {
String ss1 = l.get(i);
System.out.println(ss1);
}
System.out.println("---------------");
//遍历集合元素:迭代器遍历
Iterator<String> ss2 = l.iterator();
while (ss2.hasNext()){
System.out.println(ss2.next());
}
}
/*
输出结果:
hello
world
java
---------------
hello
world
java
---------------
hello
world
java
=============
hello
world
java
---------------
hello
world
java
---------------
hello
world
java
*/
ArrayList集合(前面基本穿插学完)
LinkedList集合
LinkedList集合的常用方法:
方法名 | 作用 |
---|---|
public void addFirst(E e) | 在该列表开头插入指定的元素。 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾。 |
public E getFirst() | 返回此列表中的第一个元素。 |
public E getLast() | 返回此列表中的最后一个元素。 |
public E removeFirst() | 从此列表中删除并返回第一个元素。 |
public E removeLast() | 从此列表中删除并返回最后一个元素。 |
public static void main(String[] args) {
LinkedList<String> l = new LinkedList<>();
l.add("hello");
l.add("world");
l.add("java");
// public void addFirst(E e);在该列表开头插入指定的元素。
// public void addLast(E e);将指定的元素追加到此列表的末尾。
l.addFirst("qqq");
l.addLast("www");
System.out.println(l);
System.out.println("===============");
// public E getFirst();返回此列表中的第一个元素。
// public E getLast()返回此列表中的最后一个元素。
System.out.println(l.getFirst());
System.out.println(l.getLast());
System.out.println("===============");
// public E removeFirst()从此列表中删除并返回第一个元素。
// public E removeLast()从此列表中删除并返回最后一个元素。
System.out.println(l.removeFirst());
System.out.println(l.removeLast());
System.out.println(l);
}
/*
输出结果:
[qqq, hello, world, java, www]
===============
qqq
www
===============
qqq
www
[hello, world, java]
*/
三、Set集合
Set集合概述和特点
Set集合特点
不包含重复元素的集合
没有带索引的方法,所以不能使用普通for循环遍历
HashSet:
对集合的迭代次序不作任何保证。(即元素可能会乱序)
/*
Set集合练习:
存储字符串并遍历
*/
public static void main(String[] args) {
//创建集合对象
Set<String> s = new HashSet<>();
//添加元素
s.add("hello");
s.add("world");
s.add("java");
//不包含重复元素的集合
s.add("java");
//遍历
for (String ss : s){
System.out.println(ss);
}
}
哈希值
哈希值:
是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
Object类中有一个方法可以获取对象的哈希值:
public int hashCode();返回对象的哈希码值
对象的哈希值特点
同一个对象多次调用hashCode()方法返回的哈希值是相同的
默认情况下,不同对象的哈希值是不同的,而重写hashCode()方法,可以实现让不同对象的哈希值相同
/*
定义学生类:
定义私有属性:name,age,address,以及它们各自的get/set方法
定义有参、无参构造方法
重写toString方法
*/
(具体代码与上一节中的案例相同)
public static void main(String[] args) {
//创建学生对象
Student s1 = new Student("张三",23,"北京");
//同一个对象多次调用hashCode()方法返回的哈希值是相同的
System.out.println(s1.hashCode()); //460141958
System.out.println(s1.hashCode()); //460141958
System.out.println("===========");
Student s2 = new Student("李四",25,"上海");
//默认情况下,不同对象的哈希值是不同的,而重写hashCode()方法,可以实现让不同对象的哈希值相同
System.out.println(s2.hashCode()); //1163157884
System.out.println("===========");
System.out.println("hello".hashCode()); //99162322
System.out.println("world".hashCode()); //113318802
System.out.println("java".hashCode()); //3254818
}
HashSet集合概述和特点
HashSet集合特点:
底层数据结构是哈希表
对集合的迭代顺序不做任何保证,即不保证存储和取出的顺序一致
没有带索引的方法,所以不能使用普通for循环遍历
由于是Set集合,所以是不包含重复元素的集合
public static void main(String[] args) {
//创建集合对象
HashSet<String> hs = new HashSet<>();
//添加集合元素
hs.add("hello");
hs.add("world");
hs.add("java");
//遍历集合
for(String s : hs){
System.out.println(s);
}
}
/*
world
java
hello
//输出顺序与存储顺序不同,它是不保证存储和取出的顺序一致
*/
常见数据结构之哈希表
哈希表
JDK8之前,底层采用数组+链表实现,可以说是一个元素为链表的数组。
JDK8以后,在长度较长的是否,底层实现了优化
哈希表在存储时是如何保证元素唯一性的?
通过下图说明:
案例:HashSet集合存储学生对象并遍历
需求:创建一个学生对象的集合,存储多个学生对象,实用程序实现在控制台遍历该集合
要求:学生对象成员变量值相同时,我们就认为是同一个对象
思路:
1、定义学生类
2、创建HashSet集合对象
3、创建学生对象
4、把学生添加到集合
5、遍历集合(增强for循环)
/*
定义学生类
定义属性:name,age,及其各自的get/set方法
定义有参、无参构造方法
重写toString、equals和hashCode方法
*/
public static void main(String[] args) {
//创建学生集合对象
HashSet<Student> hs = new HashSet<>();
//创建并添加学生对象
Student s1 = new Student("张三",23);
Student s2 = new Student("李四",26);
//验证是否会输出相同的值
Student s3 = new Student("李四",26); //保证输出元素不重复,需要重写equals和hashCode方法
//把学生对象添加到集合
hs.add(s1);
hs.add(s2);
hs.add(s3);
for (Student s : hs){
System.out.println(s);
}
}
/*
Student{name='张三', age=23}
Student{name='李四', age=26}
*/
LinkedHashSet集合概述和特点
LinkedHashSet集合特点:
哈希表和链表实现的Set接口,具有可预测的迭代次序
由链表保证元素有序,即元素的存储和取出顺序是一致的
由哈希表保证元素唯一,即没有重复的元素
/*
LinkedHashSet集合练习:存储字符串并遍历
*/
public static void main(String[] args) {
//创建集合对象
LinkedHashSet<String> lhs = new LinkedHashSet<>();
//添加元素
lhs.add("hello");
lhs.add("world");
lhs.add("java");
//验证是否会添加重复元素
lhs.add("java");
//遍历集合
for (String s : lhs){
System.out.println(s);
}
}
/*
hello
world
java
*/
TreeSet集合概述和特点
TreeSet集合特点
元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方法取决于构造方法
TreeSet():根据其元素的自然排序进行排序
TreeSet(Comparator comparator):根据指定的比较器进行排序
没有带索引的方法,所以不能使用普通for循环遍历
由于是Set集合,所以不包含重复元素的集合
/*
TreeSet集合练习:存储字符串并遍历
*/
public static void main(String[] args) {
//创建集合对象
TreeSet<Integer> ts = new TreeSet<>();
//添加集合元素
ts.add(10);
ts.add(20);
ts.add(30);
ts.add(30);
ts.add(40);
//遍历集合元素
for (int s : ts){
System.out.println(s);
}
}
/*
10
20
30
40
*/
自然排序Comparable的使用
存储学生对象并遍历,创建TreeSet集合使用无参构造方法
要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
/*
定义学生类
定义属性:name,age,及其各自的get/set方法
定义有参、无参构造方法
实现自然排序(先实现Comparable<E>,然后重写compareTo(E e)方法)
*/
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student s) {
// return 0; //返回值是0,系统会默认元素是重复的,就不会存储要添加的元素。
// return 1; //按照升序来存储
// return -1; //按照降序来存储
int num = this.age - s.age; //按照年龄升序存储
// int num = s.age - this.age; //按照年龄降序存储(相同的不存储)
int num2 = num == 0? this.name.compareTo(s.name):num; //判断年龄是否等于零,若等于零,则判断名字是否相同
return num2; //注:this.name.compareTo(s.name) 得出的是0/1/-1,因此可以用int来接收
}
}
public static void main(String[] args) {
//创建集合对象
TreeSet<Student> ts = new TreeSet<>();
/*
//创建学生对象并添加
Student s1 = new Student("张三",23);
Student s2 = new Student("王宝强",29);
Student s3 = new Student("成龙",39);
Student s4 = new Student("周润发",39);
*/
//创建学生对象并添加
Student s1 = new Student("zhangsan",23);
Student s2 = new Student("wangbaoqiang",29);
Student s3 = new Student("chenglong",39);
Student s4 = new Student("zhourunfa",39);
Student s5 = new Student("zhourunfa",39); //观察重复元素是否会输出
//把学生添加到集合
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
//遍历集合
for (Student s : ts){
System.out.println(s);
}
}
/*
Student{name='张三', age=23}
Student{name='王宝强', age=29}
Student{name='周润发', age=39}
Student{name='成龙', age=39}
*/
/*
Student{name='zhangsan', age=23}
Student{name='wangbaoqiang', age=29}
Student{name='chenglong', age=39}
Student{name='zhourunfa', age=39}
*/
小结:
1、用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
2、自然排序,就是让元素所属的类实现Comparable接口,重写compareTo方法
3、重写方法时,一定要注意排序规则必须按照要求的主要任务和次要任务来写
比较器排序Comparator的使用
存储学生对象并遍历,创建TreeSet集合使用有参构造方法
要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
/*
定义学生类
定义属性:name,age,及其各自的get/set方法
定义有参、无参构造方法
实现自然排序(先实现Comparable<E>,然后重写compareTo(E e)方法)
*/
public static void main(String[] args) {
//使用有参构造创建集合对象(匿名内部类创建比较器进行排序)
TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// this --- s1 ; s --- s2
int num1 = s1.getAge() - s2.getAge();
int num2 = num1 == 0? s1.getName().compareTo(s2.getName()) : num1;
return num2;
}
});
//创建学生对象并添加
Student s1 = new Student("zhangsan",23);
Student s2 = new Student("wangbaoqiang",29);
Student s3 = new Student("chenglong",39);
Student s4 = new Student("zhourunfa",39);
Student s5 = new Student("zhourunfa",39); //观察重复元素是否会输出
//把学生添加到集合
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
//遍历集合
for (Student s : ts){
System.out.println(s);
}
}
/*
Student{name='zhangsan', age=23}
Student{name='wangbaoqiang', age=29}
Student{name='chenglong', age=39}
Student{name='zhourunfa', age=39}
*/
小结:
1、 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序
2、 比较器排序:是让集合构造方法接收Comparator的实现类对象,重写Comparae(T o1,T o2)方法
3、 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
案例:成绩排序
需求: 用TreeSet集合存储多个学生信息(姓名,语文成绩,数学成绩),并遍历集合
要求:总分从高到低
思路:
1、定义学生类
2、创建TreeSet集合对象,通过比较器排序进行排序(自己试试通过自然排序实现)
3、创建学生对象
4、把学生对象添加到集合
5、遍历集合
方法一:通过比较器排序进行排序
/*
定义学生类:
定义属性:name,ChinaScore,MathScore,及其各自的get/set方法
定义有参、无参构造方法
重写toString方法
*/
public static void main(String[] args) {
//创建TreeSet集合对象,通过比较器进行排序
TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// int num = (s2.getChinaScore() + s2.getMathScore()) - (s1.getChinaScore() + s1.getMathScore());
//主要条件
int num = s2.getSum() - s1.getSum(); //判断总分是否相同
//次要条件
int num2 = num == 0? s2.getChinaScore()-s1.getChinaScore(): num; //若总分相同,看语文成绩是否相同(若相同则数学成绩也相同)
int num3 = num2 == 0? s2.getName().compareTo(s1.getName()): num2; //若总分相同,判断名字是否相同
return num3;
}
});
//添加学生信息
Student s1 = new Student("张三", 80,96);
Student s2 = new Student("李四", 85,99);
Student s3 = new Student("王五", 88,86);
Student s4 = new Student("老六", 78,66);
Student s5 = new Student("桃七", 88,86); //不同学生,语文数学成绩都相同时,是否会输出
//将学生添加到集合
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
//遍历集合
for (Student s :ts){
System.out.println(s + "," + s.getSum());
}
}
/*
Student{name='李四', ChinaScore=85, MathScore=99},184
Student{name='张三', ChinaScore=80, MathScore=96},176
Student{name='王五', ChinaScore=88, MathScore=86},174
Student{name='桃七', ChinaScore=88, MathScore=86},174
Student{name='老六', ChinaScore=78, MathScore=66},144
*/
方法二:通过自然排序实现
/*
定义学生类
定义属性:name,age,及其各自的get/set方法
定义有参、无参构造方法
实现自然排序(先实现Comparable<E>接口,然后重写compareTo(E e)方法)
*/
public class Student implements Comparable<Student> { 实现Comparable<E>接口
...
//重写compareTo(E e)方法
@Override
public int compareTo(Student s) {
int num1 = s.getSum() - this.getSum();
int num2 = num1 == 0? s.getChinaScore() - this.getChinaScore() : num1;
int num3 = num2 == 0? s.getName().compareTo(this.getName()) : num2;
return num3;
}
}
public static void main(String[] args) {
//创建集合对象
TreeSet<Student> ts = new TreeSet<>();
//添加学生信息
Student s1 = new Student("张三", 80,96);
Student s2 = new Student("李四", 85,99);
Student s3 = new Student("王五", 88,86);
Student s4 = new Student("老六", 78,66);
Student s5 = new Student("桃七", 88,86); //不同学生,语文数学成绩都相同时
//将学生添加到集合
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
//遍历集合
for (Student s :ts){
System.out.println(s + "," + s.getSum());
}
}
/*
Student{name='李四', ChinaScore=85, MathScore=99},184
Student{name='张三', ChinaScore=80, MathScore=96},176
Student{name='王五', ChinaScore=88, MathScore=86},174
Student{name='桃七', ChinaScore=88, MathScore=86},174
Student{name='老六', ChinaScore=78, MathScore=66},144
*/
案例:不重复的随机数
需求:编写一个程序,获取10个1-20之间的随机数,要求随机数不能重复,并在控制台输出
思路:
1、创建Set集合对象
2、创建随机数对象
3、判断集合的长度是否小于10
是:产生一个随机数添加到集合
回到3继续
4、遍历集合
public static void main(String[] args) {
//创建集合对象
Set<Integer> s = new HashSet<>();
//创建随机数对象
Random r = new Random();
//判断集合长度是否小于10
while (s.size()<10){
//产生一个随机数
int num = r.nextInt(20)+1;
s.add(num);
}
//遍历集合
for (int i : s){
System.out.println(i);
}
}
/*
16
1
2
4
5
6
7
10
14
15
*/