集合(五)——Set和Map
Posted dch-21
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了集合(五)——Set和Map相关的知识,希望对你有一定的参考价值。
集合框架
Set集合
Set集合的存储特点
- Set集合没有下标的概念
- Set集合是一个去重复的集合。在Set集合中不会添加重复的元素!!
在向Set集合中添加元素的时候,会先判断在这个元素是否已经存在,若存在了则不会再添加
- Set集合中数据的存储是无序的
无序:元素的添加顺序和存储顺序是不一致的。但无序并不意味着随机。
Set的去重规则
HashSet&LinkedHashSet的去重规则
TreeSet
- 去重:如果两个对象进行大小比较的结果是0,此时代表这两个对象是相同的(通过实现Comparable接口或者实现Comparator接口)。在TreeSet中会完成排重的处理。
注意:TreeSet中元素的去重只与对象的大小比较结果有关。 与hashCode()、equals(), 没有任何关系。
- TreeSet是一个Set接口的实现类,这样的集合,会对添加进集合的元素进行去重的处理。 同时, 这个集合会对添加进入的元素进行自动的升序排序。
Comparable接口
如果一个类实现这个接口,表示自己实现了一个可以和自己的对象进行大小比较的规则。此时,这个类的对象可以直接存储进TreeSet集合中了。因为此时TreeSet集合已经知道了怎么对两个类对象进行大小比较。
import java.util.*;
public class Test {
public static void main(String[] args) {
TreeSet<Person> sets = new TreeSet<>();
sets.add(new Person(10,"xiaohong",100,30));
sets.add(new Person(20,"xiaolong",150,40));
sets.add(new Person(30,"xiaoqong",180,60));
sets.forEach(System.out::println);
}
}
//这里的泛型一定要写且和类名一致
class Person implements Comparable<Person>{
int age;
String name;
int height;
int weight;
@Override
public String toString() {
return "age=" + age +" , name=‘" + name + " , height=" + height + " , weight=" + weight ;
}
public Person(int age, String name, int height, int weight) {
this.age = age;
this.name = name;
this.height = height;
this.weight = weight;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
@Override
public int compareTo(Person person) {
return this.age-person.age;
}
}
Comparator接口
在实例化TreeSet集合对象的时候,可以通过Comparator进行实例化。 此时, 这个集合有着自己的排序的依据, 与集合中存储的元素对应的类无关。 此时集合中存储的元素对应的类, 可以不实现Comparable接口, 依然可以完成排序。 即便这个类真的实现了Comparable接口, 最终的排序结果依然以构造方法中的Comparator为准。
import java.util.*;
public class Test {
public static void main(String[] args) {
TreeSet<Person> sets = new TreeSet<>((p1,p2)->p1.height-p2.height);
sets.add(new Person(10,"xiaohong",180,30));
sets.add(new Person(20,"xiaolong",150,40));
sets.add(new Person(30,"xiaoqong",160,60));
sets.forEach(System.out::println);
}
}
//这里可以不用实现Comparable接口,因为Conparator接口的优先级高于Comparable。写在这里只是为了证明Comparator的优先级更高
class Person implements Comparable<Person>{
int age;
String name;
int height;
int weight;
@Override
public String toString() {
return "age=" + age +" , name=‘" + name + " , height=" + height + " , weight=" + weight ;
}
public Person(int age, String name, int height, int weight) {
this.age = age;
this.name = name;
this.height = height;
this.weight = weight;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
@Override
public int compareTo(Person person) {
return this.age-person.age;
}
}
Comparable与Comparator的使用场景
- 如果这个对象, 在项目中大多数的情况下, 都采用相同的大小比较的方式。 比如: 一个Person类, 在大多数情况下, 都是按照年龄进行大小比较的。 此时就可以让Person类实现Comparable接口。
- 如果某一个类的对象, 在临时进行大小比较的时候, 使用的与默认的比较不一样的规则。 比如: 一个Person类, 大多数情况下, 都是使用的年龄进行大小比较的, 但是临时需要使用身高进行一次比较, 此时就可以使用 Comparator 临时完成了。 而且, Comparator的优先级要高于Comparable。
Map集合
Map集合的存储特点
Map是双列集合的顶级接口, 这个接口并没有继承自Collection接口。 在Map中, 更多强调的是一层映射关系。 在Map中存储的数据, 是一个个的键值对(Key-Value-Pair), 键和值是一一对应的。
需要注意:
由于Map集合并没有实现Iterable接口, 因此这个集合是不能使用增强for循环遍历的。
Map的方法
返回值 | 方法 | 描述 |
---|---|---|
V | put(K key, V value) | 将一个键值对插入到集合中。 在Map集合中,不允许出现重复的键。 如果添加的键重复了,会用新的值覆盖掉原来的值。并返回被覆盖的原来的值。 |
V | putIfAbsent(K key, V value) | 将一个键值对插入到集合中。 向集合中添加元素的时候,如果这个键已经存在了,则不进行添加。 返回集合中已经存在的这个键对应的值。 |
void | putAll(Map<K, V> map) | 将一个Map集合中所有的键值对添加到当前集合中。 |
V | remove(Object key) | 通过键,删除一个键值对,并返回这个被删除的键值对中的值。 如果这个键不存在,则返回null。 |
boolean | remove(Object key, Object value) | 通过键值对进行删除。 只有当键和值一一匹配的时候, 才会进行删除。 |
void | clear() | 清空集合。 |
V | replace(K key, V value) | 修改指定的键对应的值, 并返回被覆盖的值。 |
boolean | replace(K key, V oldValue, V newValue) | 只有当key和oldValue是匹配的情况下,才会将值修改成newValue。 |
void | replaceAll(BiFunction<K, V, V> biFunction) | 对集合中的元素进行批量的替换 将集合中的每一个键值对,带入到BiFunction的方法中, 使用接口方法的返回值替换集合中原来的值。 |
V | get(K key) | 通过键, 获取值。 如果键不存在, 返回null。 |
V | getOrDefault(K key) | 通过键, 获取值。 如果键不存在, 返回默认的值。 |
int | size() | 获取集合中的元素数量(有多少个键值对) |
boolean | isEmpty() | 判断集合是否为空 |
boolean | containsKey(K key) | 判断是否包含指定的键 |
boolean | containsValue(V value) | 判断是否包含指定的值 |
Set<K> | keySet() | 获取由所有的键组成的集合(因为键是不允许重复的, 因此这里返回的是Set集合) |
Collection<V> | values() | 获取由所有的值组成的集合 |
import java.util.*;
public class Test {
public static void main(String[] args) {
Map<String,String> map=new HashMap<>();
//由于第一次添加这个键值对,集合中没有被覆盖的元素,返回String类型的默认值null
String value=map.put("name","xiaoming");
//第二次进行覆盖,返回被覆盖的值xiaoming
String value2=map.put("name","xiaobai");
//向集合中插入键值对,如果已经存在了就不添加返回存在的这个键对应的值
String value3 =map.putIfAbsent("name","xioahong");
//由于原来没有age键存在所以返回null
String value4 =map.putIfAbsent("age","20");
//将一个Map集合中所有的键值对添加到当前的集合中
Map<String, String> tmp = new HashMap<>();
tmp.put("height", "177");
tmp.put("weight", "65");
tmp.put("age", "30");
map.putAll(tmp);
//通过键删除一个键值对,并返回这个被删的键对应的值
String value5= map.remove("weight");
//修改集合中的某一个键值对,并返回被修改的值。若这个键不存在则返回null
String value6= map.replace("name","xiaohei");
//批量替换
map.replaceAll((k,v)->{
if (k.equals("weight")) {
return v+"kg";
}else return v;
});
//通过键获取值,若键不存在返回键对应的默认值,这里是null
String value7=map.get("name1");
//通过键获取值,若键不存在返回键设置的默认值
String value8 =map.getOrDefault("name1","default");
//判断是否包含某一个键
boolean value9=map.containsKey("height");
//判断是否包含某一值
boolean value10=map.containsValue("177");
//获取所有键组成的set集合,因为键是不重复的所以用set
Set<String> keys=map.keySet();
// 获取由所有的值组成的Collection集合
Collection<String> values = map.values();
System.out.println(map);
}
}
Map集合的遍历
使用keySet进行遍历
- 可以使用keySet()获取集合中所有的键
- 遍历存储了所有键的集合,依次获取键对应的值
import java.util.*;
public class Test {
public static void main(String[] args) {
Map<String,String> map=new HashMap<>();
map.put("小明","男");
map.put("小红","女");
map.put("小兰","女");
map.put("柯蓝","男");
Set<String> sets=map.keySet();
for (String key : sets) {
String value=map.get(key);
System.out.println("key="+key+",value="+value);
}
}
}
forEach
这个forEach方法, 并不是Iterable接口中的方法。 是Map接口中定义的一个方法。 从功能上将, 与Iterable中的方法差不多。 只是在参数部分有区别。
default void forEach(BiConsumer<? super K, ? super V> action)
import java.util.*;
public class Test {
public static void main(String[] args) {
Map<String,String> map=new HashMap<>();
map.put("小明","男");
map.put("小红","女");
map.put("小兰","女");
map.put("柯蓝","男");
map.forEach((key,value)->{
System.out.println("key="+key+",value="+value);
});
}
}
使用EntrySet进行遍历
Entry<K, V>:
是Map中的内部接口, 用来描述集合中的每一个键值对。
import java.util.*;
public class Test {
public static void main(String[] args) {
Map<String,String> map=new HashMap<>();
map.put("小明","男");
map.put("小红","女");
map.put("小兰","女");
map.put("柯蓝","男");
//1、获取一个存储所有Entry的Set集合
Set<Map.Entry<String,String>> entries=map.entrySet();
//2、遍历Set集合
for (Map.Entry<String, String> entry : entries) {
//获取键和值
String key=entry.getKey();
String value=entry.getValue();
System.out.println("key="+key+",value="+value);
}
}
}
HashMap和Hashtable的区别
- HashMap是线程不安全的集合, Hashtable是线程安全的集合。
- HashMap允许出现null键值, Hashtable是不允许的。
- HashMap的父类是AbstractMap, Hashtable的父类是Dictionary。
- HashMap的Map接口的新的实现类, 底层算法效率优于Hashtable。
练习
从控制台输??串字符串,统计这个字符串中每?个字符出现的次数,并整理成新的字符串输出。
输?: abccaabdsswaabbsc
输出: a(5)b(4)c(3)d(1)s(3)w(1)
import java.util.*;
/**
* @Author 昊
* @Create 2020/4/21 23:09
* @Description
* 从控制台输??串字符串,统计这个字符串中每?个字符出现的次数,并整理成新的字符串
* 输出。
* 输?: abccaabdsswaabbsc
* 输出: a(5)b(4)c(3)d(1)s(3)w(1)
*/
public class Three {
public static void main(String[] args) {
//因为最后结果是按字母升序所以用的是TreeMap
Map<Character,Integer> map=new TreeMap<>();
int count=0;
Scanner s = new Scanner(System.in);
char[] c = s.next().toCharArray();
//循环遍历字符串数组
for (char c1 : c) {
//若字符出现在了集合中,则并将该字符串对应的值取出加一后覆盖掉原来的值
if(map.containsKey(c1)){
map.put(c1,map.get(c1)+1);
}else {
//若没有出现则存入集合,对应的值为1
map.put(c1,1);
}
}
//1、获取所有存储Entry的集合Set
Set<Map.Entry<Character,Integer>> entries=map.entrySet();
//遍历Set
for (Map.Entry<Character, Integer> entry : entries) {
//获取键和值,并按指定格式输出
char key=entry.getKey();
int value=entry.getValue();
System.out.print(key+"("+value+")");
}
}
}
以上是关于集合(五)——Set和Map的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin集合操作总结 ( List 集合 | MutableList 集合 | List 集合遍历 | Set 集合 | MutableSet 集合 | Map 集合 | 可变 Map集合 )
Kotlin集合操作总结 ( List 集合 | MutableList 集合 | List 集合遍历 | Set 集合 | MutableSet 集合 | Map 集合 | 可变 Map集合 )