目录
10.5 Map 集合
10.1 集合类概述
java.util 包中提供了一些集合类,这些集合类又被称为容器。提到容器不难想到数组,集合类与数组的不同之处是,数组的长度是固定的,集合的长度是可变的;数组用来存放基本类型的数据,集合用来存放对象的引用。常用的集合有List集合、Set集合和Map集合、其中List与Setj继承了Collection接口,各接口提供了不同的实现类。
常用集合类的继承
总结:(所有的实现类):
ArrayList:底层是数组。
LinkedList:底层是双向链表。
Vector:底层是数组,线程安全的,效率较低,使用较少。
HashSet:底层是HashMap,放到HashSet集合中的元素等同于放到HashMap集合key部分了。
TreeSet:底层是TreeMap,放到TreeSet集合中的元素等同于放到TreeMap集合key部分了。
HashMap:底层是哈希表。
Hashtable:底层也是哈希表,只不过线程安全的,效率较低,使用较少。
Properties:是线程安全的,并且key和value只能存储字符串String。
TreeMap:底层是二叉树。TreeMap集合的key可以自动按照大小顺序排序。
List集合存储元素的特点:
有序可重复
有序:存进去的顺序和取出的顺序相同,每一个元素都有下标。(先放zhangsan,就先取出zhangsan)
可重复:存进去1,可以再存储一个1.
Set(Map)集合存储元素的特点:
无序不可重复
无序:存进去的顺序和取出的顺序不一定相同。另外Set集合中元素没有下标。
不可重复:存进去1,不能再存储1了。
SortedSet(SortedMap)集合存储元素特点:
首先是无序不可重复的,但是SortedSet集合中的元素是可排序的。
无序:存进去的顺序和取出的顺序不一定相同。另外Set集合中元素没有下标。
不可重复:存进去1,不能再存储1了。
可排序:可以按照大小顺序排列。
Map集合的key,就是一个Set集合。
往Set集合中放数据,实际上放到了Map集合的key部分。
课堂笔记:Collection
List :有序
ArrayList 适用于查询较多的列表
List<E> list = new ArrayList<>( ) ;
LinkedLIst 适用于新增、删除较多的列表
List<E> list = new LinkedList<>)( ) ;
Set : 无序
10.2 Collection 接口
常用
/**
* 1.集合的理解???
* 集合的泵本质就是一个容器,用于存放数据的容器
* 2.集合的特点???
* 在java语言中,有很多种集合,不同集合对应的“数据结构”不一样,则意味着“增、删、改和查”的效率不一样。
* 3.集合的继承体系???
* Collection接口
* --List接口
* -- ArrayList实现类
* --LinkedList实现类
* --Vector实现类
* --Set接口
* --HashSet实现类
* --TreeSet实现类
* --LinkedHashSet实现类
* Map接口
* --HashMap实现类
* --TreeMap实现类
* --LinkedHashMap实现类
* --Hashtable实现类
* --Properties实现类
* 4.Collection接口的方法
* Collection接口的子接口有:List接口和Set接口,也就意味着List接口的实现类和Set接口的实现类都可以使用Collection
* int size(); 容器中元素的数量。
* boolean isEmpty(); 容器是否为空
* boolean add(Object o); 增加元素到容器中。
* boolean remove();从容器中移除元素。
* -->根据指定元素只来删除集合中的元素,删除成功返回true,删除失败返回false
* boolean contains(Object o); 容器中是否包含该元素
* --> 容器中是否包含该元素,如果包含该元素则返回true,否则返回false
* boolean containAll(Collection c);
* -->本容器是否包含c容器所有元素,如果都包含则返回true,否则返回false
* Iterator iterator(); 获得迭代器,用于遍历所有元素。
* -->
* Object[] toArray(); 把容器中元素转化成Object数组
* ---------------------------------------------------------------------------------------不常用
* bollean addAll(Collection c); 将容器c中所有元素增加到本容器。
* boolean containAll(Collection c); 本容器是否包含c容器所有元素
* -->本容器是否包含c容器所有元素,如果都包含则返回true,否则返回false
* boolean removeAll(Collection c); 移除本容器和容器c中都包含的元素。
* -->删除本容器和容器c中都包含的元素,删除成功返回true,删除失败返回false
* boolean retailAll(Collecttion c); 取本容器和容器c中都包含的元素,移除非交际元素
* -->只保留本容器和容器c中都包含的元素
* @author 86155
*
*/
方法 | 功能描述 |
add(Object e) | 将指定的对象添加到该集合中 |
remove(Object o) | 将指定的对象从该集合中移除 |
isEmpty() | 返回 boolean 值,用于判断当前集合是否为空 |
iserator() | 返回在此Collection 的元素上进行迭代的迭代器。用于遍历集合中的对象 |
size() | 返回 int 型值,获取该集合中的个数 |
10.3 List 集合
List集合介绍: java.util.List接口继承了Java.util.Collection接口,因此List接口的实现类都实现了Collection接口的方法,这样List接口的实现类的对象都可以调用来自于Collection接口的方法。
import java.util.ArrayList;
import java.util.List;
public class ListTest {
@Test
public void testListFeature(){
List<String> cities=new ArrayList<>();
}
}
10.3.1 List 接口
方法 | 功能描述 |
get (int index) | 获得指定索引位置的元素 |
set (int index, Object obj) | 将集合中指定索引位置的对象修改为指定的对象 |
10.3.2 List 接口的实现类
package chy;
import java.util.*;
public class ListTest { //创建类名
public static void main(String[] args) { //主方法
List<String> list = new ArrayList<>(); //创建集合对象
list.add("a"); //向集合添加元素
list.add("b"); //向集合添加元素
list.add("c"); //向集合添加元素
int i = (int) (Math.random()*list.size()); //从0 — 2之间的随机数
System.out.println("随机获取数组中的元素: "+list.get(i));
list.remove(2); //将指定索引位置的元素从集合中移除
System.out.println("将索引是'2' 的元素从数组移除后,数组中的元素是: ");
for(int j = 0; j < list.size(); j++) { //循环遍历数组
System.out.println(list.get(j)); //获取指定索引处的值
}
}
}
10.3.3 Iterator 迭代器
方法 | 功能描述 |
hasNext() | 如果仍有元素可以迭代,则返回true |
next() | 返回迭代的下一个元素 |
remove() | 从迭代器 |
Iterator 的next()方法返回的是 Object。
程序中使用Iterator 迭代器, 可以使用 Collection 接口中的iterator()方法返回一个Iterator对象。例题如下: 在项目中创建 IteratotTest 类, 在主方法中实例化对象, 并向集合中添加元素,最后通过 Iterator 迭代器遍历集合,将集合中的对象以 String 形式输出。
package error;
import java.util.ArrayList; //导入java.util包,其他实例都要添加该语句
import java.util.Collection;
import java.util.Iterator;
public class InteratorTest{ //创建类IteratorTest
public static void main (String args[]) { //主方法
Collection<String> list = new ArrayList<>(); //实例化集合类对象
list.add("a"); //向集合添加数据
list.add("b"); //向集合添加数据
list.add("c"); //向集合添加数据
Iterator<String> it = list.iterator(); //创建迭代器
while (it.hasNext()) {//判断是否有下一个元素
String str = (String) it.next(); //获取集合中元素
System.out.println(str); //输出信息
}
}
}
10.4 Set 集合
Set集合的特点 : 1、无索引,也就意味着没有基于索引操作集合的方法
2、唯一,元素不允许重复
Set 的构造有一个约束条件,传入的Collection 对象不能有重复值,必须小心操作可变对象(Mutable Object)。如果一个Set中可变元素改变了自身状态导致Object.equals(Object)=true,则会出现一些问题。
10.4.1 Set 接口
由于Set集合中不允许有重复元素出现,因此,在向Set集合中添加元素时,需要判断元素是否已经存在,再确定是否执行添加操作时,如下图:
10.4.2 Set 接口的实现类
set接口常用实现类 :
--HashSet实现类
--TreeSet实现类
--LinkedHashSet实现类
(不同的Set集合的实现类有不同的特点,因此有着不同的应用场景。*了解)
Set接口继承Collection接口,它没有在Collection接口之上增加额外的方法。
因此在学习Set集合重点掌握不同实现类的特性。前提是掌握了Collection 接口方法的使用。
HashSet集合的使用,HashSet实现了Set接口,HashSet的特性如下:
1、无索引,也就意味着没有基于索引操作集合的方法
2、唯一,元素不允许重复
3、存取无序,存储和取出来的顺序不一致
/**
* HashSet的特性
* 1、无索引,也就意味着没有基于索引操作集合的方法
* 2、唯一,元素不允许重复
* 3、存取无序,存储和取出来的顺序不一致
*/
@Test
public void testHashSetFeature() {
Set<String> hashSet = new HashSet<>();
hashSet.add("Java");
hashSet.add("Go");
hashSet.add("Rust");
hashSet.add("Java");
System.out.println("hashSet集合的元素的内容是"+hashSet);
/**
* 程序运行结果
* hashSet 集合元素内容是[Java, Rust , Go]
*/
}
HashSet底层的数据结构是哈希表,不同的JDK版本,哈希表的组成是不一样的。
Java8以前哈希表有数组加链表组成
Java8以后链表元素的数量如果没有超过8个,那么还是由数组加链表组成,如果链表元素的数量超过8个,哈希表由数组+链表+红黑树组成。
Set集合保持唯一性原理
Set集合保持唯一性依靠集合存储对象的两个方法: hashCode()方法和equals()方法,这两个方法是来自于Object类,因此集合的元素都有这两个方法。
hashCode()根据对象的地址计算哈希值
equals()判断对象的地址是否相等
Set集合保持唯一性的流程
1、 当Set 集合添加元素的时候,会调用该元素的hashCode()方法来计算哈希值,判断该哈希值对应的位置上是否有哈希值相同的元素,如果该位置上没有哈希值相同的元素,那么就将该元素存储到Set集合中,如果该位置上有哈希值相同的元素,那么就会产生哈希冲突。
2、如果产生了哈希冲突,然后调用元素的equals()方法上所有元素进行比较,如果比较完成后该位置上的任何一个元素和要存储的元素相等,那么就不存储,否则就存储。
在日常开发中,如果想要使用Set集合存储的自定义的类,而且想要保证数据的唯一性,那么根据业务规则必须同时重写equals()和hashCode()方法,否则即使使用了Set集合也未必能够保证数据的唯一性。
@Override
public boolean equals(Object o) {
if(this == 0) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Cellphone cellphone = (Cellphone) o;
return model.equals(cellphone.model) && price.equals(cellphone.price) && weight.equals(cellphone.weight);
}
@Override
public int hashCode() {
ruturn Object.hash(model, price, weight);
}
LinkedHashSet 继承HashSet, 有如下特性: 1、无索引,没有基于索引操作集合元素的方法
2、 唯一,集合的元素不允许重复
3、存取有序,存储和取出来的顺序一致
/**
* HashSet的特性
* 1、无索引,没有基于索引操作集合的方法
* 2、唯一,集合的元素不允许重复
* 3、存取有序,存储和取出来的顺序一致
*/
@Test
public void testLinkedHashSetFeature() {
Set<String> hashSet = new HashSet<>();
linkedSet.add("Java");
linkedSet.add("Go");
linkedSet.add("Rust");
linkedSet.add("Java");
//Java Go Rust
System.out.println("当前linkedHashSet集合的元素的内容是"+linkedHashSet);
}
一、引入排序5
package name;
import java.util.TreeSet;
public class test1 {
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();
treeSet.add(10);
treeSet.add(11);
treeSet.add(13);
treeSet.add(12);
for (Object object : treeSet) {
System.out.print(object + " " );
}
}
结论:默认进行升序排列
思考:但是对于自定义对象呢?
/*
实体类:Book
*/
public class Book {
private Integer bookId;
private String bookName;
private String bookAuthor;
private Double price;
}
/*
测试类
*/
@Test
public void test2() {
Book book1 = new Book(1, "红楼梦", "曹雪芹",11.0);
Book book2 = new Book(2, "西游记", "曹雪芹",13.0);
Book book3 = new Book(3, "水浒传", "曹雪芹",12.0);
TreeSet treeSet = new TreeSet();
treeSet.add(book1);
treeSet.add(book2);
treeSet.add(book3);
for (Object object : treeSet) {
Book book = (Book)object;
System.out.println(book.getBookName() + book.getPrice());
}
}
运行结果会出现报错,解决方案:1、自然排序 2、定制排序
二、自定义排序
自定义类型排序
如果集合存储的是自定义类型,当存入自定义的引用类型的时候就必须考虑到元素要求具有可排序性,不然会引发ClassCastException异常,所以要对自定义类型进行处理,必须要实现 Comparable或Compared接口。当把一个对象加入TreeSet集合中时,TreeSet调用该对象的compareTo(Object obj)方法与容器中的其他对象比较大小,然后根据红黑树算法决定它的存储位置。
2.1 自然排序
TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间大小关系,然后将集合元素按升序排列,这种方式就是自然排序。(比较的前提:两个对象的类型相同)。
操作步骤:
1、Book类实现Comparable接口;
2、重写Comparable接口中的compareTo方法。
/*
Book类
compareTo(To) : 比较此对象与指定对象的顺序。
特别注意在重写Compareto方法时,注意排序 !!!
*/
public class Book implements Comparable{ // 第一步:实现Comparable接口
private Integer bookId;
private String bookName;
private String bookAuthor;
private Double price;
public int compareTo(Object o) { // 第二步:重写Comparable接口中的compareTo方法。
// 若想按照价格从小到大排序,则需要return = 1 ,即已存在数据大于待存数据
Book book = (Book);
int flag = (int)(this.getPrice() - book.getPrice());
return flag;
}
}
/*
测试类
*/
@Test
public void test3() {//创建类名
Book book1 = new Book(1, "红楼梦", "曹雪芹",11.0);
Book book2 = new Book(2, "西游记", "曹雪芹",13.0);
Book book3 = new Book(3, "水浒传", "曹雪芹",12.0);
TreeSet treeSet = new TreeSet();//实例化对象
treeSet.add(book1);
treeSet.add(book2);
treeSet.add(book3);
for (Object object : treeSet) {
Book book = (Book)object;
System.out.println(book.getBookName() + book.getPrice());//输出信息
}
}
运行结果:
2.2 定制排序
TreeSet的自然排序是根据集合元素的大小,TreeSet将他们以升序排列。如果需要实现定制排序,例如降序,则可以使用Comparator接口。该接口里包含一个int compare(T o1, T o2)方法,该方法用于比较o1和o2的大小。如果需要实现定制排序,则需要在创建TreeSet集合对象时,并提供一个Comparator对象与该TreeSet集合关联,由该Comparator对象负责集合元素的排序逻辑。
操作步骤:让集合构造方法接收Comparator的实现类的compare()方法。
(插入)TreeSet集合的使用,TreeSet实现了Set接口,TreeSet的特性如下:
1、无索引,没有基于索引操作集合元素的方法
2、唯一,集合的元素不允许重复
3、有序,添加元素是按照元素对应的默认排序规则排序,元素必须实现Comparable接口的compareTo(T o)方法 即下图;这里的排序指的是升序排序
需求: 集合的元素按照字符串的字典顺序降序排序 如下:
/**
*需求: 集合的元素按照字符串的字典顺序降序排序
*/
@Test
public void testTreeSetElementStringSesc() {
Set<String> treeSet = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String str1. String str2) {
//整数的降序 后面减前面
//字符串的降序
return str2.compareTo(str1);
}
}};
treeSet.add("Java");
treeSet.add("Go");
treeSet.add("Rust");
treeSet.add("Java");
//期望结果 Rust , Java , Go
System.out.println("treeSet集合的元素按照字符串的字典降序排序"+treeSet);
}
根据两例来理解上面叙述内容
运行结果:1
/*
实体类:Book
*/
public class Book {
private Integer bookId;
private String bookName;
private String bookAuthor;
private Double price;
}
/**
测试类
*/
@Test
public void test4() {
Book book1 = new Book(1, "红楼梦", "曹雪芹",11.0);//向集合中添加数据
Book book2 = new Book(1, "西游记", "曹雪芹",3.0);//向集合中添加数据
Book book3 = new Book(1, "水浒传", "曹雪芹",12.0);//向集合中添加数据
/**
compare有两个参数,o1和o2,如果
o1 < o2 返回负数,
o1 = o2 返回正数,
o1 > o2 返回正数。
*/
TreeSet treeSet = new TreeSet(new Comparator() { // 匿名内部类操作
public int compare(Object o1, Object o2) {
Book book1 = (Book)o1;
Book book2 = (Book)o2;
return (int)( book1.getPrice() - book2.getPrice());
}
});
treeSet.add(book1);
treeSet.add(book2);
treeSet.add(book3);
for (Object object : treeSet) {
Book book = (Book)object;
System.out.println(book.getBookName() + book.getPrice());//输出信息
}
}
2
package chy;
import java.util.*; //导入java.util包,其他实例都要添加该语句
public class HashSetTest {//创建类名HashSetTest
public static void main(String[] args) {//主方法
Set set = new HashSet(); //创建Set集合
set.add("c"); //向集合中添加数据
set.add("c"); //向集合中添加数据
set.add("a"); //向集合中添加数据
set.add("b"); //向集合中添加数据
Iterator<String> it = set.iterator(); //创建迭代器
while (it.hasNext()) { //遍历HasNext集合
String str = (String) it.next(); //获取集合中的元素
System.out.println(str); //输出信息
}
}
}
10.4.2 TreeSet的两种排序方式:(结合上例了解图中内容)
方法 | 功能描述 |
first() | 返回此set中当前第一个(最低)元素 |
last() | 返回此set中当前最后一个(最高)元素 |
comparator() | 返回对此Set中的元素进行排序的比较器,如果此Set 使用自然顺序, 则返回null |
headSet(E toElement) | 返回一个新的Set集合,新集合是toElement(不包含)之前所有的对象 |
subSet(E fromElement, E fromElement) | 返回一个新的Set集合,是fromElement(包含)对象与fromElement(不包含)对象之间的所有对象 |
tailSet(E fromElement) | 返回一个新的Set集合,新集合·包含对象 fromElement(包含) 之后的所有对象 |
10.5 Map 集合
10.5.1 Map 接口
Map接口概述
存储键值对(key-value对)
package cn.itcast.map;
/*
* Map集合,独立于Collection
* Map接口,继承了Collection接口,NO
*
* Map一次存储键值对,两个对象
* 键 保证唯一性
* 值 可以重复
* 集合,每个键只能对应一个值
* Map<k,V> 泛型
* K , 作为键的对称的泛型 Key
* V , 作为值的对象的泛型 Value
*/
public class MapDemo {
public static void main(String[] args) {
}
}
方法 | 功能描述 |
put(Object key, Object value) | 向集合中添加指定的key与value映射关系 |
containsKey(Object key) | 如果此映射包含指定key关系,则返回true |
contains Value(Object value) | 如果此映射将一个或多个key映射到指定值,则返回true |
get (Object key) | 如果存在指定的key对象,则返回该对象对应的值,否则返回null |
keySet() | 返回该集合中所有key 对象形成的Set 集合 |
values() | 返回该集合中所有值形成的Collection 集合 |
10.5.2 Map 接口的实现类
package name;
import java.util.*; //导入java.util包,其他实例都要添加该语句
public class HashMapTest {//创建类名HashMapTest
public static void main(String[] args) { //主方法
// TODO Auto-generated method stub
Map<String, String> map = new HashMap<>(); //创建Map实例
map.put("ISBN-978654","Java从入门到精通"); //向集合中添加对象
map.put("ISBN-978361","Android 从入门到精通");//向集合中添加对象
map.put("ISBN-978893","21天学Android");//向集合中添加对象
map.put("ISBN-978756","21天学Java");//向集合中添加对象
Set<String> set = map.keySet();//构建Map集合中所有key对象的集合
Iterator<String> it = set.iterator(); //创建集合迭代器
System.out.println("key值: "); //输出信息
while (it.hasNext()) { //遍历集合
System.out.print(it.next()+"\t"); //输出信息
}
Collection<String> coll = map.values();//创建集合迭代器
it = coll.iterator();
System.out.println("\nvalues值: ");//输出信息
while (it.hasNext()) { //遍历集合
System.out.print(it.next()+"\t");//输出信息
}
}
}
10.6 集合的使用场合
前面介绍了java中最常见的3种集合: List 集合、Set 集合和 Map 集合,那么在实际开发中,具体何时应该选择哪种集合呢 ? 这里我们总结了以下原则。
(1)List 集合关注的是索引,其元素是顺序存放的,例如一个班的学生成绩,成绩可以重复,就可以使用List集合存取。
(2)Set 集合关注唯一性,它的值不允许重复,例如每个班的学生的学号,每个学生的学号是不能重复的。
(3)Map 集合类关注的是唯一的标识符(KEY), 它将唯一的键映射到某个元素,例如每个班学生的学号与姓名的映射,每个学号对应一个学生的姓名,学号是不能重复的,但是学生的姓名有可能重复。
10.7 小结
本讲主要包括Collection、List集合、Set集合和Map集合。重点掌握遍历数组、添加对象、
删除对象的方法,解读例子。