大数据第十九天
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了大数据第十九天相关的知识,希望对你有一定的参考价值。
Set集合概述
一个不包含重复元素的 collection。
Set集合类似于一个罐子,程序中可以把多个对象放进Set集合,但是Set集合通常不能记住这些元素的添加顺序,Set集合与Collection基本相同,没有提供额外的方法,实际上Set就是Collection,只不过是行为略有不同(不允许元素重复)
如果试图把两个相同的元素放入同一个Set集合的话,则添加失败,add方法返回值为false,并且新元素不会被加入
上面的这些特性是Set集合的特性,它的实现类完全符合这些特征,另外还有自己的特色
比如:TreeSet可以排序,LinkedHashSet能保证元素有序等等
Set集合基本功能演示
存储字符串对象的引用并遍历
虽然Set集合的元素无序,但是,作为集合来说,它肯定有它自己的存储顺序,而当你存放的顺序恰好和它的存储顺序一致时,会让你误以为Set是有序的,你可以多存储一些数据,就能看到效果。
最经常使用的就是它的实现子类HashSet
import java.util.HashSet;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
// 创建集合对象,加泛型
Set<String> set = new HashSet<String>();
// 创建并添加元素
set.add("hello");
set.add("java");
set.add("world");
set.add("java");//重复字符串,不能添加
set.add("world");
// 增强for遍历
for (String s : set) {
System.out.println(s);
}
//除了增强for,还有其他的遍历方式么?迭代器
//没有索引,普通for不能用;直接打印的仅是toString结果
}
}
HashSet类概述
HashSet是Set接口的典型实现,大多数情况下,都是使用的HashSet这个实现类。
HashSet按hash算法来存储集合中的元素,因此具有很好的存取和查询性能。
HashSet有以下特点:
不保证元素的排列顺序,有可能与添加的顺序相同,有可能与添加的顺序不同
特别是它不保证该顺序恒久不变,也就是说元素的顺序有可能会变化
HashSet一般使用案例
import java.util.HashSet;
public class HashSetDemo {
public static void main(String[] args) {
// 创建集合对象,没使用多态
HashSet<String> hs = new HashSet<String>();
// 创建并添加元素
hs.add("hello");
hs.add("world");
hs.add("java");
hs.add("world");//想添加重复的字符串,不能添加成功
//在访问时,有可能打印出来的结果并不是存储时的顺序
//增强for遍历
for (String s : hs) {
System.out.println(s);
}
//迭代器遍历,这样遍历有什么好处?
Iterator<String> it = hs.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
for(Iterator it = hs.iterator();it.hasNext();){
System.out.println(it.next());
}
}
}
总结:
HashSet并不保证存储元素的顺序
当元素个数增加时,元素的顺序会重新计算
HashSet存储结构示意图
HashSet如何保证元素唯一性
HashSet保持元素唯一性的概述
- HashSet底层数据结构是哈希表,哈希表按哈希值来存储,HashSet集合中有若干个存储区域,而每个对象可以计算出一个hash值,对应一个存储区域
- 当添加新元素时,系统会调用这个元素的hashCode方法,计算出这个元素的hash值,然后跟存储区域的每一个元素进行比较,如果不相同则添加该新元素(占用一个新的槽位)。如果相同表明槽位已经有元素了,那么再调用元素的equals方法比较,若为false,则添加该元素到这个槽位后的链表上;若为true,则这个元素就添加不进去。
- 简单说就是通过元素的两个方法,hashCode()和equals()来完成,如果元素的hashCode()返回值相同,再判断equals()返回值是否为true,若为true就存不进去;如果元素的hashCode()返回值不同,不会调用equals()方法,直接就能存进去
add()方法在底层所依赖的两个方法
保证元素唯一性就是在元素添加的时候,看集合中是否已经有相同的元素了,hashSet就是在使用add方法时,对元素是否已经存在作出判断,如果已经存在了,就不会添加,否则就添加,从而保证了集合中元素的唯一性,add方法在底层依赖以下两个方法
- int hashCode():调用系统本地方法,返回一个int值
- boolean equals(Object obj):默认比较的是两个对象的内存地址
当往HashSet中添加元素时,先比较hashCode的返回值,如果相同,再调用元素的equals方法
验证以上两个方法中,到底是谁决定了HashSet的唯一性
//class A只重写了hashCode方法
public class A {
@Override
public int hashCode() {
return 1;//意味着所有A对象的hashCode方法返回值都为1
}
}
//class B只重写了equals方法
public class B {
@Override
public boolean equals(Object obj) {
return true;//意味着所有B对象的equals方法返回值都为true
}
}
//class C重写了hashCode和equals方法
public class C {
@Override
public int hashCode() {
return 2;
//意味着所有C对象的hashCode返回值都为2,注意:与类A的对象返回值不同
}
@Override
public boolean equals(Object obj) {
return true; //意味着所有C对象的equals方法返回值都为true
}
}
//测试类
public class HashSetDemo {
public static void main(String[] args) {
HashSet set = new HashSet<>();
//分别添加多个ABC对象,看最后到底有几个能被添加
set.add(new A());
set.add(new A());
set.add(new B());
set.add(new B());
set.add(new C());
set.add(new C());
for (Object object : set) {
System.out.println(object);
}
}
}
//打印结果,[类名]+[@]+[hashCode返回值]
[email protected] //由于只有一个C的对象,所以这里只有一个元素
说明:
- 单纯的重写hashCode方法,让不同的对象hashCode返回值相同,并不能保证元素唯一性,因为还要去比较equals方法的返回值
- 单纯的重写equals方法,让不同对象的equals返回值为true,也不能保证元素唯一性,因为hashCode方法返回值不同的话,根本就不调用此方法
小结:
1.自定义类创建的对象如果想要放进HashSet集合中,通常需要重写hashCode和equals方法,系统判断两个元素是相同元素的标准就是:两个元素的hashCode返回值相同并且equals方法返回值为true,所以,我们就要告诉系统什么情况下能达到上述条件
2.当两个对象的hashCode不等的时候,直接能放进HashSet集合中不同的桶
3.当两个对象的hashCode相等时,后一个元素是否能够添加进HashSet集合,取决于这个对象的equals方法的返回值,若为true,则不能添加,若为false,则可以添加
4.相同hashCode,并且equals返回值为false的多个元素会以链表形式存放在同一个桶中,这样会影响集合性能,所以,在重写两个方法的时候,一定要保证equals方法返回值为false的元素hashCode方法的返回值不同,这样做是为了保证它们能分散在不同的桶中
自定义类重写hashCode方法的原则
重写hashCode方法的目的在于,让不同的对象能尽量落在不同的桶中,尽量避免没有关系的两个元素hashCode返回值恰巧相等,因为这样还得去比较两个元素的equals方法返回值。
重写hashCode方法的原则有:
- 在程序运行过程中,同一个对象多次调用hashCode方法应该返回相同的值(如果不相同的话,意味着它对应的集合中的存储位置就不同了,也就是说同一个对象可以多次添加到这个集合了)。
- 当两个对象通过equals方法比较返回值为true时,这两个对象的hashCode方法的返回值应该为相等的值,也就是说,这两个对象是相同元素,只能添加一个
- 对象中用作equals方法比较标准的实例变量,都应该用于计算hashCode的值
当重写的hashCode方法返回值相同,而equals方法返回值为false的时候,这样的元素是被放在同一个桶中的,这样会影响集合的性能,如何进行优化呢?
优化的办法就是尽可能的让equals方法返回值为false的两个元素的hashCode值尽可能的不同,也就是说,尽可能的让单个元素占用一个“桶”,而不是多个元素共用一个“桶”
那么如何让不同的对象调用hashCode方法时返回值不同呢?
由于各个对象的实例变量值一般是不同的,比如Person类中的姓名,年龄,身份证号等,这些属性值重复的概率是很低的,那就可以在hashCode方法中使用本对象的这些属性值来构建一个hash值,这样,不同的对象之间hashCode相同的概率就大大降低了
在使用实例变量的时候需要注意,如果实例变量是一个基本数据类型的话,那就可以直接使用了,如果是一个引用数据类型的话,应该考虑到这个数据是否为null,如果为null,就用0代替,如果不是null,那就求出其对应的hash值来使用(由于是引用数据类型,其父类Object已经有了一个hashCode的方法,可以直接用)
光是考虑这些条件就够麻烦的了,有没有简便一些的方法呢?
当然有,强大的IDE已经为我们实现了
自动生成hashCode和equals方法的例子
定义完成员变量之后,从Source中自动生成重写后的hashCode和equals
public class Student {
private String name;
private int age;
public Student() { }
public Student(String name, int age) {
this.name = name;
this.age = 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;
}
@Override//自动生成
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override//自动生成
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
测试类
import java.util.HashSet;
public class HashSetDemo2 {
public static void main(String[] args) {
// 创建集合对象
HashSet<Student> hs = new HashSet<Student>();
// 创建学生对象
Student s1 = new Student("tom", 27);
Student s2 = new Student("张三", 22);
Student s3 = new Student("张四", 30);
Student s4 = new Student("tom", 27);
Student s5 = new Student("tom", 20);
Student s6 = new Student("李四", 22);
// 添加元素
hs.add(s1);
hs.add(s2);
hs.add(s3);
hs.add(s4);
hs.add(s5);
hs.add(s6);
// 遍历集合
for (Student s : hs) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
HashSet集合存储自定义对象并遍历
重点看自定义类存在多个成员变量时,使用IDE的优势
public class Dog {
private String name;
private int age;
private String color;
private char sex;
public Dog() {}
public Dog(String name, int age, String color, char sex) {
this.name = name;
this.age = age;
this.color = color;
this.sex = sex;
}
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 getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
//自动生成的hashCode方法和equals方法
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((color == null) ? 0 : color.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + sex;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Dog other = (Dog) obj;
if (age != other.age)
return false;
if (color == null) {
if (other.color != null)
return false;
} else if (!color.equals(other.color))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (sex != other.sex)
return false;
return true;
}
}
测试类
import java.util.HashSet;
public class DogDemo {
public static void main(String[] args) {
// 创建集合对象
HashSet<Dog> hs = new HashSet<Dog>();
// 创建狗对象
Dog d1 = new Dog("AA", 25, "红", ‘男‘);
Dog d2 = new Dog("BB", 22, "黑", ‘女‘);
Dog d3 = new Dog("AA", 25, "红", ‘男‘);//
Dog d4 = new Dog("AA", 20, "红", ‘女‘);
Dog d5 = new Dog("CC", 28, "白", ‘男‘);
Dog d6 = new Dog("DD", 23, "黄", ‘女‘);
Dog d7 = new Dog("DD", 23, "黄", ‘女‘);//
Dog d8 = new Dog("DD", 23, "黄", ‘男‘);
// 添加元素
hs.add(d1);
hs.add(d2);
hs.add(d3);
hs.add(d4);
hs.add(d5);
hs.add(d6);
hs.add(d7);
hs.add(d8);
// 遍历
for (Dog d : hs) {
System.out.println(d.getName() + "---" + d.getAge() + "---"
+ d.getColor() + "---" + d.getSex());
}
}
}
获取10个1至20的随机数,要求随机数不能重复(使用HashSet实现)
有范围的随机数:Random类的nextInt(int bound)方法
HashSet本身能保证元素不重复,但是往集合中尝试添加的次数不确定
import java.util.HashSet;
import java.util.Random;
public class HashSetDemo {
public static void main(String[] args) {
// 创建随机数对象
Random r = new Random();
// 创建一个Set集合
HashSet<Integer> ts = new HashSet<Integer>();
// 判断集合的长度是不是小于10
while (ts.size() < 10) {
int num = r.nextInt(20) + 1;
ts.add(num);
}
// 遍历Set集合
for (Integer i : ts) {
System.out.println(i);
}
}
}
LinkedHashSet类概述
HashSet有个子类:LinkedHashSet类,元素也是不允许重复,它的特点是使用链表维护其元素的顺序,这样使得元素是看起来按照插入的顺序保存的,也就是访问元素的顺序和存储元素的顺序一致
LinkedHashSet特点:
- 由链表保证元素访问顺序,因此性能略低于HashSet
- 由哈希表保证元素唯一
import java.util.LinkedHashSet;
public class LinkedHashSetDemo {
public static void main(String[] args) {
// 创建集合对象
LinkedHashSet<String> hs = new LinkedHashSet<String>();
// 创建并添加元素
hs.add("hello");
hs.add("world");
hs.add("java");
hs.add("world");
hs.add("java");
// 遍历
for (String s : hs) {
System.out.println(s);
}
}
}
TreeSet类概述
从继承关系上看,TreeSet实现了SortedSet接口,其元素可以进行比较,从而可以进行排序
TreeSet集合对元素进行排序有两种实现方式:
1.让元素具有比较性:使用元素的自然顺序对元素进行排序
2.让集合本身具有比较性:根据创建 set 时提供的 Comparator 进行排序
具体取决于使用的构造方法
存储整型值,自动排序的样例:
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet<Integer> ts = new TreeSet<Integer>();
// 创建元素并添加
ts.add(20);
ts.add(18);
ts.add(23);
ts.add(22);
// 遍历
for (Integer i : ts) {
System.out.println(i);
}
}
}
字符串一样可以实现排序
自定义类型的使用
自定义类实现排序的第一种方式:自然排序,让元素具有比较性
如果一个类的元素要想能够进行自然排序,就必须实现自然排序接口Comparable--可比较的,实现接口的时候指定了泛型,所以在实现compareTo方法的时候,就可以直接指定形参的类型,省去了类型转换的麻烦
public class Student implements Comparable<Student> {//实现Comparable接口
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = 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;
}
@Override//由于指定了泛型,所以在重写方法的时候,直接写类型即可
public int compareTo(Student s) {
// return 0;
// return 1;
// return -1;
// 这里返回什么,其实应该根据我的排序规则来做
// 按照年龄排序,主要条件
int num = this.age - s.age;
// 次要条件
// 年龄相同的时候,还得去看姓名是否也相同
// 如果年龄和姓名都相同,才是同一个元素
int num2 = num == 0 ? this.name.compareTo(s.name) : num;
return num2;
//若想倒序,直接将两个变量的顺序颠倒即可
//int num = s.age - this.age;
//int num2 = num == 0?s.name.compareTo(this.name):num;
//return num2;
/*另一种写法
if(this.age > o.age){
return -1;
}
if(this.age == o.age){
return o.name.compareTo(this.name);
}
return 1;*/
}
}
测试类TreeSetDemo2
import java.util.TreeSet;
public class TreeSetDemo2 {
public static void main(String[] args) {
TreeSet<Student> ts = new TreeSet<Student>();
// 创建元素
Student s1 = new Student("zhanga", 27);
Student s2 = new Student("zhangb", 27);
Student s3 = new Student("tom", 23);
Student s4 = new Student("tom", 27);
// 添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
// 遍历
for (Student s : ts) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
条件不一样比较的方式也不一样
先比较名字长度,再比较名字内容,最后是年龄,如果都相同,是同一元素,不能添加
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = 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;
}
@Override//直接指定形参的类型是Student,因为在方法声明的时候使用了泛型
public int compareTo(Student s) {
// 主要条件 姓名的长度
int num = this.name.length() - s.name.length();
// 姓名的长度相同,不代表姓名的内容相同
int num2 = num == 0 ? this.name.compareTo(s.name) : num;
// 姓名的长度和内容相同,不代表年龄相同,所以还得继续判断年龄
int num3 = num2 == 0 ? this.age - s.age : num2;
return num3;//如果三个属性值都相同,那就返回0
}
}
//测试类TreeSetDemo
import java.util.TreeSet;
/*
* 需求:请按照姓名的长度排序
*/
public class TreeSetDemo {
public static void main(String[] args) {
// 创建集合对象
TreeSet<Student> ts = new TreeSet<Student>();
// 创建元素
Student s1 = new Student("linqingxia", 27);
Student s2 = new Student("zhangguorong", 29);
Student s3 = new Student("wanglihong", 23);
Student s4 = new Student("linqingxia", 27);
Student s5 = new Student("liushishi", 22);
Student s6 = new Student("wuqilong", 40);
Student s7 = new Student("fengqingy", 22);
Student s8 = new Student("linqingxia", 29);
// 添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
ts.add(s8);
// 遍历
for (Student s : ts) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
比较器接口Comparator
自定义类实现排序的第二种方式:让集合具有比较性
使用上图中的构造方法,在创建集合的时候,传递一个比较器对象,这样的集合就是具有比较性的集合了,首先要有一个比较器才行,实现Comparator接口
import java.util.Comparator;
//自定义比较器,实现Comarator接口,实现compare方法
public class MyComparator implements Comparator<Student> {
@Override
public int compare(Student s1, Student s2) {
// 姓名长度
int num = s1.getName().length() - s2.getName().length();
// 姓名内容
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
// 年龄
int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
return num3;
}
}
//Student类
public class Student {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
this.name = name;
this.age = 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;
}
}
//测试类
import java.util.Comparator;
import java.util.TreeSet;
/*
* 排序:
* A:自然排序(元素具备比较性)
* 让元素所属的类实现自然排序接口 Comparable
* B:比较器排序(集合具备比较性)
* 让集合的构造方法接收一个比较器接口的子类对象 Comparator
*/
public class TreeSetDemo {
public static void main(String[] args) {
// TreeSet<Student> ts = new TreeSet<Student>(); //自然排序
// public TreeSet(Comparator comparator) //比较器排序的构造
// TreeSet<Student> ts = new TreeSet<Student>(new MyComparator());
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// 姓名长度
int num = s1.getName().length() - s2.getName().length();
// 姓名内容
int num2 = num == 0 ? s1.getName().compareTo(s2.getName())
: num;
// 年龄
int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
return num3;
}
});
// 创建元素
Student s1 = new Student("linqingxia", 27);
Student s2 = new Student("zhangguorong", 29);
Student s3 = new Student("wanglihong", 23);
Student s4 = new Student("linqingxia", 27);
Student s5 = new Student("liushishi", 22);
Student s6 = new Student("wuqilong", 40);
Student s7 = new Student("fengqingy", 22);
Student s8 = new Student("linqingxia", 29);
// 添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
ts.add(s8);
// 遍历
for (Student s : ts) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩)
按照总分从高到低输出到控制台
总分相同的,按语文从高到低排序
前两者都相同的,按数学从高到低排序
前三者都相同的,按英语从高到底排序
如果上面所有值都相同,说明是同一个元素,不能添加
使用集合是可排序的方式完成
public class Student {
// 姓名
private String name;
// 语文成绩
private int chinese;
// 数学成绩
private int math;
// 英语成绩
private int english;
public Student(String name, int chinese, int math, int english) {
this.name = name;
this.chinese = chinese;
this.math = math;
this.english = english;
}
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getChinese() {
return chinese;
}
public void setChinese(int chinese) {
this.chinese = chinese;
}
public int getMath() {
return math;
}
public void setMath(int math) {
this.math = math;
}
public int getEnglish() {
return english;
}
public void setEnglish(int english) {
this.english = english;
}
public int getSum() {
return this.chinese + this.math + this.english;
}
}
//测试类
public class TreeSetDemo {
public static void main(String[] args) {
// 创建一个TreeSet集合
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// 总分从高到低
int num = s2.getSum() - s1.getSum();
// 总分相同的不一定语文相同
int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num;
// 总分相同的不一定数学相同
int num3 = num2 == 0 ? s1.getMath() - s2.getMath() : num2;
// 总分相同的不一定英语相同
int num4 = num3 == 0 ? s1.getEnglish() - s2.getEnglish() : num3;
// 姓名还不一定相同呢
int num5 = num4 == 0 ? s1.getName().compareTo(s2.getName())
: num4;
return num5;
}
});
System.out.println("学生信息录入开始");
// 键盘录入5个学生信息
for (int x = 1; x <= 5; x++) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入第" + x + "个学生的姓名:");
String name = sc.nextLine();
System.out.println("请输入第" + x + "个学生的语文成绩:");
String chineseString = sc.nextLine();
System.out.println("请输入第" + x + "个学生的数学成绩:");
String mathString = sc.nextLine();
System.out.println("请输入第" + x + "个学生的英语成绩:");
String englishString = sc.nextLine();
// 把数据封装到学生对象中
Student s = new Student();
s.setName(name);
s.setChinese(Integer.parseInt(chineseString));
s.setMath(Integer.parseInt(mathString));
s.setEnglish(Integer.parseInt(englishString));
// 把学生对象添加到集合
ts.add(s);
}
System.out.println("学生信息录入完毕");
System.out.println("学习信息从高到低排序如下:");
System.out.println("姓名\t语文成绩\t数学成绩\t英语成绩");
// 遍历集合
for (Student s : ts) {
System.out.println(s.getName() + "\t" + s.getChinese() + "\t"
+ s.getMath() + "\t" + s.getEnglish());
}
}
}
Map接口概述
Map接口概述
将键映射到值的对象
一个映射不能包含重复的键
每个键最多只能映射到一个值
Map接口和Collection接口的不同
Map是双列的,Collection是单列的
Map的键唯一,Collection的子体系Set是唯一的
Map集合的数据结构只针对key有效,跟value无关
Collection集合的数据结构是针对元素有效
Map集合的功能概述:
1:添加功能
V put(K key,V value):添加元素
如果键是第一次存储,就直接存储元素,返回null
如果键不是第一次存在,就用值把以前的值替换掉,返回以前的值
2:删除功能
void clear():移除所有的键值对元素
V remove(Object key):根据键删除键值对元素,并把值返回
3:判断功能
boolean containsKey(Object key):判断集合是否包含指定的键
boolean containsValue(Object value):判断集合是否包含指定的值
boolean isEmpty():判断集合是否为空
4:获取功能
Set<Map.Entry<K,V>> entrySet()
V get(Object key):根据键获取值
Set<K> keySet():获取集合中所有键的集合
Collection<V> values():获取集合中所有值的集合
5:长度功能
int size():返回集合中的键值对的对数
Map集合常见操作演示
import java.util.HashMap;
import java.util.Map;
/*
* 作为学生来说,是根据学号来区分不同的学生的,那么假设我现在已经知道了学生的学号,我要根据学号去获取学生姓名,请问怎么做呢?
* 如果采用前面讲解过的集合,我们只能把学号和学生姓名作为一个对象的成员,然后存储整个对象,将来遍历的时候,判断,获取对应的名称。
* 但是呢,如果我都能把学生姓名拿出来了,我还需要根据编号去找吗?
* 针对我们目前的这种需求:仅仅知道学号,就想知道学生姓名的情况,Java就提供了一种新的集合 Map。
* 通过查看API,我们知道Map集合的一个最大的特点,就是它可以存储键值对的元素。这个时候存储我们上面的需求,就可以这样做
* 学号1 姓名1
* 学号2 姓名2
* 学号3 姓名3
* 学号2(不行)姓名4
* 学号4 姓名4
* Map集合的特点:
* 将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。
*
* Map集合和Collection集合的区别?
* Map集合存储元素是成对出现的,Map集合的键是唯一的,值是可重复的
* Collection集合存储元素是单独出现的,Collection的子接口Set是唯一的,List是可重复的。
* 注意:
* Map集合的数据结构只针对键有效,跟值无关
* Collection集合的数据结构是针对元素有效*/
public class MapDemo {
public static void main(String[] args) {
// 创建集合对象,带有泛型
Map<String, String> map = new HashMap<String, String>();
// 添加元素
// V put(K key,V value):添加元素。其返回值有时也有用
// System.out.println("put:" + map.put("文章", "马伊俐"));
//第一次添加没有返回值
// System.out.println("put:" + map.put("文章", "姚笛"));
map.put("邓超", "孙俪");
map.put("黄晓明", "杨颖");
map.put("周杰伦", "蔡依林");
map.put("刘恺威", "杨幂");
// void clear():移除所有的键值对元素
// map.clear();
// V remove(Object key):根据键删除键值对元素,并把值返回
// System.out.println("remove:" + map.remove("黄晓明"));
// System.out.println("remove:" + map.remove("黄晓波"));
// boolean containsKey(Object key):判断集合是否包含指定的键
// System.out.println("containsKey:" + map.containsKey("黄晓明"));
// System.out.println("containsKey:" + map.containsKey("黄晓波"));
// boolean isEmpty():判断集合是否为空
// System.out.println("isEmpty:"+map.isEmpty());
//int size():返回集合中的键值对的对数
System.out.println("size:"+map.size());
// 输出集合名称
System.out.println("map:" + map);
}
}
Map常用获取方法
获取功能:
- V get(Object key):根据键获取值
- Set<K> keySet():获取集合中所有键的集合,不可重复
- Collection<V> values():获取集合中所有值的集合,可重复
- Set<Map.Entry<K,V>> entrySet()
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapDemo2 {
public static void main(String[] args) {
// 创建集合对象
Map<String, String> map = new HashMap<String, String>();
// 创建元素并添加元素
map.put("邓超", "孙俪");
map.put("黄晓明", "杨颖");
map.put("周杰伦", "蔡依林");
map.put("刘恺威", "杨幂");
// V get(Object key):根据键获取值
System.out.println("get:" + map.get("周杰伦"));
System.out.println("get:" + map.get("周杰")); // 返回null
System.out.println("----------------------");
// Set<K> keySet():获取集合中所有键的集合
Set<String> set = map.keySet();
for (String key : set) {
System.out.println(key);
}
System.out.println("----------------------");
// Collection<V> values():获取集合中所有值的集合
Collection<String> con = map.values();
for (String value : con) {
System.out.println(value);
}
}
}
Map集合遍历的两种方式
Map集合遍历方式一
- 方式1:根据key找value
获取所有key的集合
遍历key的集合,通过key获取到每一个value
- 方式2:根据键值对对象找key和value
获取所有键值对对象的集合
遍历键值对对象的集合,获取到每一个键值对对象
根据键值对对象找key和value
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapDemo3 {
public static void main(String[] args) {
// 创建集合对象
Map<String, String> map = new HashMap<String, String>();
// 创建元素并添加到集合
map.put("杨过", "小龙女");
map.put("郭靖", "黄蓉");
map.put("杨康", "穆念慈");
map.put("陈玄风", "梅超风");
// 遍历
// 获取所有的键
Set<String> set = map.keySet();
// 遍历键的集合,获取得到每一个键
for (String key : set) {
// 根据键去找值
String value = map.get(key);
System.out.println(key + "---" + value);
}
}
}
Map集合遍历方式二
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapDemo4 {
public static void main(String[] args) {
// 创建集合对象
Map<String, String> map = new HashMap<String, String>();
// 创建元素并添加到集合
map.put("杨过", "小龙女");
map.put("郭靖", "黄蓉");
map.put("杨康", "穆念慈");
map.put("陈玄风", "梅超风");
// 获取所有键值对对象的集合
Set<Map.Entry<String, String>> set = map.entrySet();
// 遍历键值对对象的集合,得到每一个键值对对象
for (Map.Entry<String, String> me : set) {
// 根据键值对对象获取键和值
String key = me.getKey();
String value = me.getValue();
System.out.println(key + "---" + value);
}
}
}
Map接口实现类:HashMap
HashMap类概述
key是哈希表结构,可以保证key的唯一性
HashMap常见用法
- HashMap<String,String>
- HashMap<Integer,String>
- HashMap<String,Student>
- HashMap<Student,String>
HashMap演示案例一
import java.util.HashMap;
import java.util.Set;
/*
* HashMap:是基于哈希表的Map接口实现。
* 哈希表的作用是用来保证键的唯一性的。
* HashMap<String,String>
* 键:String
* 值:String
*/
public class HashMapDemo {
public static void main(String[] args) {
// 创建集合对象
HashMap<String, String> hm = new HashMap<String, String>();
//添加元素
hm.put("it001", "马云");
hm.put("it003", "马化腾");
hm.put("it004", "乔布斯");
hm.put("it005", "张朝阳");
hm.put("it001", "比尔盖茨");
// 遍历,通过key集合去找value
Set<String> set = hm.keySet();
for (String key : set) {
String value = hm.get(key);
System.out.println(key + "---" + value);
}
}
}
HashMap演示案例二
import java.util.HashMap;
import java.util.Set;
/*
* HashMap<Integer,String>
* 键:Integer
* 值:String
*/
public class HashMapDemo2 {
public static void main(String[] args) {
// 创建集合对象
HashMap<Integer, String> hm = new HashMap<Integer, String>();
//添加元素
hm.put(27, "林青霞");
hm.put(30, "风清扬");
hm.put(28, "张三");
hm.put(29, "林青霞");
// 下面的写法是八进制,但是不能出现8以上的单个数据
// hm.put(003, "hello");
// hm.put(006, "hello");
// hm.put(007, "hello");
// hm.put(008, "hello");//error
// 遍历
Set<Integer> set = hm.keySet();
for (Integer key : set) {
String value = hm.get(key);
System.out.println(key + "---" + value);
}
// 下面这种方式仅仅是集合的元素的字符串表示
// System.out.println("hm:" + hm);
}
}
演示案例:存放学生对象
由于使用的是HashMap所以自定义的类中一定要重写hashCode和equals方法,这样才能保证key能准确存储到集合中
public class Student {
private String name;
private int age;
public Student() { }
public Student(String name, int age) {
this.name = name;
this.age = 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;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
//测试类
import java.util.HashMap;
import java.util.Set;
/*
* HashMap<String,Student>
* 键:String 学号
* 值:Student 学生对象
*/
public class HashMapDemo3 {
public static void main(String[] args) {
// 创建集合对象
HashMap<String, Student> hm = new HashMap<String, Student>();
// 创建学生对象
Student s1 = new Student("周星驰", 58);
Student s2 = new Student("刘德华", 55);
Student s3 = new Student("梁朝伟", 54);
Student s4 = new Student("刘嘉玲", 50);
// 添加元素
hm.put("9527", s1);
hm.put("9522", s2);
hm.put("9524", s3);
hm.put("9529", s4);
// 遍历
Set<String> set = hm.keySet();
for (String key : set) {
// 注意了:这次值不是字符串了
Student value = hm.get(key);
System.out.println(key + "---" + value.getName() + "---"
+ value.getAge());
}
}
}
第二种方法
import java.util.HashMap;
import java.util.Set;
/*
* HashMap<Student,String>
* 键:Student
* 要求:如果两个对象的成员变量值都相同,则为同一个对象。
* 值:String
*/
public class HashMapDemo4 {
public static void main(String[] args) {
// 创建集合对象
HashMap<Student, String> hm = new HashMap<Student, String>();
// 创建学生对象
Student s1 = new Student("貂蝉", 27);
Student s2 = new Student("王昭君", 30);
Student s3 = new Student("西施", 33);
Student s4 = new Student("杨玉环", 35);
Student s5 = new Student("貂蝉", 27);
// 添加元素
hm.put(s1, "8888");
hm.put(s2, "6666");
hm.put(s3, "5555");
hm.put(s4, "7777");
hm.put(s5, "9999");//s5于s1属于重复对象,所以不能添加
// 遍历
Set<Student> set = hm.keySet();
for (Student key : set) {
String value = hm.get(key);
System.out.println(key.getName() + "---" + key.getAge() + "---"
+ value);
}
}
}
以上是关于大数据第十九天的主要内容,如果未能解决你的问题,请参考以下文章