Set、Map集合及其应用以及应用Collections实现集合排序

Set集合

  • Set集合代表一个元素无序,不可重复的集合
  • Set集合与List集合使用方法基本相同,只是处理行为略有不同
  • Set集合常用的实现类是:HashSet与TreeSet

Set集合体系

image.png

SortedSet可排序Set,就是允许我们可以通过自定义的形式对Set里的诗句进行排序

HashSet在进行数据提取的时候速度是非常快的

TreeSet的特点就是可以将里面的数据根据我们自定义的规则进行排序

LinkedHashSet采用链表的形式来对我们的HashSet进行排序

EnumSet是基于枚举的Set

无序性

我们可以通过add()方法往Set里增加数据,但是输出的数据是无序的

因为Set是无序的,所以Set中关于索引的都不能完成,没有get()等方法

 //实例化Set集合
        Set<String> mobileSet = new HashSet<String>();
        //通过add方法增加新的元素
        mobileSet.add("13311112222");
        mobileSet.add("13333334444");
        mobileSet.add("13355556666");
        System.out.println(mobileSet);

结果:[13333334444, 13355556666, 13311112222]

验证Set是不可重复的

add()方法依旧是返回的Boolean类型的值

//Set集合不允许出现重复,add方法返回值代表是否真正在集合中插入数据
        boolean isChanged = mobileSet.add("13377778888");
        System.out.println("Set集合是否发生改变:" + isChanged);
        //对于已有的数据,再次调用add方法写入将返回false
        isChanged = mobileSet.add("13377778888");
        System.out.println("Set集合是否发生改变:" + isChanged);
        System.out.println(mobileSet);

结果:

Set集合是否发生改变:true
Set集合是否发生改变:false
[13333334444, 13355556666, 13377778888, 13311112222]

我们也可以用size()方法统计Set里有多少数据

使用contains()方法查找Set中有没有指定的数据

//Set集合可以使用所有Collection接口定义的方法
        int count = mobileSet.size();
        boolean result = mobileSet.contains("13377778888");
        System.out.println(result);

结果:true

Set集合如何确保数据的唯一性

Set集合在新增数据时先判断数据的hashCode()是否已存在,若hashCode()在Set集合存在再调用equals()进行值比较;

hashCode()与equals()都存在的情况下,Set集合才认为数据已存在,不予新增

为什么要用对象的hashCode()直接用equals()判断不行吗?

hashCode()返回的整数结果决定了Set集合中的存放位置,hashCode()计算速度很快,但可能出现哈希碰撞;equals()则对值进行比较,处理速度相对较慢

不同的类实现的hashCode()都是不一样的;例如String类中的hashCode()方法就是根据当前字符串的值进行运算得到一个整形的数字

String a = "a";
String b = "a";
System.out.println(a.hashCode()+":"+b.hashCode());

结果: 97:97

例如Object类使用native关键字使用本地化的方式,调用了c++代码,根据我们当前对象的内存地址生成的一个整数

我们也可以重写hashCode()

public int hashCode(){
    retuen this.sn.hashCode();
}
public boolean equals(object obj){
    if(obj instanceof Goods){
        Goods goods = (Goods)obj;
        if(this.sn.equals(((Goods)obj).getsn())){
            return true;
        }else{
            return false;
        }
    }
}

说明:此处的sn是商品类(Goods)的一个属性,是编码的意思

这里重写hashCode()的目的就是希望商品根据sn属性来确定hashCode()的值从而达到商品不重复的目的

HashSet与TreeSet存储原理

HashSet

  • HashSet是Set接口的典型实现,大多数时候使用Set集合就是使用这个实现类
  • HashSet按Hash算法来决定集合元素的顺序,具有很好查找性能
  • 当向HashSet集合中存入一个元素时,根据该对象的hashCode值决定该对象在HashSet中存储位置
Hash是什么
  • HAsh、一般翻译做散列、杂凑、或者音译为哈希,是吧任意长度的数据通过散列算法变换成固定的输出,该输出就是散列值

  • HashSet是基于数组和链表或者加上红黑树来进行的


    image.png

LinkedHashSet

  • LinkedHashSet是HashSet的子类,除HashSet的特性外,它同时在使用链表维护元素的次序,可以保障按插入顺序提取数据,它的底层逻辑是在每一个元素上面增加一个链表指针来指明上一个元素是什么下一个元素是什么
  • LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet的性能
  • 迭代访问Set里的全部元素时将有很好的性能,因为它以链表来维护内部顺序

TreeSet

  • TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态,这个排序是按照我们自定义的规则来排序的
  • TreeSet采用红黑树的数据结构来存储集合元素
  • TreeSet默认采用自然排序对元素(字母顺序或数字顺序)升序排序,也可以实现Comparable接口自定义排序方式
  • 红黑树
    • 红黑树开始的根节点必须是黑色的,叶节点是不存储数据的黑色空节点
    • 红色节点的孩子只能是黑色的
    • 任意节点到其可到达的叶节点间包含相同数量的黑色节点


      image.png

      红黑树开始的根节点必须是黑色的,叶节点是不存储数据的黑色空节点

红色节点的孩子只能是黑色的

掌握HashSet与TreeSet的应用

LinkedHashSet

根据数据插入的顺序来决定数据提取的顺序,但存储方式不是按插入顺序存储的

public class LinkedHashSetSample {
    public static void main(String[] args) {
        Set<String> mobileSet = new LinkedHashSet<String>();
        mobileSet.add("13377778888");
        mobileSet.add("13311112222");
        mobileSet.add("13333334444");
        mobileSet.add("13355556666");
        System.out.println(mobileSet);
    }
}

结果:[13377778888, 13311112222, 13333334444, 13355556666]

TreeSet

根据对象字面的数值或者文本的字面义来进行排序的,可以进行自定义

自定义排序:根据数字大小进行排序 o1-o2升序 o2-o1降序

class IntegerComparator implements Comparator<Integer>{

        @Override
        public int compare(Integer o1, Integer o2) {
            return o2-o1;
        }
    }
 public void sort(){
        Set<Integer> set = new TreeSet<Integer>(new IntegerComparator());
        set.add(100);
        set.add(140);
        set.add(180);
        set.add(200);
        System.out.println(set);
    }
public static void main(String[] args) {
        new TreeSetSample().sort();
    }

初识Map接口与HashMap

Map映射特点

  • Map用于保存具有映射关系的数据,每组映射都是Key(键)与Value(值)组合而成
  • Key与Value可以使任何引用类型数据,但是Key通常是String
  • Map中的Key不允许重复,重复为同一个Key设置Value,后者Value会覆盖前者Value


    image.png

HashMap

  • HashMap是Map接口的典型实现类,对Key进行无序存储
  • HashMap不能保证数据存储顺序读取,且Key全局唯一

HashMap与HashSet的关系

  • Java先右Map后有Set,HashSet从HashMap精简而来,Map中的Hash体现在对Key进行Hash运算,HashSet相当于只是获取了Map中的Key,而Value则是通过 private Static final Object PRESENT = new Object(); 把PRESENT赋值给value,这里的PRESENT相当于一个占位符
    image.png

    HashMap的使用方法

Value部分可能根据Key的不同可能是不同的类型,所以用Object对象

put()方法的返回值是别覆盖之前的Value值

import java.util.HashMap;
//实例化HashMap,HashMap同样存储在java.util包下
        //泛型可以只写在左边,右边泛型可以省略
        HashMap<String, Object> student = new HashMap<>();
  //put方法向Map放入键值对
        student.put("name", "张三");
        //多次为同一个key赋值,新的value会覆盖旧value,同时将旧value返回
        String name = (String)student.put("name", "李四");
        System.out.println(name + "已被替换为李四");

利用get方法获取指定key的value

//利用get方法获取指定key的value
        String n = (String)student.get("name");
        System.out.println(n);

***containsKey用于判断传入的key是否存在 *** 高频

//containsKey用于判断传入的key是否存在
        boolean r1 =  student.containsKey("name");
        System.out.println(r1);

containsValue用于判断传入的value是否存在

 //containsValue用于判断传入的value是否存在
        boolean r2 =  student.containsValue(61);
        System.out.println(r2);

size方法返回当前键值对的总数

 //size方法返回当前键值对的总数
        int count = student.size();
        System.out.println(count);

remove方法将指定的键值对删除,并将value返回

//remove方法将指定的键值对删除,并将value返回
        Integer w = (Integer)student.remove("weight");
        System.out.println("weight项已被移除,其值为:" + w);
        System.out.println(student);

HashMap与LinkedHashMap的区别

LinkedHashMap就是对HashMap增加了一个链表的实现,这样让插入数据的顺序和显示数据的顺序保持一致

public class LinkedHashMapSample {
    public static void main(String[] args) {
        Map<String, Object> student = new LinkedHashMap<>();
        student.put("name", "张三");
        student.put("age", 18);
        student.put("height", 182);
        student.put("weight", 60);
        //按插入顺序提取数据
        System.out.println(student);
    }
}

结果:

{name=张三, age=18, height=182, weight=60}

初识TreeMap

  • TreeMap存储Key-value对时,需要根据Key对节点进行排序
  • TreeMap支持两种Key排序:自然排序与定制排序
  • 与TreeSet相同,TreeMap也是基于红黑树结构对数据进行排序

自然排序

public static void main(String[] args) {
       Map<String, Object> record = new TreeMap<>(new RecordComparator());
        record.put("A1", "1");
        record.put("C3", "2");
        record.put("B5", "3");
        record.put("X1", "4");
        record.put("C1", "5");
        record.put("B1", "6");
        System.out.println(record);
    }

自定义排序

Comparator对象实现排序业务逻辑

o2.compareTo(o1)意思是按照字典顺序当o2比o1大的时候就会返回一个大于0的数,

o2.compareTo(o1)降序排列

o1.compareTo(o2)升序排列

public class TreeMapSample {
        //按自定义规则对TreeMap进行排序
        class RecordComparator implements Comparator<String>{
            @Override
            public int compare(String o1, String o2) {
                return o2.compareTo(o1);
            }
        }
        public void sort(){
        Map<String, Object> record = new TreeMap<>(new RecordComparator());
        record.put("A1", "1");
        record.put("C3", "2");
        record.put("B5", "3");
        record.put("X1", "4");
        record.put("C1", "5");
        record.put("B1", "6");
        System.out.println(record);
    }
    public static void main(String[] args) {
        TreeMapSample sample = new TreeMapSample();
        sample.sort();
    }
}

Map三种遍历

初始化Map

 Map<String, Object> student = new LinkedHashMap<>();
        student.put("name", "张三");
        student.put("age", 18);
        student.put("height", 182);
        student.put("weight", 60);
        System.out.println(student);

利用for循环遍历所有key,再获取value

keySet()可以得到保存Key的Set集合,返回的是一个Set接口

 //利用for循环遍历所有key,再获取value
    public void doForLoop(Map map){
        Set<String> keys =  map.keySet();
        for(String k : keys){
            System.out.println(k + ":" + map.get(k));
        }
    }

利用forEach方法+Lambda表达式循环遍历(推荐)

//利用forEach方法+Lambda表达式循环遍历(推荐)
public void doForEach(Map map){
    map.forEach((key,value) -> {
        System.out.println(key + ":" + value);
    });
}

使用迭代器对象Iterator循环遍历每一个Entry对象,通过Entry对象获取键值对

Map.Entry Map的实体对象

map.entrySet().iterator() 获取这个集合的迭代器对象

hasNext() 是否有下一个对象

next() 获取下一个对象

//使用迭代器对象Iterator循环遍历每一个Entry对象,通过Entry对象获取键值对
public void doIterator(Map map){
    Iterator<Map.Entry<String,Object>> itr = map.entrySet().iterator();
    while(itr.hasNext()){
        Map.Entry<String,Object> entry = itr.next();
        System.out.println(entry.getKey() + ":" + entry.getValue());
    }
}

应用Collections实现集合排序

通过Collections实现List排序

Collections对集合提供额外的工具方法

Collections.sort(List)对集合里的数进行升序排列,对List内部进行加工,不会产生多余的List

public List<Integer> sort(List<Integer> list){
    //利用Collections.sort方法实现对List、Set进行排序
    Collections.sort(list,new SampleComparator());
    System.out.println(list);
    return list;
}

自定义类继承Comparator接口实现自定义排序的功能

当o1-o2时是升序

o2-o1时是降序

class SampleComparator implements Comparator<Integer>{
        //70 90 30 50
        // 结果>0,则交换位置
        // 结果=0或小于0,则位置不变
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2-o1;
        }
    }

自定义类如何集合排序

Goods 商品类 sn 商品类属性

getSn() 返回Sn值

compareTo() 比较o1和o2谁大

o1.getSn().compareTo(o2.getSn()); 升序

o2.getSn().compareTo(o1.getSn()); 降序

Collections.sort(被排序的对象,自定义排序方式对象)

private Class CustomComparator implements Comparator<Goods>{
    @Override
    public int compare(Goods o1,Goods o2){
        return o1.getSn().compareTo(o2.getSn());
}
}
public List<Integer> sort(List<Integer> list){
        //利用Collections.sort方法实现对List、Set进行排序
        Collections.sort(list,new SampleComparator());
        System.out.println(list);
        return list;
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容