大数据第十九天

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]

[email protected]

[email protected]      //由于只有一个C的对象,所以这里只有一个元素

[email protected]

[email protected]

说明:

  • 单纯的重写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);

}

}

}

时间: 2024-10-18 14:12:17

大数据第十九天的相关文章

论大数据的十大局限

“忽如一夜春风来,千树万树梨花开”,似乎在一夜之间,大数据就红遍了南北半球,,大数据被神化得无处不在,无所不包,无所不能.这里面有认识上的原因,也有故意忽悠的成份.笔者以为,越是在热得发烫的时候,越是需要有人在旁边吹吹冷风.在这里谈大数据的十大局限性,并非要否定其价值.相反,只有我们充分认识了大数据的特点和优劣势,才能更加有效地对其进行采集.加工.应用,充分挖掘和发挥其价值.         1.数据噪声:与生俱来的不和谐 大数据之所以为大数据,首先是因为其数据体量巨大.然而,在这海量的数据中,

蔡先生论道大数据之(十五) :什么是数据化运营?

数据化运营讨论(1) 近现代营销理论代表思想是4P理论,4P是指产品.价格.渠道.促销,它的核心是产品,其他要素都是围绕产品这个核心来的,进入21世纪人们又提出了4C的营销理论,包括消费者.成本.方便性.沟通交流,它的核心是用户是消费者,一切以消费者为核心,这也是目前大多数企业市场营销战略核心思想. 社会在进步,4C理论之后来到了大数据时代,所面对的行业状况是,白热化的市场竞争,越来越严苛的营销预算.堆积如山的海量数据,迫使企业寻找更加适合的营销思路和方法.目前行业主流的理论是3P3C,包括概率

蔡先生论道大数据之(十六) :穷则思变

数据化运营讨论(2) 数据化运营的理念与技术对企业来说是革命性的,它能将现在"卖方市场"商业状态变成为"买方市场". "穷则思变"古话说的好,在当下传统营销方式手段很难明显提升业绩和市场竞争力时"数据化运营"的理念和技术就成为企业未来打破竞争,提升自我强有力地商业武器. 企业通过对数据的分析和挖掘,运营不在盲目,真正做到心中有数,有的放矢.举例,传统的营销活动带来的问题:"我知道投入的广告费有一半打了水漂,收效甚微,

蔡先生论道大数据之十: 企业如何入手大数据战略(1)

今天之后的几章我们重点讨论企业如果要利用大数据应该从那几个方面入手,我粗粗的总结了一下大致分三个方面,这三个方面做到为了,恭喜你你的企业正开始享受和拥有大数据来的红利和价值. 具体入手之前,我们先要明确一点就是首先企业管理层需要有清晰思路然后从上到下认真贯彻,管理层还要对预期的业务影响要有个清晰的认识,能够给出从数据收集.模型建立到企业文化转型的一揽子方法,避免掉进"数据可以为企业做那些"这样的思维陷阱中去. 企业定制大数据战略,需要注意三个关键点,说白了也就根据前面文章总结出来的精髓

大数据营销十大切入点

许多人感觉到大数据时代正在到来,但往往只是一种朦胧的感觉,对于其真正对营销带来的威力可以用一个时髦的词来形容——不明觉厉.实际上,还是应尽量弄明白,才会明白其厉害之处.对于多数企业而言,大数据营销的主要价值源于以下几个方面. 第一,用户行为与特征分析.显然,只要积累足够的用户数据,就能分析出用户的喜好与购买习惯,甚至做到“比用户更了解用户自己”.有了这一点, 才是许多大数据营销的前提与出发点.无论如何,那些过去将“一切以客户为中心”作为口号的企业可以想想,过去你们真的能及时全面地了解客户的需求与

史上最全解析!大数据在十大行业的应用

什么是大数据?这次我们不谈概念,不谈理论,避虚就实,关注大数据在十大行业的实际应用.从证券行业到医疗领域,越来越多公司意识到大数据的重要性.2015年Gartner调查显示,超过75%的公司正在投资或计划在未来两年内投资大数据.而在2012年进行的类似调查中,仅有58%的公司在未来两年内计划投资大数据.增强客户体验.降低成本.精准营销以及提高流程效率.数据安全是公司关注大数据的主要目的.本文将研究正在使用大数据的10个垂直行业及面临的挑战,以及大数据如何解决这些难题. 1. 银行和证券挑战:通过

跟上节奏 大数据时代十大必备IT技能(转)

新的想法诞生新的技术,从而造出许多新词,云计算.大数据.BYOD.社交媒体……在互联网时代,各种新词层出不穷,让人应接不暇.这些新的技术,这些新兴应用和对应的IT发展趋势,使得IT人必须了解甚至掌握最新的IT技能. 新的想法诞生新的技术,从而造出许多新词,云计算.大数据.BYOD.社交媒体.3D打印机.物联网……在互联网时代,各种新词层出不穷,让人应接不暇.这些新的技术,这些新兴应用和对应的IT发展趋势,使得IT人必须了解甚至掌握最新的IT技能.另一方面,云计算和大数据乃至其他助推各个行业发展的

跟上节奏 大数据时代十大必备IT技能

新的想法诞生新的技术,从而造出许多新词,云计算.大数据.BYOD.社交媒体……在互联网时代,各种新词层出不穷,让人应接不暇.这些新的技术,这些新兴应用和对应的IT发展趋势,使得IT人必须了解甚至掌握最新的IT技能. 新的想法诞生新的技术,从而造出许多新词,云计算.大数据.BYOD.社交媒体.3D打印机.物联网……在互联网时代,各种新词层出不穷,让人应接不暇.这些新的技术,这些新兴应用和对应的IT发展趋势,使得IT人必须了解甚至掌握最新的IT技能.另一方面,云计算和大数据乃至其他助推各个行业发展的

大数据的十大来源及其应用价值

当你开车路过一家餐厅的停车场时,你的手机屏幕上弹出了这家餐厅的当日特价菜品推荐,这种体验是不是很棒?如果×××老板把发牌人忘记付给你的20美元亲自送还给你,你的心里是不是有点儿小激动?如果在线视频游戏能够把和我们玩法相近的用户即刻告知我们,这世界会不会变得很美妙?你是不是要下调汽车保险费率?大数据能让这一切变成现实. 网络数据即使不是最原始的大数据源,也是使用最广泛.认可度最高的大数据源.除此之外,还有很多大数据源,它们都有各自的使用价值.其中一些广为人知,而另一些几乎没有名气.我们在此要借用本