集合
单列集合 Collection (接口)
双列集合 Map (接口)
集合:集合是java中提供的一种容器,可以用来存储多个数据。
集合和数组既然都是容器,它们有什么区别呢?
- 数组的长度是固定的!可以存放任意类型的数据(基本类型+引用类型),只不过,我们在开发的时候经常将同一类型的数据放入一个数组!【它的操作非常麻烦的,没有方法处理】
Object[] array = new Object[5];
array[0] = true;
array[1] = 100;
array[2] = “Niubility”; - 集合的长度是可变的!也可以存放任意类型的数据(基本类型的数据使用包装类)!实际开发,集合里面一般也是放相同类型的数据!【它的操作非常方便的,提供了很多便捷的方法】
ArrayList list = new ArrayList();
list.add(true);
list.add(100);
list.add(“Niubility”);
一、 Collection集合
1、Collection共有属性
1.1单列集合体系结构
1.2 Collection 常用功能
Collection是所有单列集合的父接口,因此在Collection中定义了单列集合(List和Set)通用的一些方法,这些方法可用于操作所有的单列集合。
public boolean add(E e): // 把给定的对象添加到当前集合中 。【E如果指定了具体类型,那么就只能向集合中添加指定类型的数据,没有指定具体类型,就相当于Object】
public void clear(): // 清空集合中所有的元素。
public boolean remove(E e): // 把给定的对象在当前集合中删除。
public boolean contains(Object obj): // 判断当前集合中是否包含给定的对象。
public boolean isEmpty(): // 判断当前集合是否为空。
public int size(): // 返回集合中元素的个数。
public Object[] toArray(): // 把集合中的元素,存储到数组中
1.3 Iterator迭代器
Iterator接口
在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK专门提供了一个接口java.util.Iterator。
想要遍历Collection集合,那么就要获取该集合对应的迭代器来完成迭代操作,下面介绍一下获取迭代器的方法:
// Iterator接口里面的方法都需要使用对象来调用!所有的单列集合都有iterator()方法(就可以得到Iterator接口的实例对象)
public Iterator iterator(): // 获取集合对应的迭代器,用来遍历集合中的元素的
**迭代:**即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出来,一直把集合中的所有元素全部取出。
Iterator接口的常用方法
public boolean hasNext(): // 如果仍有元素可以迭代,则返回 true。
public E next(): // 返回迭代的下一个元素。
代码演示:
/*
迭代器Iterator: 获取集合中所有的数据
在获取数据之前,先判断集合里面是否有数据,有就取出来,然后继续判断继续取,一直到判断集合里面没有数据的时候就不取了。
使用迭代器:
1. 通过集合对象获得迭代器对象
2. 调用迭代器的hasNext()方法判断集合中是否有数据
3. 只要有数据,那么就调用next()方法获取集合中的数据!
*/
public class Test01 {
public static void main(String[] args) {
// 创建集合对象
Collection coll = new ArrayList(); // 多态
// 添加数据到集合中去
coll.add("itheima");
coll.add("itcast");
coll.add("javaee");
// 通过集合对象获得迭代器对象
Iterator iterator = coll.iterator(); // 将集合的数据封装到迭代器中去了
// 判断
while(iterator.hasNext()){
// 获取下一个数据
Object obj = iterator.next();
System.out.println("obj:"+ obj);
}
}
}
注意1:在进行集合元素获取时,如果集合中已经没有元素了,还继续使用迭代器的next方法,将会抛出java.util.NoSuchElementException没有集合元素异常
public class Test01 {
public static void main(String[] args) {
// 创建集合对象
Collection coll = new ArrayList(); // 多态
// 添加数据到集合中去
coll.add("itheima");
coll.add("itcast");
coll.add("javaee");
// 通过集合对象获得迭代器对象
Iterator iterator = coll.iterator(); // 将集合的数据封装到迭代器中去了
// 判断
while(coll.hasNext()){
// 获取下一个数据
Object obj = iterator.next();
System.out.println("obj:"+ obj);
}
System.out.println(iterator.next()); // 迭代器中的元素数据已经取完了,还在获取,就会出现这个没有此元素异常!
}
}
注意2:在进行集合元素获取时,如果添加或移除集合中的元素 , 将无法继续迭代 , 将会抛出ConcurrentModificationException并发修改异常.
public class Test02 {
public static void main(String[] args) {
// 创建集合对象
Collection coll = new ArrayList(); // 多态
// 添加数据到集合中去
coll.add("itheima");
coll.add("itcast");
coll.add("javaee");
// 通过集合对象获得迭代器对象
Iterator iterator = coll.iterator(); // 将集合的数据封装到迭代器中去了
// 此时集合和迭代器是2个内容(里面的数据是一样的!)
while(iterator.hasNext()){
// 获取下一个数据
Object obj = iterator.next();
System.out.println("obj:"+ obj);
// 修改集合的数据(增删改)
coll.add("完犊子了。。。"); // ConcurrentModificationException:并发修改异常!
}
}
}
1.4 迭代器的实现原理
当遍历集合时,首先通过调用集合的iterator()方法获得迭代器对象,然后使用hashNext()方法判断集合中是否存在下一个元素,如果存在,则调用next()方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。Iterator迭代器对象在遍历集合时,内部采用指针的方式来跟踪集合中的元素。在调用Iterator的next方法之前,迭代器的索引位于第一个元素之前,不指向任何元素,当第一次调用迭代器的next方法后,迭代器的索引会向后移动一位,指向第一个元素并将该元素返回,当再次调用next方法时,迭代器的索引会指向第二个元素并将该元素返回,依此类推,直到hasNext方法返回false,表示到达了集合的末尾,终止对元素的遍历。
迭代器不允许在迭代数据的过程中对集合中的数据进行修改操作,但是之前的普通循环遍历是可以的!
下列说法错误的是:( B )
A: 迭代器是用来迭代集合容器的;
B: 一个迭代器可以对一个集合迭代多次;
C: 迭代器只能从头到尾迭代,不能倒着迭代;
D: 迭代器迭代集合时,不能使用迭代器之外的方法修改集合的长度
1.5 增强for
增强for循环(也称for each循环)是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删改操作。
* 普通for格式:
// 在遍历集合的过程中,可以对集合中的元素数据进行修改(增删改)操作!
for(int i=0;i<list.size();i++){
// 获得单个对象的数据
Object obj = list.get(i);
}
* 增强for格式:
// 在遍历集合的过程中,不能对集合中的元素数据进行修改(增删改)操作!【底层是迭代器】
for(数据类型 变量名称:集合对象或者数组){
}
* 注意:
① 增强for循环必须有被遍历的目标,目标只能是Collection或者是数组;
② 增强for(迭代器)仅仅作为遍历操作出现,不能对集合进行增删改元素操作,否则抛出ConcurrentModificationException并发修改异常
public class Test03 {
public static void main(String[] args) {
// 创建集合对象
Collection coll = new ArrayList(); // 多态
// 添加数据到集合中去
coll.add("itheima");
coll.add("itcast");
coll.add("javaee");
// 普通遍历
/*
for (int i = 0; i < coll.size(); i++) {
// 需要调用子类独有的方法,向下转型
ArrayList list = (ArrayList) coll;
// 得到单个对象
Object o = list.get(i);
System.out.println("o:" + o);
list.remove(o); // 可以的!
}
*/
// 增强for循环 【快捷键:被遍历集合对象.iter+回车】
for (Object obj : coll) { // 【集合在创建的时候没有泛型类型,默认是Object类型,那么遍历集合coll得到的单个对象的数据类型就是Object】
System.out.println("obj:" + obj);
// 删除数据
// coll.remove(obj); // 并发修改异常ConcurrentModificationException
}
}
}
1.6 Collections常用功能
java.utils.Collections是集合工具类,用来对集合进行操作。常用方法如下:
public static void shuffle(List<?> list) : // 打乱集合顺序。
public static void sort(List list) : // 将集合中元素按照默认规则排序。
public static void sort(List list,Comparator<? super T> ) : // 将集合中元素按照指定规则排序。
static boolean addAll(Collection<? super T> c, T… elements): // 将所有指定的元素添加到指定的集合。
1.7Comparator比较器
指定排序的规则的一个接口(java.util包下),重写该接口下的一个方法compare(T o1, T o2)即可!
public class Test02 {
public static void main(String[] args) {
// 创建集合对象
ArrayList<Integer> list1 = new ArrayList<>();
list1.add(10);
list1.add(60);
list1.add(20);
list1.add(80);
list1.add(30);
System.out.println("list1:" + list1); // list1:[10, 60, 20, 80, 30]
// 排序(默认排序,自然排序)
Collections.sort(list1);
System.out.println("list1:" + list1); // list1:[10, 20, 30, 60, 80]
// 升序排序!
Collections.sort(list1, new Comparator<Integer>() {
// 此方法是排序的核心
@Override
public int compare(Integer o1, Integer o2) {
// 前-后 升序! 后-前 降序!(前后针对的是2个参数o)
return o1-o2;
}
});
System.out.println("list1:" + list1); // list1:[10, 20, 30, 60, 80]
// 降序排序!
Collections.sort(list1, new Comparator<Integer>() {
// 此方法是排序的核心
@Override
public int compare(Integer o1, Integer o2) {
// 前-后 升序! 后-前 降序!(前后针对的是2个参数o)
return o2-o1;
}
});
System.out.println("list1:" + list1); // list1:[80, 60, 30, 20, 10]
}
}
public class Test03 {
public static void main(String[] args) {
// 创建集合
ArrayList<String> list2 = new ArrayList<>();
list2.add("abc");
list2.add("bbd");
list2.add("cbd");
list2.add("bac");
System.out.println("list2:"+list2); // list2:[abc, bbd, cbd, bac]
// 默认排序
Collections.sort(list2);
System.out.println("list2:"+list2); // list2:[abc, bac, bbd, cbd]
// 自己指定规则(升序!)
Collections.sort(list2, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// 前-后:升序; 后-前:降序!
return o1.compareTo(o2);
}
});
System.out.println("list2:"+list2); // list2:[abc, bac, bbd, cbd]
// 降序!
Collections.sort(list2, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// 后-前:降序!
return o2.compareTo(o1);
}
});
System.out.println("list2:"+list2); // list2:[abc, bac, bbd, cbd]
}
}
比较器应用在 List排序(Collections.sort(List list,new Comparator)) 和 TreeSet的构造方法中
1.3 可变参数
在JDK1.5之后,如果我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以对其简化
* 格式:
修饰符 返回值类型 方法名(数据类型... 形参名){ }
/*
Collections工具类通过了对集合进行便捷操作的静态方法:
static <T> boolean addAll(Collection<? super T> c, T... elements): // 将所有指定的元素添加到指定的集合。
T...elements:它是一个可变参数!
1. 可变参数的类型是集合的泛型类型
2. 参数可以传递任意个(0个也行)
3. 方法的参数,里面除了有可变参数,还有额外参数(这些个参数的类型全都一样,那么额外的参数必须放在可变参数的前面)
*/
public class Test04 {
public static void main(String[] args) {
// 创建集合对象
ArrayList<Integer> list1 = new ArrayList<>();
list1.add(10);
list1.add(60);
list1.add(20);
list1.add(80);
list1.add(30);
System.out.println("list1:" + list1); // list1:[10, 60, 20, 80, 30]
// 使用工具类的addAll方法向集合中添加数据
ArrayList<Integer> list2 = new ArrayList<>();
Collections.addAll(list2, 10, 15, 20, 3, 9);
//Collections.addAll(list2); // 正确的!
System.out.println("list2:" + list2); // list2:[10, 15, 20, 3, 9]
}
}
需求:定义方法,求N个整数的累加和
/*
可变参数:
定义方法,求N个整数的累加和
*/
public class Test05 {
public static void main(String[] args) {
// 调用方法
int sum1 = getSum(1, 2, 3);
System.out.println("sum1:" + sum1);
// 再次调用方法
int sum2 = getSum(3, 6, 2, 10, 9);
System.out.println("sum2:"+ sum2);
}
// 定义方法:求N个整数的累加和
public static int getSum(int... number) { // 将可变参数可以理解为一个可变数组或者集合
// 定义求和变量
int sum = 0;
if (number.length != 0) {
// 遍历
for (int i : number) {
// 累加求和
sum += i;
}
}
// 返回
return sum;
}
}
* 注意:
1.一个方法只能有一个可变参数
2.如果方法中有多个参数,可变参数要放到最后。
public class Test06 {
public static void main(String[] args) {
// 调用方法
int sum1 = getSum(0, 2, 3); // // 第一个参数为为计数器变量赋默认值!
System.out.println("sum1:" + sum1);
// 再次调用方法
int sum2 = getSum(0, 6, 2, 10, 9); // 第一个参数为为计数器变量赋默认值!
System.out.println("sum2:"+ sum2);
}
// 定义方法:求N个整数的累加和
public static int getSum(int count,int... number) { // 将可变参数可以理解为一个可变数组或者集合
//public static int getSum(int... number,int count) { // 可变参数与额外参数的数据类型一致,必须将可变参数放在后面!
// 定义求和变量
int sum = 0;
if (number.length != 0) {
// 遍历
for (int i : number) {
// 累加求和
sum += i;
count++;
}
}
System.out.println("当前有"+count+"个数据在相加!");
// 返回
return sum;
}
}
应用场景: Collections
在Collections中也提供了添加一些元素方法:
public static <T> boolean addAll(Collection<T> c, T... elements) : // 往集合中添加一些元
public static void main(String[] args) {
// 创建集合对象
ArrayList<Integer> list1 = new ArrayList<>();
list1.add(10);
list1.add(60);
list1.add(20);
list1.add(80);
list1.add(30);
System.out.println("list1:" + list1); // list1:[10, 60, 20, 80, 30]
// 使用工具类的addAll方法向集合中添加数据
ArrayList<Integer> list2 = new ArrayList<>();
Collections.addAll(list2, 10, 15, 20, 3, 9);
//Collections.addAll(list2); // 正确的! // list2:[]
System.out.println("list2:" + list2); // list2:[10, 15, 20, 3, 9]
}
2、List接口
2.1 List接口介绍
java.util.List接口继承自Collection接口,是单列集合的一个重要分支,习惯性地会将实现了List接口的对象称为List集合。
* 特点:有序、有索引、可重复
①它是一个元素存取有序的集合。 【向集合中存入数据是什么数据,那么取出来的数据就是之前存入数据的顺序】 abc abc
②它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素。
③集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。
2.2 List接口中的常用方法
List作为Collection集合的子接口,不但继承了Collection接口中的全部方法,而且还增加了一些根据元素索引来操作集合的特有方法。
public void add(int index, E element): // 将指定的元素,添加到该集合中的指定位置上。
public E get(int index): // 返回集合中指定位置的元素。
public E remove(int index): // 移除列表中指定位置的元素, 返回的是被移除的元素。
public E set(int index, E element):// 用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
2.3 ArrayList集合
java.util.ArrayList集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以ArrayList是最常用的集合之一。
public boolean add(E e); // 直接在集合的末尾添加元素数据e
public void add(int index,E e); // 在集合指定index索引位置添加元素数据e
public E get(int index); // 获得集合总指定index索引位置的元素数据
public E remove(int index); // 删除集合中指定index索引位置的元素数据
public boolean remove(Object obj); // 删除集合中指定的元素数据obj
public E set(int index,E e); // 将集合index索引位置的元素数据修改为e
public int size(); // 获得集合里面元素数据的个数!
2.4 LinkedList集合
java.util.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 E pop(): // 从此列表所表示的堆栈处弹出一个元素。
public void push(E e): // 将元素推入此列表所表示的堆栈。
public boolean isEmpty():// 如果列表不包含元素,则返回true。
public class Test02LinkedList {
public static void main(String[] args) {
// 创建对象
LinkedList<String> ll = new LinkedList<>();
// 造数据
ll.add("itcast");
ll.add("itheima");
ll.add("java");
ll.add("java");
System.out.println("ll: " + ll); // ll:[itcast, itheima, java, java] 【LinkedList具备List接口的特点!】
// 删除(链表的头部)
String pop = ll.pop();
System.out.println("pop:" + pop); // pop:itcast
// 添加(链表的头部)
ll.push("jack");
System.out.println("ll: " + ll); // ll: [jack, itheima, java, java]
// 是否为空
System.out.println(ll.isEmpty()); // false
// 删除尾部
ll.removeLast();
System.out.println("ll: " + ll ); // ll: [jack, itheima, java]
// 删除头部
ll.removeFirst();
System.out.println("ll: " + ll ); // ll: [itheima, java]
// 获得头部
System.out.println(ll.getFirst()); // itheima
// 获得尾部
System.out.println(ll.getLast()); // java
// 头部添加
ll.addFirst("jack");
System.out.println("ll: " + ll ); // ll: [jack, itheima, java]
// 尾部添加
ll.addLast("rose");
System.out.println("ll: " + ll ); // ll: [jack, itheima, java, rose]
3、Map接口
3.1 Map接口介绍
现实生活中,我们常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射。Java提供了专门的集合类用来存放这种对象关系的对象,即java.util.Map接口。
我们通过查看Map接口描述,发现Map接口下的集合与Collection接口下的集合,它们存储数据的形式不同,如下图。
* Collection中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。
* Map中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。
* Collection中的集合称为单列集合,Map中的集合称为双列集合。
* 需要注意的是,Map中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。【重复key,其值会被覆盖!】
3.2 Map的常用子类
通过查看Map接口描述,看到Map有多个子类,这里我们主要讲解常用的HashMap集合、LinkedHashMap集合、TreeMap集合。
- HashMap<K,V>:存储数据采用的哈希表(数组、链表、红黑树)结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
- LinkedHashMap<K,V>:HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
- TreeMap<K,V>:TreeMap集合和Map相比没有特有的功能,底层的数据结构是红黑树;可以对元素的键进行排序,排序方式有两种:自然排序和比较器排序
3.3 Map的常用方法
Map接口中定义了很多方法,常用的如下:
public V put(K key, V value) : // 把指定的键与指定的值添加到Map集合中。
public V remove(Object key) : // 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
public V get(Object key): // 根据指定的键,在Map集合中获取对应的值。
public Set<K> keySet() : // 获取Map集合中所有的键,存储到Set集合中。
public Set<Map.Entry<K,V>> entrySet() : // 获取到Map集合中所有的键值对对象的集合(Set集合)。
public boolean containKey(Object key) : // 判断该集合中是否有此键。
/*
HashMap:
无序!唯一(key)!
*/
public class Test01Map {
public static void main(String[] args) {
// 创建Map集合对象
Map<String, String> map = new HashMap<>();
// 向map集合中添加数据
map.put("username", "jack");
map.put("password", "jack");
map.put("sex", "男");
map.put("age", "38");
map.put("address", "天津滨海新区互联网教育大厦203");
// 打印
System.out.println("map:" + map); // map:{password=123456, address=天津滨海新区互联网教育大厦203, sex=男, age=38, username=jack}
// 再次添加
map.put("age", "28"); // 重复的key再次赋值,修改数据!
System.out.println("map:" + map); // map:{password=123456, address=天津滨海新区互联网教育大厦203, sex=男, age=28, username=jack}
// 获取
String address = map.get("address");
System.out.println("address:" + address); // address:天津滨海新区互联网教育大厦203
// 删除(返回被删除的那个key对应的值)
String password = map.remove("password");
System.out.println("password:" + password); // password:jack
System.out.println("map:" + map); // map:{address=天津滨海新区互联网教育大厦203, sex=男, age=28, username=jack}
// 判断map中是否包含指定的key
System.out.println(map.containsKey("sex")); // true
System.out.println(map.containsKey("email")); // false
}
}
注意:指定的键(key)在集合中存在,则返回值为集合中键对应的值(该值为替换前的值),并把指定键所对应的值,替换成指定的新值。
tips:Map接口中的集合都有两个泛型变量<K,V>,在使用时,要为两个泛型变量赋予数据类型。两个泛型变量<K,V>的数据类型可以相同,也可以不同。
// 创建Map集合对象
Map<String, String> map = new HashMap<>();
// 向map集合中添加数据
map.put("username", "jack");
map.put("password", "123456");
map.put("username", "rose"); // 此处会将username的进行修改,用rose替换jack
Map<String, String> map = new HashMap<>(); // key,value的泛型类型相同!
Map<Integer,Character> map = new HashMap<>(); // key,value的泛型类型不同!
3.4 Map的遍历 【重点】
方式1: 键找值方式
/*
Map的遍历
方式一:先获得所有的key(构成一个Set集合),然后通过key获得对应的value
* keySet()方法:获得map中所有的key构成的Set集合
* 遍历所有的key,然后通过map的getKey(Object obj)方法获得指定key的value值!
*/
public class Test02Map {
public static void main(String[] args) {
// 创建Map集合对象
Map<String, String> map = new HashMap<>();
// 向map集合中添加数据
map.put("username", "jack");
map.put("password", "123456");
map.put("sex", "男");
map.put("age", "38");
map.put("address", "天津滨海新区互联网教育大厦203");
// 获得map中所有的key
Set<String> keys = map.keySet();
for (String key : keys) {
//System.out.println("key:"+key);
// 通过key获得value
String value = map.get(key);
System.out.println("key="+ key+", value="+value);
}
}
}
方式2: 键值对方式
/*
Map的遍历
方式二:先获得所有的key(构成一个Set集合),然后通过key获得对应的value
* 通过map的engtrySet()方法获得map中所有(key,value)键值对(entry实体)构成的Set集合
* 遍历set集合,获得单个的(key,value)键值对(entry实体)
* 直接通过getKey获得键(key)的值!通过getValue获得值(value)的值!
*/
public class Test03Map {
public static void main(String[] args) {
// 创建Map集合对象
Map<String, String> map = new HashMap<>();
// 向map集合中添加数据
map.put("username", "jack");
map.put("password", "123456");
map.put("sex", "男");
map.put("age","38");
map.put("address", "天津滨海新区互联网教育大厦203");
// 通过map的engtrySet()方法获得map中所有(key,value)键值对(entry实体)构成的Set集合
Set<Map.Entry<String, String>> entrySet = map.entrySet();
// 遍历set集合,获得单个的(key,value)键值对(entry实体)
for (Map.Entry<String, String> entry : entrySet) {
// 直接通过getKey获得键(key)的值!通过getValue获得值(value)的值!
System.out.println("key="+entry.getKey()+", value="+entry.getValue());
}
}
}
上面2种方式对应的图解如下:
注意:Map集合不能直接使用迭代器或者foreach进行遍历。但是转成Set之后就可以使用了。
3.5 HashMap存储自定义类型
练习:每位学生(姓名,年龄)都有自己的家庭住址。那么,既然有对应关系,则将学生对象和家庭住址存储到map集合中。学生作为键, 家庭住址作为值。
注意:学生姓名相同并且年龄相同视为同一名学生。
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public void show(){
System.out.println(this.name + ", " + this.age);
}
@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);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
/*
每位学生(姓名,年龄)都有自己的家庭住址。那么,既然有对应关系,则将学生对象和家庭住址存储到map集合中。学生作为键, 家庭住址作为值。
学生姓名相同并且年龄相同视为同一名学生
*/
public class StudentTest {
public static void main(String[] args) {
// 创建Map对象
Map<Student,String> map = new HashMap<>();
// 创建对个学生对象
Student s1 = new Student("jack",38);
Student s2 = new Student("jack",18);
Student s3 = new Student("jack",38); // 姓名相同并且年龄相同视为同一名学生 (map的key一样了!)
Student s4 = new Student("rose",18);
// 将多个学生对象作为key, 住址作为value,添加到map集合
map.put(s1,"北京");
map.put(s2,"南京");
map.put(s3,"天津"); // 添加不进去!(s3和s1是重复的!)
map.put(s4,"东京");
// 遍历(通过key找值的方式)
for (Student student : map.keySet()) {
// 获得key的值!
student.show();
// 获得value的值
String address = map.get(student);
System.out.println(address);
}
// 键值对方式的遍历!
/*for (Map.Entry<Student, String> entry : map.entrySet()) {
}*/
}
}
* 当给HashMap中存放自定义对象时,如果自定义对象作为key存在,这时要保证对象唯一,必须复写对象的hashCode和equals方法(如果忘记,请回顾HashSet存放自定义对象)。
* 如果要保证map中存放的key和取出的顺序一致,可以使用java.util.LinkedHashMap集合来存放。
3.6 LinkedHashMap介绍
在HashMap下面有一个子类LinkedHashMap,它是链表和哈希表(数组+链表+红黑树)
组合的一个数据存储结构。
/*
HashMap:无序!
LinkedHashMap:有序!
*/
public class Test01 {
public static void main(String[] args) {
// 创建HashMap集合对象
Map<String, String> map = new HashMap<>();
// 向map集合中添加数据
map.put("username", "jack");
map.put("password", "123456");
map.put("sex", "男");
map.put("age", "38");
map.put("address", "天津滨海新区互联网教育大厦203");
// 打印【无序!】
System.out.println("map:" + map); // map:{password=123456, address=天津滨海新区互联网教育大厦203, sex=男, age=38, username=jack}
///
// 创建LinkedHashMap
Map<String, String> map1 = new LinkedHashMap<>();
// 向map集合中添加数据
map1.put("username", "jack");
map1.put("password", "123456");
map1.put("sex", "男");
map1.put("age", "38");
map1.put("address", "天津滨海新区互联网教育大厦203");
// 打印【无序!】
System.out.println("map1:" + map1); // map1:{username=jack, password=123456, sex=男, age=38, address=天津滨海新区互联网教育大厦203}
}
}
3.7 TreeMap集合
TreeMap集合和Map相比没有特有的功能,底层的数据结构是红黑树;可以对元素的键进行排序,排序方式有两种:自然排序和比较器排序;到时使用的是哪种排序,取决于我们在创建对象的时候所使用的构造方法;
public TreeMap() // 使用自然排序
public TreeMap(Comparator<? super K> comparator) // 比较器排
自然排序:
/*
HashMap: 无序!唯一(key)!
LinkedHashMap: 有序!唯一(key)!
TreeMap: 可排序(针对key)!唯一(唯一)!
*/
public class StudentDemo {
public static void main(String[] args) {
// LinkedHashMap
Map<String,Student> map1 = new LinkedHashMap<>();
Student s1 = new Student("jack",38);
Student s2 = new Student("rose",18);
Student s3 = new Student("rose",18);
Student s4 = new Student("jack",18);
map1.put("bbc",s1);
map1.put("bac",s2);
map1.put("cbd",s3);
map1.put("cba",s4);
// 遍历
for (String s : map1.keySet()) {
System.out.println(s); // 存的是什么顺序,取的就是什么顺序!(bbc bac cbd cba)
}
System.out.println("======================================");
//
// TreeMap(会排序的!默认升序!)
Map<String,Student> map2 = new TreeMap<>();
map2.put("bbc",s1);
map2.put("bac",s2);
map2.put("cbd",s3);
map2.put("cba",s4);
// 遍历
for (String s : map2.keySet()) {
System.out.println(s); // 存的是什么顺序,取的就是什么顺序!(bac bbc cba cbd)
}
System.out.println("=======================================");
}
}
比较器排序:
/*
HashMap: 无序!唯一(key)!
LinkedHashMap: 有序!唯一(key)!
TreeMap: 可排序(针对key)!唯一(唯一)!
*/
public class StudentDemo {
public static void main(String[] args) {
// LinkedHashMap
Map<String,Student> map1 = new LinkedHashMap<>();
Student s1 = new Student("jack",38);
Student s2 = new Student("rose",18);
Student s3 = new Student("rose",18);
Student s4 = new Student("jack",18);
map1.put("bbc",s1);
map1.put("bac",s2);
map1.put("cbd",s3);
map1.put("cba",s4);
// 遍历
for (String s : map1.keySet()) {
System.out.println(s); // 存的是什么顺序,取的就是什么顺序!(bbc bac cbd cba)
}
System.out.println("======================================");
//
// TreeMap(会排序的!降序!)
Map<String,Student> map3 = new TreeMap<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.charAt(0)-o1.charAt(0);
}
});
map3.put("abc",s1);
map3.put("bac",s2);
map3.put("cbd",s3);
map3.put("dba",s4);
// 遍历
for (String s : map3.keySet()) {
System.out.println(s); // 存的是什么顺序,取的就是什么顺序!(dba cbd bac abc)
}
}
}