Set接口:也称Set集合,但凡是实现了Set接口的类都叫做Set集合
特点: 元素无索引,元素不可重复(唯一)
HashSet集合: 实现类–元素存取无序
LinkedHashSet集合:实现类–元素存取有序
TreeSet集合:实现类–> 对元素进行排序
注意:
1.Set集合没有特殊的方法,都是使用Collection接口的方法
2.Set集合没有索引,所以遍历元素的方式就只有: 增强for循环,或者迭代器
在JDK1.8之前,哈希表底层采用数组+链表实现,即使用数组处理冲突,同一hash值的链表都存储在一个数组里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间.
简单的来说,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的
给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一.
package com.claire.day13;
import java.util.Objects;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, 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 String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test2 {
public static void main(String[] args) {
Person p1 = new Person("张三", 19);
Person p2 = new Person("张三", 20);
Person p3 = new Person("张三", 19);
Person p4 = new Person("张三", 19);
// System.out.println(p1.equals(p2));
HashSet<Person> set = new HashSet<>();
set.add(p1);
set.add(p2);
set.add(p3);
set.add(p4);
System.out.println(set);
}
}
由于重写了equals和hashCode方法, 所以存储的元素就唯一了
//内部一个HashMap——HashSet内部实际上是用HashMap实现的
private transient HashMap<E,Object> map;
// transient 不会被序列化
/**
* 构造一个新的HashSet,
* 内部实际上是构造了一个HashMap
public HashSet() {
map = new HashMap<>();
}
HashSet的add方法源码解析
public boolean add(E e) {
//内部实际上添加到map中,键:要添加的对象,值:Object对象
return map.put(e, PRESENT)==null;
}
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>{}
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
}
Map集合的特点: K用来限制键的类型,V用来限制值的类型
1.Map集合存储元素是以键值对的形式存储,每一个键值对都有键和值
2.Map集合的键是唯一,值可以重复,如果键重复了,那么值就会被覆盖
3.根据键取值
Map集合子类:
- HashMap:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。
由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
- LinkedHashMap:HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。
通过链表结构可以保证键值对的存取顺序一致;
通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
- TreeMap:TreeMap集合和Map相比没有特有的功能,底层的数据结构是红黑树;
可以对元素的键进行排序,排序方式有两种:自然排序和比较器排序
Map接口中定义了很多方法,常用的如下:
package com.claire.day13;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Test3 {
public static void main(String[] args) {
// 可以自定义索引的数组
Map<String, String> map = new HashMap<>();
map.put("name", "张三");
map.put("addr", "沈阳市");
map.put("age", "22");
map.put("age", "30");
System.out.println(map); //{name=张三, addr=沈阳市, age=22}
// 获取
String name = map.get("name");
System.out.println(name);
String v = map.remove("addr");
System.out.println(v);
System.out.println(map);
// 判断是否包含指定的键
System.out.println(map.containsKey("name"));
System.out.println(map.containsKey("addr"));
// 判断是否包含指定的键
Set<String> keys = map.keySet();
System.out.println(keys);
Set<Map.Entry<String, String>> entries = map.entrySet();
System.out.println(entries);
// 遍历
System.out.println("-------遍历1--------");
for (String key: keys){
String val = map.get(key);
System.out.println("k = " + key+ " value + " + val);
}
System.out.println("-------遍历2--------");
Set<Map.Entry<String, String>> entrySet = map.entrySet();
for(Map.Entry<String, String> entry: entrySet){
String key = entry.getKey();
String value = entry.getValue();
System.out.println("k = " + key+ " value + " + value);
}
}
}
map自定义类型存储
package com.claire.day13;
import java.util.Objects;
public class Student {
private String name;
private int age;
private String id;
public Student() {
}
public Student(String name, int age, String id) {
this.name = name;
this.age = age;
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
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 boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(id, student.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", id='" + id + '\'' +
'}';
}
}
package com.claire.day13;
import java.util.HashMap;
public class Test4 {
public static void main(String[] args) {
HashMap<Student, String> map = new HashMap<>();
Student s1 = new Student("张三", 12, "22321312312");
Student s2 = new Student("李四", 12, "12312312");
Student s3 = new Student("王五", 12, "3213213213");
Student s4 = new Student("赵六", 12, "312321321321");
map.put(s1, "北京");
map.put(s2, "上海");
map.put(s3, "广州");
map.put(s4, "深圳");
System.out.println(map);
System.out.println(map.size());
}
}
需求
分析
实现
package com.claire.day13;
import java.util.HashMap;
public class Test5 {
public static void main(String[] args) {
// - 获取一个字符串对象
String s = "dasdsadsadsadasa";
// - 创建一个Map集合,键代表字符,值代表次数。
HashMap<Character, Integer> map = new HashMap<>();
// - 遍历字符串得到每个字符。
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
// - 判断Map中是否有该键。
// - 如果没有,第一次出现,存储次数为1;如果有,则说明已经出现过,获取到对应的值进行++,再次存储。
if (map.containsKey(c)){
Integer val = map.get(c);
val++;
map.put(c, val);
}else {
map.put(c, 1);
}
}
System.out.println(map);
// - 打印最终结果
}
}