集合(五)——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进行遍历

  1. 可以使用keySet()获取集合中所有的键
  2. 遍历存储了所有键的集合,依次获取键对应的值
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的区别

  1. HashMap是线程不安全的集合, Hashtable是线程安全的集合。
  2. HashMap允许出现null键值, Hashtable是不允许的。
  3. HashMap的父类是AbstractMap, Hashtable的父类是Dictionary。
  4. 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的主要内容,如果未能解决你的问题,请参考以下文章

Java深入理解及巩固 Map & Set 集合

Kotlin集合操作总结 ( List 集合 | MutableList 集合 | List 集合遍历 | Set 集合 | MutableSet 集合 | Map 集合 | 可变 Map集合 )

Kotlin集合操作总结 ( List 集合 | MutableList 集合 | List 集合遍历 | Set 集合 | MutableSet 集合 | Map 集合 | 可变 Map集合 )

peptide map DDA和IMS有啥区别

Java学习笔记31(集合框架五:set接口哈希表的介绍)

Java集合系列五HashMap解析