集合与数组一样,是一种容器。下面做个集合与数组的对比:
对比项 | 数组 | 集合 |
---|---|---|
容量 | 容量不可变 | 容量可变 |
可存类型 | 基本数据类型 & 引用数据类型 | 引用数据类型 |
类型唯一 | 只能存放同一种类型 | 可存放不同类型(一般还是存放同一类型) |
Collection 常用方法
创建Collection对象需要通过多态的形式,其根据 new 的实现类不同体现不同的特性:
List接口下实现类:[有序]、有下标、可重复
Set接口下实现类:[无序]、无下标、不可重复
常用方法名 | 方法描述 |
---|---|
boolean add(Object o) | 添加一个对象到集合 |
boolean remove(Object o) | 删除指定对象 |
void clear() | 删除集合所有元素 |
boolean contains(Object o) | 查询集合是否有 o 对象 |
int size() | 返回此集合的元素个数 |
boolean isEmpty() | 判断集合是否为空 |
boolean addAll(Collection c) | 将一个集合的所有对象添加到本集合(可自己添加自己) |
boolean equals(Object o) | 比较两个集合中元素是否相同 |
Object[ ] toArray() | 将此集合转换成数组 |
Iterator iterator() | 返回此集合中元素的迭代器。(重点方法) |
Iterator 迭代器
说明一下 Iterator 类,它是专为集合所准备的遍历器,其中包含3个主要方法:
Iterator类的方法 | 方法描述 |
---|---|
boolean hasNext() | 判断迭代器中是否有下一个元素 |
E next() | 返回迭代器中的下一个元素并前进光标位置 |
void remove() | 集合中移除此迭代器返回的最后一个元素(必须先用next()方法才能使用此方法) |
Collection 方法使用
添加元素:
public abstract class Demo1 {
public static void main(String[] args) {
Collection clc = new ArrayList();
//添加元素
clc.add("华为");
clc.add("苹果");
clc.add("小米");
clc.add("OPPO");
//查看集合元素个数
System.out.println("集合中有:"+clc.size()+"个元素");
System.out.println(clc);
}
}
---*---
输出结果:
集合中有:4个元素
[华为, 苹果, 小米, OPPO]
删除元素:
public abstract class Demo1 {
public static void main(String[] args) {
Collection clc = new ArrayList();
//添加元素
clc.add("华为");
clc.add("苹果");
clc.add("小米");
clc.add("OPPO");
//查看集合元素个数
System.out.println("集合中有:"+clc.size()+"个元素");
System.out.println(clc);
//删除元素
clc.remove("小米");
System.out.println("集合中有:"+clc.size()+"个元素");
clc.clear();
System.out.println("集合中有:"+clc.size()+"个元素");
}
}
---*---
输出结果:
集合中有:4个元素
[华为, 苹果, 小米, OPPO]
集合中有:3个元素
集合中有:0个元素
判断:
public abstract class Demo1 {
public static void main(String[] args) {
Collection clc = new ArrayList();
//添加元素
clc.add("华为");
clc.add("苹果");
clc.add("小米");
clc.add("OPPO");
//查看集合元素个数
System.out.println("集合中有:"+clc.size()+"个元素");
System.out.println(clc);
//判断是否有此元素
System.out.println(clc.contains("小米"));
//判断集合是否为空
System.out.println(clc.isEmpty());
}
}
---*---
输出结果:
集合中有:4个元素
[华为, 苹果, 小米, OPPO]
true
false
遍历:
public abstract class Demo1 {
public static void main(String[] args) {
Collection clc = new ArrayList();
//添加元素
clc.add("华为");
clc.add("苹果");
clc.add("小米");
clc.add("OPPO");
//查看集合元素个数
System.out.println("集合中有:"+clc.size()+"个元素");
System.out.println(clc);
//遍历元素
System.out.println("-----增强for遍历-----");
for (Object o: clc) {
String s = (String)o;
System.out.println(o);
}
System.out.println("-----Iterator遍历-----");
Iterator it = clc.iterator();
while (it.hasNext()){
String s = (String)it.next();
System.out.println(s);
}
}
}
---*---
输出结果:
集合中有:4个元素
[华为, 苹果, 小米, OPPO]
-----增强for遍历-----
华为
苹果
小米
OPPO
-----Iterator遍历-----
华为
苹果
小米
OPPO
特点
List 接口继承 Collection 接口,特点是 List 有序、有下标、元素可重复。所以这里着重介绍 List 特有方法。
List方法
常用方法 | 方法描述 |
---|---|
void add(int index,Object o) | 在index位置插入对象O |
E remove(int index) | 取走集合中指定位置元素 |
E set(int index, E element) | 替换集合中指定位置元素 |
E get(int index) | 返回集合中指定位置元素 |
boolean addAll(int index, Collection c) | 将一个集合的所有对象添加到本集合指定位置 |
List subList(int fromIndex, int toIndex) | 返回formIndex(包含)到toIndex(不包含)之间的元素集合 |
int indexOf(Object o) | 返回集合中 o 元素的下标 |
ListIterator listIterator() | 返回此列表中元素的列表迭代器(正反迭代) |
ListIterator 列表迭代器
ListIterator 类继承 Iterator,它是专为List集合所准备的遍历器,可以从反方向遍历集合。下面说一下其特有方法:
ListIterator特有方法 | 方法描述 |
---|---|
boolean hasPrevious() | 判断当前光标的上一个位置是否有元素 |
E Previous() | 返回当前光标的上一个位置元素并后移动光标位置 |
void add(E e) | 将指定的元素插入集合 |
void set(E e) | 用指定的元素替换 next()或 previous()返回的最后一个元素 |
int nextIndex() | 返回当前光标位置数值 |
int previousIndex() | 返回当前光标位置的上一个数值 |
List 方法使用
通过下标插入元素:
public class Demo {
public static void main(String[] args) {
List list = new ArrayList();
list.add("苹果");
list.add("华为");
list.add(0,"小米");//通过下标添加元素
System.out.println("集合元素个数为:"+list.size());
System.out.println(list);
}
}
---*---
输出结果:
集合元素个数为:3
[小米, 苹果, 华为]
通过下标删除元素:
public class Demo {
public static void main(String[] args) {
List list = new ArrayList();
list.add("苹果");
list.add("华为");
list.add(0,"小米");
System.out.println("删除前集合元素个数为:"+list.size());
System.out.println("删除的元素是:"+list.remove(1));//通过下标删除元素
System.out.println("删除后集合元素个数为:"+list.size());
System.out.println(list);
}
}
---*---
输出结果:
删除前集合元素个数为:3
删除的元素是:苹果
删除后集合元素个数为:2
[小米, 华为]
通过下标替换元素:
public class Demo {
public static void main(String[] args) {
List list = new ArrayList();
list.add("苹果");
list.add("华为");
list.add(0,"小米");
System.out.println("替换掉的元素是:"+list.set(1, "中兴"));//替换元素
System.out.println("集合元素个数为:"+list.size());
System.out.println(list);
}
}
---*---
输出结果:
替换掉的元素是:苹果
集合元素个数为:3
[小米, 中兴, 华为]
遍历:
public class Demo {
public static void main(String[] args) {
List list = new ArrayList();
list.add("苹果");
list.add("华为");
list.add(0,"小米");
System.out.println(list);
System.out.println("----for遍历集合----");
for (int i = 0; i < list.size(); i++) {
String s = (String)list.get(i);
System.out.println(s);
}
System.out.println("----增强for遍历集合----");
for (Object o:list) {
String s = (String) o;
System.out.println(s);
}
System.out.println("----Iterator遍历集合----");
Iterator it = list.iterator();
while(it.hasNext()){
String s = (String) it.next();
System.out.println(s);
}
System.out.println("----ListIterator遍历集合----");
ListIterator listit = list.listIterator();
while (listit.hasNext()){
String s = (String) listit.next();
System.out.println(s);
}
while (listit.hasPrevious()){
String s = (String)listit.previous();
System.out.println(s);
}
}
}
---*---
输出结果:
[小米, 苹果, 华为]
----for遍历集合----
小米
苹果
华为
----增强for遍历集合----
小米
苹果
华为
----Iterator遍历集合----
小米
苹果
华为
----ListIterator遍历集合----
小米
苹果
华为
华为
苹果
小米
补充:增删基本数据类型
public class Demo {
public static void main(String[] args) {
List list = new ArrayList();
//添加数字自动装箱
list.add(10);
list.add(20);
list.add(30);
list.add(40);
list.add(50);
System.out.println(list);
//删除操作
// list.remove(20); remove方法默认以下标删除
list.remove((Object) 20);
list.remove(Integer.valueOf(20));
System.out.println(list);
}
}
---*---
输出结果:
[10, 20, 30, 40, 50]
[10, 30, 40, 50]
ArrayList 、LinkedList 和 Vector 都实现了 List 接口( List 接口有一个 AbstractList 抽象类用来重写通用方法),所以它们的使用方法也基本是相同的。下面说一下区别:
类名 | 存储结构 | 适用场景 | 线程安全 |
---|---|---|---|
ArrayList | 数组结构 | 查询快、增删慢 | X |
LinkedList | 链表结构 | 查询慢、增删快 | X |
Vector | 数组结构 | 查询快、增删慢 | √ |
特点
Set 接口继承 Collection 接口,特点是 Set [无序]、无下标、元素不重复。Set集合没有特有方法,全继承自 Collection 接口。其根据 new 的实现类不同体现不同的特性:
元素不重复原理
在 HashSet 对象添加元素时会进行以下两次判断:
啥是哈希值
1.哈希值是JDK根据对象的 地址或字符串或数字 算出来的 int类型的数值。
2.因为 Object 类中有一个 hashCode() 方法,该方法返回对象的哈希码值。所以任何对象都有哈希码值。
//学生类
class Student {
private String name;
private int age;
public Student(String name ,int age) {
this.name = name;
this.age = age;
}
}
//测试类
public class Demo {
public static void main(String[] args) {
Student s1 = new Student("赵子龙",27);
Student s2 = new Student("张翼德",33);
System.out.println("s1的哈希值为:"+s1.hashCode());
System.out.println("s1的哈希值为:"+s1.hashCode());
System.out.println("s2的哈希值为:"+s2.hashCode());
}
}
---*---
输出结果:
s1的哈希值为:1239731077
s1的哈希值为:1239731077
s2的哈希值为:557041912
可以看出,同一个对象的哈希码值是不变的。不同对象的哈希码值是不同的。(重写hashCode方法可以让同类型不同对象的哈希值相同)
重写 hashCode 方法和 equals 方法
默认情况下同一个类的不同对象的哈希值是不同的。实际中我们希望一个类的多个对象属性不同时才视为不同元素。
重写 hashCode 方法:为了让属性个数和值相同的对象,哈希值相同。(注意:这里不同类型的属性个数和值相同,哈希值也会相同)
重写 equals 方法:为了判对象是否是同一类型。
//学生类
class Student {
private String name;
private int age;
public Student(String name ,int age) {
this.name = name;
this.age = age;
}
//重写equals方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
//重写hashCode方法
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
//测试类
public class Demo {
public static void main(String[] args) {
HashSet<Student> hs = new HashSet<>();
Student s1 = new Student("赵子龙",27);
Student s2 = new Student("张翼德",33);
Student s3 = new Student("张翼德",33);
hs.add(s1);//成功添加
hs.add(s2);//成功添加
hs.add(s3);
hs.add(s1);
System.out.println("集合元素个数:"+hs.size());
System.out.println("s1的哈希值为:"+s1.hashCode());
System.out.println("s2的哈希值为:"+s2.hashCode());
System.out.println("s3的哈希值为:"+s3.hashCode());
}
}
---*---
输出结果:
集合元素个数:2
s1的哈希值为:1102553374
s2的哈希值为:757717223
s3的哈希值为:757717223
重写了hashCode方法和equals方法,当哈希值相同而且还是同一对象时。我们就视为同一元素,不存储该元素。
Set 集合存储并遍历
public class Demo {
public static void main(String[] args) {
Set<String> ss = new HashSet<>();
//添加元素
ss.add("1.苹果");
ss.add("2.华为");
ss.add("3.vivo");
ss.add("3.vivo");//来一个重复的元素
ss.add("4.菠萝");
//遍历
for (String str : ss){
String s1 = str;
System.out.println(s1);
}
}
}
---*---
输出结果:
3.vivo
1.苹果
2.华为
4.菠萝 //可以看出元素无序且不重复
结果可以看出HashSet集合存储元素是无序的并且不重复。
类名 | 存储结构 | 适用场景 |
---|---|---|
HashSet | 哈希表(数组+链表) | 不重复、无序 |
TreeSet | 红黑树 | 不重复、定制排序 |
LinkdeHashSet | 哈希表+链表 | 不重复、有序 |
public class Demo {
public static void main(String[] args) {
Set<String> ss = new HashSet<>();
ss.add("1.苹果");
ss.add("2.华为");
ss.add("3.vivo");
ss.add("4.三星");
ss.add("4.三星");
//遍历
Iterator<String> it = ss.iterator();
while (it.hasNext()){
String s = it.next();
System.out.println(s);
}
}
}
---*---
输出结果:
3.vivo
4.三星
1.苹果
2.华为
特点
TreeSet 存储结构
Tree翻译是树的意思,TreeSet以节点形式存储数据,每个节点可有两个子节点。当存储数据时,第一个数据将存储在根节点上,之后的数据都会和根节点比较,比根节点小存入根节点左边子节点,比根节点大存入根节点右边子节点,和根节点相等则视为相同数据不存储。当子节点已经有数据时,存入数据和子节点数据进行比较,比子节点小存左,比子节点大存右。以此类推,找到没有数据的节点。
无参构造存放学生对象并遍历(自然排序)
//学生类
class Student implements Comparable<Student>{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//该方法用于判断是否元素相同和存储位置
@Override
public int compareTo(Student s) {
int num = this.age-s.age;
int num2 = num==0? this.name.compareTo(s.name):num;
return num2;
}
}
//测试类
public class Demo {
public static void main(String[] args) {
TreeSet<Student> trs = new TreeSet<>();
Student s1 = new Student("zhanglei", 18);
Student s2 = new Student("xiaohong", 8);
Student s3 = new Student("renxianqi", 38);
Student s4 = new Student("wanglihong", 38);
trs.add(s1);//如果没有继承Comparable则抛出异常
trs.add(s2);
trs.add(s3);
trs.add(s4);
trs.add(s4);
System.out.println("集合中元素个数:"+trs.size());
for (Student stu: trs){
System.out.println(stu.name+","+stu.age);
}
}
}
---*---
输出结果:
集合中元素个数:4
xiaohong,8
zhanglei,18
renxianqi,38
wanglihong,38
注意:使用TreeSet集合无参构造创建的对象添加元素,需要元素对象的类型必须实现Comparable接口,重写CompareTo方法(方法用于判断元素是否重复和元素存放位置)。
带参构造存放学生对象并遍历(比较器排序)
//学生类
class Student {
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
//测试类
public class Demo {
public static void main(String[] args) {
TreeSet<Student> trs = new TreeSet<>(new Comparator<Student>() {
//该方法和CompareTo功能一样
@Override
public int compare(Student o1, Student o2) {
int num = o1.age - o2.age;
int num2 = num==0?o1.name.compareTo(o2.name):num;
return num2;
}
});
Student s1 = new Student("zhanglei", 18);
Student s2 = new Student("xiaohong", 8);
Student s3 = new Student("renxianqi", 38);
Student s4 = new Student("wanglihong", 38);
trs.add(s1);
trs.add(s2);
trs.add(s3);
trs.add(s4);
trs.add(s4);
System.out.println("集合中元素个数:"+trs.size());
for (Student stu: trs){
System.out.println(stu.name+","+stu.age);
}
}
}
---*---
输出结果:
集合中元素个数:4
xiaohong,8
zhanglei,18
renxianqi,38
wanglihong,38
使用比较器排序可以实现和自然排序一样的功能,区别是自然排序需要在元素类型中实现Comparable接口,比较器是在TreeSet构造方法中传一个Comparator。
Comparable 和 Comparator 接口
使用TreeSet集合存储元素,需要元素对象类型实现Comparable接口重写其compareTo方法或者使用TreeSet的带参构造传入一个Comparator(比较器)对象。两者其本质是一样的,需要将存入元素和已有元素进行对比,以确定存入的元素是否重复,和存放的位置。(具体参考上面的 TreeSet 存储结构)。
public class Demo {
public static void main(String[] args) {
TreeSet<Integer> is = new TreeSet<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;//o2-o1反序 --- o1-o2正序
}
});
is.add(20);
is.add(50);
is.add(10);
is.add(30);
is.add(40);
for (Integer ii : is) {
System.out.println(ii);
}
}
}
---*---
输出结果:
50
40
30
20
10
上面是调用TreeSet带参构造创建的集合对象,存储Integer对象。着重说明一下Comparator中的compare方法。
public int compare(Integer o1, Integer o2) { return o2-o1;}
该方法会返回一个int类型的值,当集合存储元素时,会把存即将入元素(o1)和已有元素(o2)进行比较。o1-o2=0说明两元素相同不进行存储,o1-o2>0将o1存储到o2元素后面,o1-o2<0将o1存储到o2元素前面。
练习:TreeSet存储字符串元素,以长短排序,相同长度以字母排序。
public class Demo1 {
public static void main(String[] args) {
//带参构造创建集合对象
TreeSet<String> ss = new TreeSet<>(new Comparator<String>() {
//重写compare方法
@Override
public int compare(String o1, String o2) {
int num = o1.length()-o2.length();
int num2 = num==0?o1.compareTo(o2):num;
return num2;
}
});
//添加元素
ss.add("xyz");
ss.add("abc");
ss.add("xz");
ss.add("x");
ss.add("xysdsz");
ss.add("xyaaadfz");
ss.add("aab");
ss.add("aaa");
for (String s:ss ) {
System.out.println(s);
}
}
}
---*---
输出结果:
x
xz
aaa
aab
abc
xyz
xysdsz
xyaaadfz
特点
LinkdeHashSet集合的存储结构是哈希表+链表实现的,其存储元素不重复且有序。有序是由链表实现的。
public class Demo {
public static void main(String[] args) {
LinkedHashSet<String> ss = new LinkedHashSet<>();
ss.add("1.苹果");
ss.add("2.华为");
ss.add("3.vivo");
ss.add("4.三星");
ss.add("4.三星");
//遍历
for (String str : ss){
System.out.println(str);
}
}
}
---*---
输出结果:
1.苹果
2.华为
3.vivo
4.三星
LinkedHashSet 算是 HashSet 的增强版,让元素不重复并且实现了存取顺序一致。
概述
简单来说就是,集合中的元素个数修改时会有一个A变量计数,创建迭代器时将集合中的A赋值给了迭代器中的变量B。在迭代器调用next()方法时会先判断A是否等于B,如果不等于就抛出并发修改异常。所以在创建迭代器之后不能使用集合中的方法进行增删元素,只能使用迭代器中的方法增删。
演示代码
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Demo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("苹果");
//拿到该集合的迭代器
Iterator<String> iterator = list.iterator();
//修改集合元素个数
list.add("榴莲");
while (iterator.hasNext()){
String s = iterator.next();//此处报错
System.out.println(s);
}
}
}
---*---
输出结果:
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1043)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:997)
at com.Zy.Demo.main(Demo.java:17)
解决办法:使用增强for代替Iterator
创建 Map 对象需要通过多态的形式,其根据 new 的实现类不同体现不同的特性:
Map 常用方法
常用方法名 | 描述 |
---|---|
V put(K kkey,V value) | 添加元素 |
V remove(Object key) | 根据键删除键值对元素 |
void clear() | 删除集合所有元素 |
boolean containsKey(Object key) | 判断集合是否包含指定的键 |
boolean containsValue(Object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 返回集合长度 |
V get(Object key) | 根据键获取值 |
Set< K > keySet() | 获取所有键的集合 |
Collection< V > values() | 获取所有值的集合 |
Set< Map.Entry< K,V >> entrySet() | 获取所有键值对对象的集合 |
方法练习
添加元素(提示:添加元素出现键相同值不同时,值将被替换。)
public class Demo {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("cn","中国");
map.put("usa","美国");
map.put("br","巴西");
map.put("cu","古巴");
map.put("cu","巴古");
System.out.println(map);
}
}
---*---
输出结果:
{
br=巴西, usa=美国, cu=巴古, cn=中国}
删除元素
public class Demo {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("cn","中国");
map.put("usa","美国");
map.put("br","巴西");
map.put("cu","古巴");
System.out.println("集合元素个数:"+map.size());
map.remove("usa");
System.out.println("删除后元素个数:"+map.size());
}
}
---*---
输出结果:
集合元素个数:4
删除后元素个数:3
遍历
public class Demo {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("cn","中国");
map.put("usa","美国");
map.put("br","巴西");
map.put("cu","古巴");
System.out.println("集合元素个数:"+map.size());
//keySet遍历
Set<String> strings = map.keySet();
for (String s:strings ) {
//strings可以替换为map.keySet()
System.out.println(s+","+map.get(s));
}
System.out.println("--------");
//entrySet遍历
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String,String> ii: entries){
entries可以替换为map.entrySet()
System.out.println(ii.getKey()+","+ii.getValue());
}
}
}
---*---
输出结果:
集合元素个数:4
br,巴西
usa,美国
cu,古巴
cn,中国
--------
br,巴西
usa,美国
cu,古巴
cn,中国
添加学生对象并遍历(为了简洁学生类重写了toString方法)
public class Demo {
public static void main(String[] args) {
Map<String,Student> map = new HashMap<>();
//创建学生对象
Student s1 = new Student("张磊", 15);
Student s2 = new Student("小明", 18);
Student s3 = new Student("小黑", 20);
//添加元素
map.put("001",s1);
map.put("002",s2);
map.put("003",s3);
//遍历
for (Map.Entry<String,Student> ii:map.entrySet()){
String key = ii.getKey();
Student value = ii.getValue();
System.out.println("学号"+key+"\t"+value);
}
}
}
---*---
输出结果:
学号001 Student{
name='张磊', age=15}
学号002 Student{
name='小明', age=18}
学号003 Student{
name='小黑', age=20}
HashMap 和 HashSet 一样要想实现存入键的元素不重复需要重写键元素对象类的hashCode方法和equals方法。
LinkedHashMap 和 LinkedHashSet一样,算是 HashMap 的增强版,让键元素不重复并且实现了存取顺序一致。
TreeMap 和 TreeSet 一样要想实现存入键的元素不重复需要键元素对象类型实现Comparable接口或者使用TreeMap的带参构造传入一个Comparator(比较器)对象。
HashMap添加学生对象和住址
public class Demo {
public static void main(String[] args) {
HashMap<Student,String> treeMap = new HashMap<>();
Student s1 = new Student("张磊", 15);
Student s2 = new Student("小明", 38);
Student s3 = new Student("小黑", 20);
Student s4 = new Student("小黑", 20);//相同属性对象s3 s4
treeMap.put(s2,"重庆");
treeMap.put(s1,"北京");
treeMap.put(s3,"上海");
treeMap.put(s4,"无锡");//添加相同键值对象
//遍历
for (Map.Entry<Student,String> ii:treeMap.entrySet()){
Student key = ii.getKey();
String value = ii.getValue();
System.out.println(key+"居住在:"+value);
}
}
}
---*---
输出结果:
学生{
姓名='张磊', 年龄=15}居住在:北京
学生{
姓名='小明', 年龄=38}居住在:重庆
学生{
姓名='小黑', 年龄=20}居住在:无锡
TreeMap添加学生对象和住址(按学生年龄排序)
public class Demo {
public static void main(String[] args) {
TreeMap<Student,String> treeMap = new TreeMap<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int num = o1.age-o2.age;//学生年龄升序
int num2 = num==0?o1.name.compareTo(o2.name):num;//年龄相同按字符排列
return num2;
}
});
Student s1 = new Student("张磊", 15);
Student s2 = new Student("小明", 38);
Student s3 = new Student("小黑", 20);
Student s4 = new Student("小黑", 20);//相同属性对象s3 s4
treeMap.put(s2,"重庆");
treeMap.put(s1,"北京");
treeMap.put(s3,"上海");
treeMap.put(s4,"江津");//添加相同键值对象
//遍历
for (Map.Entry<Student,String> ii:treeMap.entrySet()){
Student key = ii.getKey();
String value = ii.getValue();
System.out.println(key+"居住在:"+value);
}
}
}
---*---
输出结果:
学生{
姓名='张磊', 年龄=15}居住在:北京
学生{
姓名='小黑', 年龄=20}居住在:江津
学生{
姓名='小明', 年龄=38}居住在:重庆
列表集合存储HashMap元素遍历
public class Demo {
public static void main(String[] args) {
ArrayList<HashMap<String,String>> aa = new ArrayList<>();
HashMap<String,String> hh1 = new HashMap<>();
HashMap<String,String> hh2 = new HashMap<>();
HashMap<String,String> hh3 = new HashMap<>();
hh1.put("张无忌","赵敏");
hh1.put("林心如","霍建华");
hh2.put("赵丽颖","冯绍峰");
hh3.put("刘欣","邵峰");
aa.add(hh1);
aa.add(hh2);
aa.add(hh3);
//遍历
for (HashMap<String,String> hh: aa){
for (Map.Entry<String,String> mm: hh.entrySet()){
System.out.println(mm.getKey()+","+mm.getValue());
}
}
}
}
---*---
输出结果:
林心如,霍建华
张无忌,赵敏
赵丽颖,冯绍峰
刘欣,邵峰
将一个对象放入集合中时,由于集合并不知道我们要放什么类型,所以统统由 Object 类型变量接收。当我们在次使用对象的时候,对象变为Object类型,我们必须自己转换其类型。为了解决这个问题,则提出泛型。
泛型本质是参数化类型,将数据类型作为参数传递 。可用于类、接口和方法上。
格式:
:T视为一个数据类型占位符,表示一种引用类型。(可以定义多个泛型,用逗号隔开)
使用泛型的好处:
1.提供编译时类型检测
2.避免了强制类型转换
3.提高了代码的灵活性
泛型类格式:
格式:修饰符 class 类名 < 类型 > { }
范例:public class Generic < T > { }
//泛型类
class Student <T> {
T age;
public T getAge() {
return age;
}
public void setAge(T age) {
this.age = age;
}
}
//测试类
public class Demo {
public static void main(String[] args) {
//传入Integer类型
Student<Integer> stu1 = new Student<>();
stu1.setAge(5);
System.out.println("传入Integer类型:"+stu1.getAge());
//传入String类型
Student<String> stu2 = new Student<>();
stu2.setAge("8");
System.out.println("传入String类型:"+stu2.getAge());
}
}
---*---
输出结果:
传入Integer类型:5
传入String类型:8
注意:当泛型类被实例化或者被继承时,如果没有传入确定的类型,则该泛型将被当做Object类型看待。
泛型接口格式:
格式:修饰符 interface 接口名 < 类型 > { }
范例:public interface Generic < T > { }
//泛型接口
interface Genercic <T> {
void show(T t);
}
//实现类
class Genericimpl<T> implements Genercic<T>{
@Override
public void show(T t) {
System.out.println(t);
}
}
//测试类
public class Demo {
public static void main(String[] args) {
Genericimpl<String> s = new Genericimpl<>();
s1.show("苹果");
Genericimpl<Integer> i = new Genericimpl<>();
s2.show(50);
}
}
---*----
输出结果:
苹果
50
注意:泛型接口的实现类应该是一个泛型类,注意该类的格式。它定义了一个和接口一样的泛型。
泛型方法格式:
格式:修饰符 < 类型 > 返回值类型 方法名(类型 变量名) { }
范例:public < T > void show(T t) { }
//测试类
public class Demo {
//泛型方法
public static <T> T show(T t){
System.out.println(t);
return t;
}
//main方法
public static void main(String[] args) {
Demo.show("青岛");
Demo.show(100);
Demo.show(12.5);
Demo.show(true);
}
}
---*---
输出结果:
青岛
100
12.5
true
很明显,泛型方法可以用更少的代码实现方法的重载。方法传入类型的时候才确定泛型的类型,也就是说传入什么类型就是什么类型。
方法 | 描述 |
---|---|
将制定的列表按升序排序 | |
< T > void sort(List list, Comparator super T> c) | 根据指定比较器引发的顺序对指定列表进行排序 |
void reverse(List>list) | 翻转制定列表中元素的顺序 |
void shuffle(List> list) | 使用默认的随机源随机排列指定的列表 |
< T > void fill(List super T> list, T obj) | 用指定的元素替换指定列表的所有元素 |
Collections更多方法
练习题