Set、Map、Collections
Set集合
Set子接口
- 特点:无序、无下标、元素不可重复。
- 方法:全部继承自Collection中的方法。
/**
* 测试Set接口的使用
* 特点:1.无序,没有下标;2.重复
* 1.添加数据
* 2.删除数据
* 3.遍历【重点】
* 4.判断
*/
public class setMethod {
public static void main(String[] args) {
//创建集合
Set<String> set =new HashSet<>();
//1.添加数据
set.add("java");
set.add("python");
set.add("golang");
System.out.println("数据个数:"+set.size());
System.out.println(set);
//2.删除元素
// set.remove("python");
// System.out.println(set);
//3.遍历
//3.1使用增强for
System.out.println("===使用增强for===");
for (String str:set) {
System.out.println(str);
}
//3.2使用迭代器
System.out.println("===使用迭代器===");
Iterator<String> it = set.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
//4.判断
System.out.println(set.contains("java"));
System.out.println(set.isEmpty());
}
}
运行结果:
数据个数:3
[python, java, golang]
===使用增强for===
python
java
golang
===使用迭代器===
python
java
golang
true
false
Set实现类
HashSet【重点】
特点:
- 基于HashCode计算元素存放位置。
- 当存入元素的哈希码相同时,会调用equals进行确认,如结果为true,则拒绝后者存入。
/**
* HashSet集合的使用
* 存储结构:哈希表(数组+链表+红黑树)
* 1.添加元素
* 2.删除元素
* 3.遍历
* 4.判断
*/
public class hashsetMethod {
public static void main(String[] args) {
//1.新建集合
HashSet<String> hashSet = new HashSet<>();
hashSet.add("java");
hashSet.add("python");
hashSet.add("golang");
System.out.println("元素个数:"+hashSet.size());
System.out.println(hashSet);
//2.删除元素
// hashSet.remove("python");
// System.out.println(hashSet);
//3.遍历
//3.1使用增强for
System.out.println("===使用增强for===");
for (String str:hashSet) {
System.out.println(str);
}
//3.2使用迭代器
System.out.println("===使用迭代器===");
Iterator<String> iterator = hashSet.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
public class Person {
private String name;
private int age;
public Person(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 String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
import java.util.HashSet;
public class Test {
public static void main(String[] args) {
//创建集合
HashSet<Person> persons = new HashSet<>();
//1.添加元素
Person p1 = new Person("hjx",18);
Person p2 = new Person("xjh",19);
persons.add(p1);
persons.add(p2);
// persons.add(p2); 重复元素不能添加
System.out.println("元素个数:"+persons.size());
System.out.println(persons);
persons.add(new Person("xjh",19));
System.out.println(persons);
}
}
运行结果:
元素个数:2
[Person{name='xjh', age=19}, Person{name='hjx', age=18}]
[Person{name='xjh', age=19}, Person{name='xjh', age=19}, Person{name='hjx', age=18}]
HashSet存储过程
- 根据hashCode计算保存的位置,如果位置为空,则直接保存,否则执行第二步。
- 执行equals方法,如果方法返回true,则认为是重复,拒绝存储,否则形成链表。
import java.util.Objects;
public class Person {
private String name;
private int age;
public Person(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 String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
/* @Override
public int hashCode() {
int n1 = this.name.hashCode();
int n2 = this.age;
return n1+n2;
}
@Override
public boolean equals(Object obj) {
if (obj==null){
return false;
}
if (this==obj){
return true;
}
if (this instanceof Person){
Person p = (Person)obj;
if (this.name.equals(p.getName())&&this.age==p.getAge()){
return true;
}
}
return false;
}
*/
//编译器给的重写hashCode和equals方法:
@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 && name.equals(person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
import java.util.HashSet;
import java.util.Iterator;
public class Test {
public static void main(String[] args) {
//创建集合
HashSet<Person> persons = new HashSet<>();
//1.添加元素
Person p1 = new Person("hjx", 18);
Person p2 = new Person("xjh", 19);
persons.add(p1);
persons.add(p2);
// persons.add(p2); 重复元素不能添加
System.out.println("元素个数:" + persons.size());
System.out.println(persons);
persons.add(new Person("xjh", 19)); //重写了hashCode和equals方法就不能添加了
System.out.println(persons);
//2.删除元素
// persons.remove(p1);
//3.遍历
//3.1使用增强for
for (Person p:persons) {
System.out.println(p);
}
//3.2使用列表迭代器
Iterator<Person> it = persons.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
//4.判断
System.out.println(persons.contains(p1));
}
}
运行结果:
元素个数:2
[Person{name='xjh', age=19}, Person{name='hjx', age=18}]
[Person{name='xjh', age=19}, Person{name='hjx', age=18}]
Person{name='xjh', age=19}
Person{name='hjx', age=18}
Person{name='xjh', age=19}
Person{name='hjx', age=18}
true
TreeSet
1.特点:
- 基于排序顺序实现不重复。
- 实现了SortedSet接口,对集合元素自动排序。
- 元素对象的类型必须实现Comparable接口,指定排序规则。
- 通过CompareTo方法确定是否为重复元素。
import java.util.TreeSet;
/**
* 使用TreeSet保存数据
* 存储结构:红黑树
* 要求:元素类必须实现Comparable接口,compareTo方法返回值为0,认为是重复元素
*/
public class treesetMethod {
public static void main(String[] args) {
TreeSet<Person> people = new TreeSet<>();
Person p1 = new Person("hjx", 18);
Person p2 = new Person("xjh", 19);
Person p3 = new Person("xjh", 20);
//需要实现Comparable接口
people.add(p1);
people.add(p2);
people.add(p3);
System.out.println(people);
}
}
查看Comparable接口的源码,发现只有一个compareTo抽象方法,在Person类中实现它:
public class Person implements Comparable<Person>{
@Override
//1.先按姓名比
//2.再按年龄比
public int compareTo(Person o) {
int n1=this.getName().compareTo(o.getName());
int n2=this.age-o.getAge();
return n1 == 0?n2:n1;
}
}
运行结果:
[Person{name='hjx', age=18}, Person{name='xjh', age=19}, Person{name='xjh', age=20}]
2.除了实现Comparable接口里的比较方法,TreeSet也提供了一个带比较器Comparator的构造方法,使用匿名内部类来实现它:
/**
* TreeSet的使用
* Comparator:实现定制比较(比较器)
*/
public class treesetUse {
public static void main(String[] args) {
//创建集合,指定比较规则
TreeSet<Person> persons = new TreeSet<>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
int n1 = o1.getAge()-o2.getAge();
int n2 = o1.getName().compareTo(o2.getName());
return n1 == 0?n2:n1;
}
});
Person p1 = new Person("hjx", 18);
Person p2 = new Person("xjh", 19);
Person p3 = new Person("xjh", 20);
Person p4 = new Person("hjx", 20);
persons.add(p1);
persons.add(p2);
persons.add(p3);
persons.add(p4);
System.out.println(persons);
}
}
运行结果:
[Person{name='hjx', age=18}, Person{name='xjh', age=19}, Person{name='hjx', age=20}, Person{name='xjh', age=20}]
3.案例:
(匿名 new Comparator() 可以替换为 lambda)
TreeSet<String> treeSet = new TreeSet<>((o1, o2) -> {
int n1 = o1.length()-o2.length();
int n2 = o1.compareTo(o2);
return n1 ==0 ?n2:n1;
});
import java.util.Comparator;
import java.util.TreeSet;
/**
* 要求:使用TreeSet集合实现字符串按照长度进行排序
* java python golang
* Comparator接口实现定制比较
*/
public class Application {
public static void main(String[] args) {
TreeSet<String> treeSet = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
int n1 = o1.length()-o2.length();
int n2 = o1.compareTo(o2);
return n1 ==0 ?n2:n1;
}
});
treeSet.add("java");
treeSet.add("python");
treeSet.add("golang");
System.out.println(treeSet);
}
}
运行结果:
[java, golang, python]
Map体系集合
1.Map接口的特点:
- 用于存储任意键值对(Key-Value)。
- 键:无序、无下标、不允许重复(唯一)。
- 值:无序、无下标、允许重复。
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Map接口的使用
* 特点:1.存储键值对 2.键不能重复,值可以重复 3.无序
*/
public class mapMethod {
public static void main(String[] args) {
//创建Map集合
Map<String,String> map = new HashMap<>();
//1.添加元素
map.put("水","water");
map.put("花","flower");
map.put("叶","leave");
System.out.println(map);
//2.删除元素
// map.remove("水");
// System.out.println(map);
//3.遍历
//3.1使用keySet();
System.out.println("====使用keySet()====");
Set<String> key = map.keySet();
for (String k:key) {
System.out.println(k+"==="+map.get(k));
}
//3.2使用entrySet();效率较高
System.out.println("====使用entrySet()====");
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> e:entries) {
System.out.print(e+"\t");
System.out.print(e.getKey()+"\t");
System.out.print(e.getValue()+"\t");
}
}
}
运行结果:
{花=flower, 水=water, 叶=leave}
====使用keySet()====
花===flower
水===water
叶===leave
====使用entrySet()====
花=flower 花 flower 水=water 水 water 叶=leave 叶 leave
Map集合的实现类
HashMap【重点】
JDK1.2版本,线程不安全,运行效率快;允许用null作为key或是value
public class Student {
private String name;
private int id;
public Student(String name, int id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", id=" + id +
'}';
}
}
import java.util.HashMap;
/**
* HashMap集合的使用
* 存储结构:哈希表(数组+链表+红黑树)
* 使用key的hashcode和equals作为重复
*/
public class HashMapMethod {
public static void main(String[] args) {
//创建集合
HashMap<Student, String> students = new HashMap<>();
//1.添加元素
Student s1 = new Student("hjx",001);
Student s2 = new Student("xjh",100);
students.put(s1,"北京");
students.put(s2,"上海");
students.put(new Student("xjh",100),"上海");
//假如相同的属性认为是一个对象怎么修改?
System.out.println("元素个数:"+students.size());
System.out.println(students);
}
}
运行结果:
元素个数:3
{Student{name='xjh', id=100}=上海, Student{name='xjh', id=100}=上海, Student{name='hjx', id=1}=北京}
要想使相同的属性无法再重复添加,重复依据是hashCode和equals方法,重写后(编译器自带的):
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return id == student.id && name.equals(student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, id);
}
运行结果变为:
元素个数:2
{Student{name='xjh', id=100}=上海, Student{name='hjx', id=1}=北京}
public class HashMapMethod {
public static void main(String[] args) {
//创建集合
HashMap<Student, String> students = new HashMap<>();
//1.添加元素
Student s1 = new Student("hjx",001);
Student s2 = new Student("xjh",100);
students.put(s1,"北京");
students.put(s2,"上海");
students.put(new Student("xjh",100),"上海");
//假如相同的属性认为是一个对象怎么修改?
System.out.println("元素个数:"+students.size());
System.out.println(students);
//2.删除元素
// students.remove(s1);
// System.out.println(students);
//3.遍历
//3.1使用keySet();
System.out.println("===使用keySet===");
for (Student key:students.keySet()) {
System.out.println(key+"==="+students.get(key));
}
//3.2使用entrySet(); 返回值是一个键值对
System.out.println("===2使用entrySet===");
for (Map.Entry<Student,String> entry: students.entrySet()) {
System.out.println(entry);
}
//4.判断
System.out.println(students.containsKey(s1));
}
}
运行结果:
元素个数:2
{Student{name='xjh', id=100}=上海, Student{name='hjx', id=1}=北京}
===使用keySet===
Student{name='xjh', id=100}===上海
Student{name='hjx', id=1}===北京
===2使用entrySet===
Student{name='xjh', id=100}=上海
Student{name='hjx', id=1}=北京
true
HashMap源码
1.默认初始化容量:
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
2.数组最大容量:
static final int MAXIMUM_CAPACITY = 1 << 30;
3.默认加载因子:
static final float DEFAULT_LOAD_FACTOR = 0.75f;
4.链表调整为红黑树的链表长度阈值(JDK1.8):
static final int TREEIFY_THRESHOLD = 8;
5.红黑树调整为链表的链表长度阈值(JDK1.8):
static final int UNTREEIFY_THRESHOLD = 6;
6.链表调整为红黑树的数组最小阈值(JDK1.8):
static final int MIN_TREEIFY_CAPACITY = 64;
7.HashMap存储的数组:
transient Node<K,V>[] table;
8.HashMap存储的元素个数:
transient int size;
9.默认加载因子是什么?
就是判断数组是否扩容的一个因子。假如数组容量为100,如果HashMap的存储元素个数超过了100*0.75=75,那么就会进行扩容。
10.链表调整为红黑树的链表长度阈值是什么?
假设在数组中下标为3的位置已经存储了数据,当新增数据时通过哈希码得到的存储位置又是3,那么就会在该位置形成一个链表,当链表过长时就会转换成红黑树以提高执行效率,这个阈值就是链表转换成红黑树的最短链表长度;
11.红黑树调整为链表的链表长度阈值是什么?
当红黑树的元素个数小于该阈值时就会转换成链表。
12.链表调整为红黑树的数组最小阈值是什么?
并不是只要链表长度大于8就可以转换成红黑树,在前者条件成立的情况下,数组的容量必须大于等于64才会进行转换。
HashSet源码
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
public HashSet() {
map = new HashMap<>();
}
}
HashSet的存储结构就是HashMap,它的存储方式,可以看一下add方法:
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
它的add方法调用的就是map的put方法,把元素作为map的key传进去的。
Hashtable
- JDK1.0版本,线程安全,运行效率慢;不允许null作为key或是value。
- 初始容量11,加载因子0.75。
Properties
- Hashtable的子类,要求key和value都是String。通常用于配置文件的读取。
它继承了Hashtable的方法,与流关系密切。
TreeMap
- 实现了SortedMap接口(是Map的子接口),可以对key自动排序。
import java.util.Map;
import java.util.TreeMap;
public class TreeMapMethod {
public static void main(String[] args) {
//创建集合
TreeMap<Student,String> treeMap = new TreeMap<>();
//1.添加元素
Student s1 = new Student("hjx",001);
Student s2 = new Student("xjh",100);
treeMap.put(s1,"北京");
treeMap.put(s2,"上海");
//不能直接打印,需要实现Comparable接口,因为红黑树需要比较大小
System.out.println("元素个数:"+treeMap.size());
System.out.println(treeMap);
//2.删除元素
// treeMap.remove(s1);
// System.out.println(treeMap);
//3.遍历
//3.1使用keySet
System.out.println("===用keySet===");
for (Student key: treeMap.keySet()) {
System.out.println(key+"==="+treeMap.get(key));
}
//使用entrySet
System.out.println("===用entrySet===");
for (Map.Entry<Student,String> entry: treeMap.entrySet()) {
System.out.println(entry);
}
//4.判断
System.out.println(treeMap.containsKey(s1));
}
}
运行结果:
元素个数:2
{Student{name='hjx', id=1}=北京, Student{name='xjh', id=100}=上海}
===用keySet===
Student{name='hjx', id=1}===北京
Student{name='xjh', id=100}===上海
===用entrySet===
Student{name='hjx', id=1}=北京
Student{name='xjh', id=100}=上海
true
在Student类中实现Comparable接口:
public class Student implements Comparable<Student>{
@Override
public int compareTo(Student o) {
int n1=this.id-o.id;
return n1;
}
除此之外还可以使用比较器来定制比较:
TreeMap<Student, Integer> treeMap2=new TreeMap<Student, Integer>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
// 略
return 0;
}
});
TreeSet源码
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
{
private transient NavigableMap<E,Object> m;
private static final Object PRESENT = new Object();
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
public TreeSet() {
this(new TreeMap<E,Object>());
}
}
TreeSet的存储结构实际上就是TreeMap,它的存储方式:
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
它的add方法调用的就是TreeMap的put方法,将元素作为key传入到存储结构中。
Collections工具类
1.概念:集合工具类,定义了除了存取以外的集合常用方法。
2.方法:
public static void reverse(List<?> list)
//反转集合中元素的顺序public static void shuffle(List<?> list)
//随机重置集合元素的顺序public static void sort(List<T> list)
//升序排序(元素类型必须实现Comparable接口)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class CollectionsMethod {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(34);
list.add(12);
list.add(56);
list.add(78);
//sort排序
System.out.println("排序之前:"+list);
Collections.sort(list);
System.out.println("排序之后:"+list);
//binarySearch二分查找
int i = Collections.binarySearch(list, 56);
System.out.println(i);
//copy复制
List<Integer> dest = new ArrayList<>();
for (int j = 0; j < list.size(); j++) {
dest.add(0);
}//该方法要求目标元素容量大于等于源目标
Collections.copy(dest,list);
System.out.println("dest:"+dest);
//reverse反转
Collections.reverse(list);
System.out.println("反转之后:"+list);
//shuffle打乱
Collections.shuffle(list);
System.out.println("打乱之后:"+list);
System.out.println("===list转成数组===");
//补充:list转成数组
Integer[] arr = list.toArray(new Integer[4]);
System.out.println(arr.length);
System.out.println(Arrays.toString(arr));
System.out.println("===数组转成集合===");
//补充:数组转成集合
String[] str = {"h","j","x"};
List<String> l = Arrays.asList(str);
//受限集合,不能添加和删除
System.out.println(l);
//住:基本数据类型转成集合时需要修改为包装类
Integer[] integers = {12,34,56,78};
List<Integer> in = Arrays.asList(integers);
System.out.println(in);
}
}
运行结果:
排序之前:[34, 12, 56, 78]
排序之后:[12, 34, 56, 78]
2
dest:[12, 34, 56, 78]
反转之后:[78, 56, 34, 12]
打乱之后:[56, 34, 12, 78]
===list转成数组===
4
[56, 34, 12, 78]
===数组转成集合===
[h, j, x]
[12, 34, 56, 78]