集合的知识

2023-11-09

集合

collection集合的常用方法

collection的特点

Collection代表单列集合,每个元素(数据)只包含一个值。
Map代表双列集合,每个元素包含两个值(键值对)。
image.png

image.png

Collection集合特点

由于collection是一个泛型,所以我们在定义的时候要用确定的类型来定义这个

  • List系列集合:添加的元素是有序、可重复、有索引。
  • ArrayList、LinekdList :有序、可重复、有索引。
  • Set系列集合:添加的元素是无序、不重复、无索引。
  • HashSet: 无序、不重复、无索引;
  • LinkedHashSet: 有序、不重复、无索引。
  • TreeSet:按照大小默认升序排序、不重复、无索引。

Collection集合有哪两大常用的集合体系,各自有啥特点?
List系列集合:添加的元素是有序、可重复、有索引。
Set系列集合:添加的元素是无序、不重复、无索引。

根据日常使用的需求,我们来挑选不同的集合

举例

public class CommonFunction {
    public static void main(String[] args) {
        //简单确认一下collection集合 的特点
        ArrayList<String> arr = new ArrayList<>();
        arr.add("qiu");
        arr.add("ke");
        arr.add("hao");
        System.out.println(arr);  //[qiu, ke, hao]有序、可重复、有索引。
        HashSet<String> hah = new HashSet<>();
        hah.add("qiu");
        hah.add("ke");
        hah.add("hao");
        hah.add("hao");
        System.out.println(hah);   //[hao, ke, qiu]无序、不重复、无索引;
    }
}

colllection的常用方法

这些方法是所有集合都能够进行使用的方法

方法名 说明
public boolean add(E e) 把给定的对象添加到当前集合中
public void clear() 清空集合中所有的元素
public boolean remove(E e) 把给定的对象在当前集合中删除
public boolean contains(Object obj) 判断当前集合中是否包含给定的对象
public boolean isEmpty() 判断当前集合是否为空
public int size() 返回集合中元素的个数。
public Object[] toArray() 把集合中的元素,存储到数组中
package colletion.CommonFunction;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

public class collectionFunction {
    public static void main(String[] args) {
        Collection<String> c = new ArrayList<>();
        c.add("qiu");
        c.add("ke");
        c.add("hao");
        c.add("qiu");
        System.out.println(c);
        System.out.println(c.isEmpty());
        System.out.println(c.size());
        System.out.println("------------------------");

        c.remove("qiu");
        c.remove("k");
        System.out.println(c);
        System.out.println(c.size());
        System.out.println(c.contains("qiu"));
        System.out.println(c.contains("h"));



        System.out.println("------------------------");

        //在某些情况下,这个里面的数据类型并不一定都是字符串类型
        Object[] arr = c.toArray();
        System.out.println(Arrays.toString(arr));

        //如果我们就是想让其转化为字符数组呢
        String[] str = c.toArray(new String[c.size()]);
        System.out.println(Arrays.toString(str));
        
        System.out.println("------------------------");
        c.clear();
        System.out.println(c.isEmpty());
        System.out.println(c.size());


    }
}

collection 的遍历方法

迭代器

集合来调用这个迭代器的方法
然后我们再用这个Iterator类来获取这个返回对象

it.next()获取下一个值,并后移
it.hasnext()判断其后面有没有值
如果我们遍历的比较多的话,我们很有可能会造成这个迭代器越界,一定要注意

迭代器遍历的时候我们一定要注意这个迭代遍历不能在一段循环代码中,执行几次,否则的话,容易出错

        Iterator<String> iter = c.iterator();
        while (iter.hasNext()){
            String s = iter.next();
            System.out.println(s);
        }

增强型for循环

可以用来遍历这个集合和数组
for(元素的数据类型 变量名 : 数组或者集合){

}

        //增强for
        for(String s : c){
            System.out.println(s);
        }

IDEA快捷for循环遍历,如果我们想用这个索引遍历就是 数组/集合名字.fori
如果是增强for循环就是数组.集合名字.for

lambda表达式

源码

foreach方法
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

其实foreach本质还是调用这个增强for循环

        //lambda表达式
        c.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
        c.forEach(s->System.out.println(s));  
        //方法引用
		c.forEach(System.out::println); //快速遍历方式,IDEA里的是c.foreach(sout);快速构建

案例

遍历电影,考验我们对于对象的遍历的理解,我们还可以通过重写这个toString方法,来进行快速遍历

 movies.forEach(System.out::println);  //我们可以通过重写内部类的toString方法来实现这个
package colletion.CommonFunction.movies;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class moviesOutput {
    public static void main(String[] args) {
        Collection<movie> movies = new ArrayList<>();
        movies.add(new movie("《肖生克的救赎》", 9.7 ,  "罗宾斯"));
        movies.add(new movie("《霸王别姬》", 9.6 ,  "张国荣、张丰毅"));
        movies.add(new movie("《阿甘正传》", 9.5 ,  "汤姆.汉克斯"));

        for (movie mov : movies) {
            System.out.println("电影名: "+mov.getName());
            System.out.println("得分: "+mov.getScore() );
            System.out.println("演员: "+mov.getActor());
            System.out.println("--------------------");
        }

        Iterator<movie> iterator = movies.iterator();
        while (iterator.hasNext()){
            System.out.println("电影名: "+iterator.next().getName());
            System.out.println("得分: "+iterator.next().getScore() );
            System.out.println("演员: "+iterator.next().getActor());
            System.out.println("--------------------");
        }

        movies.forEach(System.out::println);  //我们可以通过重写内部类的toString方法来实现这个
    }

}

list

List系列集合特点: 有序,可重复,有索引
ArrayList:有序,可重复,有索引。
LinkedList:有序,可重复,有索引。
image.png

List和Collection都是接口,我们并不能够直接创建对象,我们要通过接口的方式来创建对象

常用方法

方法名称 说明
void add(int index,E element) 在此集合中的指定位置插入指定的元素
E remove(int index) 删除指定索引处的元素,返回被删除的元素
E set(int index,E element) 修改指定索引处的元素,返回被修改的元素
E get(int index) 返回指定索引处的元素

常用遍历方法

因为我们可以通过这个for循环索引遍历的方式
这个是因为这个list是有索引的,和这个collection不一样

list系列集合的底层原理

Arraylist

Arraylist集合的底层原理
其是基于 数组来实现的
特点是查询速度快,注意是根据索引查询速度快

  1. 查询数据通过地址值和索引定位,查询任意数据耗时相同
  2. 删除效率低,需要把后面很多的数据进行前移
  3. 添加效率第,需要把后面的数据后移,在添加元素,或者也有可能进行数组的扩容

构建原理

  1. 利用无参构造器创建的集合,会在底层创建一个默认长度为0的数组
  2. 添加第一个元素时,底层会创建一个新的长度为10的数组
  3. 存满时,会扩容1.5倍
  4. 如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准

适合根据索引查询数据,比如根据随机索引取数据,或者数据量不是很大的时候
不适合,数据量大的同时,又要频繁的进行增删操作

LinkList

基于双链表实现的。
链表中的结点是独立的对象,在内存中是不连续的,每个结点包含数据值和下一个结点的地址。

image.png

链表的特点1:查询慢,无论查询哪个数据都要从头开始找。
链表的特点2:链表增删相对快

单向链表
双向链表, 特点:查询慢,增删相对较快,但对首尾元素进行增删改查的速度是极快的。可以根据索引位置,选择从前,从后遍历

常用方法

image.png

队列

文章目录


Linkedlist的应用场景之一,我们可以用来设计队列----先进先出
队列是一种常见的数据结构系统,是一种经常用来操作首尾数据的一种系统,比如说排队系统

        LinkedList<String> queue = new LinkedList<>();
        queue.addLast("11");
        queue.addLast("22");
        queue.add("55");  //add本身也是在最后面添加元素
        queue.addLast("33");
        queue.add("44");
        System.out.println(queue);

LinkedList的引用场景之一,可以用来设计栈
栈的特点是:后进先出,先进后出

数据进入栈模型的过程称为:压/进栈(push)
数据离开栈模型的过程称为:弹/出栈(pop)

        LinkedList<String> stack = new LinkedList<>();
        stack.addFirst("11");
        stack.addFirst("22");
        stack.addFirst("33");
        System.out.println(stack);
        System.out.println(stack.removeFirst());
        System.out.println(stack.removeFirst());
        System.out.println(stack.removeFirst());

从JDK6以后,java就在linkedlist里面添加了push和pop的API,其实就是换了个马甲

    public void push(E e) {
        addFirst(e);
    }
    public E pop() {
        return removeFirst();
    }

set

image.png

无序:添加数据的顺序和获取出的数据顺序不一致; 不重复; 无索引;

  1. HashSet : 无序、不重复、无索引。
  2. LinkedHashSet:有序、不重复、无索引。
  3. TreeSet:排序、不重复、无索引。会对你上传的数据,会自动进行排序,升序排序

注意:
Set要用到的常用方法,基本上就是Collection提供的!!
自己几乎没有额外新增一些常用功能!

其实不重复的

hashset

无序、不重复、无索引。

        Set<Integer> set = new HashSet<>();
        set.add(90);
        set.add(80);
        set.add(70);
        set.add(70);
        System.out.println(set);
        //[80, 70, 90]
底层原理

注意:在正式了解HashSet集合的底层原理前,我们需要先搞清楚一个前置知识:哈希值!
哈希值
就是一个int类型的数值,Java中每个对象都有一个哈希值。
Java中的所有对象,都可以调用Obejct类提供的hashCode方法,返回该对象自己的哈希值
对象哈希值的特点
同一个对象多次调用hashCode()方法返回的哈希值是相同的。
不同的对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞)。
比如说abc和acD

基于哈希表实现。
哈希表是一种增删改查数据,性能都较好的数据结构

  1. 创建一个默认长度16的数组,默认加载因子为0.75,数组名table
  2. 使用元素的哈希值对数组的长度求余计算出应存入的位置
  3. 判断当前位置是否为null,如果是null直接存入
  4. 如果不为null,表示有元素,则调用equals方法比较
    1. 相等,则不存;不相等,则存入数组
      1. JDK 8之前,新元素存入数组,占老元素位置,老元素挂下面 链表
      2. JDK 8开始之后,新元素直接挂在老元素下面 链表

image.png

哈希表是一种增删改查数据性能都较好的结构。
根据数据对应的hash值,我们可以快速定位,所以查很快
增删改由于这个虽然是数组,但是可以允许有空元素的存在再加上链表的特征,所以也很快

无序

这是有一个计算方法的,所以无序

不重复

因为重复的不存,不重复的挂链表

无索引

元素是无序的,自然就不支持索引,没法确定索引

如果数组快占满了,会出什么问题
链表会过长,导致查询性能降低
16 * 0.75 = 12 当数组占满了12的时候就会扩容 0.75是加载因子

哈希表
JDK8之前,哈希表 = 数组+链表
JDK8开始,哈希表 = 数组+链表+红黑树
JDK8开始,当链表长度超过8,且数组长度>=64时,自动将链表转成红黑树

一个节点包括一下内容

  1. 父节点的地址值
  2. 左节点的地址值
  3. 右节点的地址值
    如果没有父节点或者没有 左右节点,那么这些节点对应的位置是null


每一个节点的子节点的数量
二叉树中任意节点的度为<=2

树高
树的总层数

根节点
最顶层的节点

左子树
右子树

二叉查找树
左节点比根节点小
右节点比根节点大
我们经常使用这个二叉查找树来进行查找节点

二叉树的遍历方式

前序遍历 从跟节点开始 左子节点,右子节点的顺序遍历
当前,左 ,右的遍历方式

中序遍历 我们先遍历最左边的节点(即最左边的叶子节点),等这个节点遍历完之后我们就往上一层,如果有右节点,那么先观察它有没有左节点,如果有的话那么我们就遍历左节点
记住关键点,一切都是以左节点为先

后序遍历
左,右,当前节点

层次遍历
一层一层的遍历

平衡二叉树

平衡二叉树的特点
二叉树左右两个子树的高度差不超过1
任意节点的左右两个子树都是一颗平衡二叉树

平衡二叉树旋转
旋转触发时机
当添加一个节点之后,该树不再是一颗平衡二叉树
左旋
就是将根节点的右侧往左拉,原先的右子节点变成新的父节点,并把多余的左子节点出让,给已经降级的根节点当右子节点
从添加的节点开始,不断的往父节点中找不平衡的点
image.png
image.png
image.png

平衡右旋
右旋
就是将根节点的左侧往右拉,左子节点变成了新的父节点,并把多余的右子节点出让,给已经降级根节点当左子节点
image.png

image.png

左右
左右: 当根节点左子树的右子树有节点插入,导致二叉树不平衡
如何旋转: 先在左子树对应的节点位置进行左旋,在对整体进行右旋
image.png

我们并不能通过一次旋转来实现这个二叉树的变换

image.png

红黑树

image.png

image.png

简单路径其实综合来说就是,不能回头
我们通过第五条规则来设定这个红色节点的数量

添加节点的规则

默认添加的节点是红色的,这样的话效率高

image.png

叔叔节点就是父节点的兄弟节点
根据这个操作来说,是不难的

红黑树是一种增删改查数据性能相对都较好的结构

去重复

HashSet集合默认不能对内容一样的两个不同对象去重复!
比如内容一样的两个学生对象存入到HashSet集合中去 , HashSet集合是不能去重复的!
如何让HashSet集合能够实现对内容一样的两个不同对象也能去重复???

结论:如果希望Set集合认为2个内容一样的对象是重复的,
必须重写对象的hashCode()和equals()方法
IDEA里有快速构造方法

linkset

有序、不重复、无索引。

        Set<Integer> set = new LinkedHashSet<>();
        set.add(90);
        set.add(80);
        set.add(70);
        set.add(70);
        System.out.println(set); 
        [90, 80, 70]
底层原理

依然是基于哈希表(数组、链表、红黑树)实现的。
但是,它的每个元素都额外的多了一个双链表的机制记录它前后元素的位置。

image.png

其实也是一样的,存放的位置和hashset是一样的,但是每一个元素都有一个前置节点和后置节点,可以确定这个节点的下一个元素,和这个节点的上一个元素,这样我们就能够知道这个哈希表中的第一个元素和后一个元素.

里面记录的信息更多,这样也会导致它更占内存

treeset

排序、不重复、无索引。会对你上传的数据,会自动进行排序,升序排序

        Set<Integer> set = new TreeSet<>();
        set.add(90);
        set.add(80);
        set.add(70);
        set.add(70);
        System.out.println(set);
        [80, 70, 90]

底层是基于红黑树实现的排序。

对于数值类型:Integer , Double,默认按照数值本身的大小进行升序排序。
对于字符串类型:默认按照首字符的编号升序排序。
对于自定义类型如Student对象,TreeSet默认是无法直接排序的。

我们需要自己自定义比较器
第一种方式是在学生类中添加接口

public class Student implements Comparable<Student>

    @Override
    public int compareTo(Student o) {
        //如果认为左边对象大于右边对象返回正整数
        //如果认为左边对象小于右边对象返回负整数
        //如果认为左边对象等于右边对象返回0  相等的话,就认为这个对象不存在,所以我们要在添加另一个规则
        //左边对象就是调用对象
        //按照年龄升序排序
        int num =0;
        num  = this.age-o.age;
        num = num==0?(int)(this.height-o.height):num;
        num = num==0? this.name.compareTo(o.name):num;
        return num;
    }
}

第二种方式是用匿名内部类

        Set<Student> s = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return 0;
            }
        });

注意点
接口是
Comparable<Student>
接口是
Comparator<Student>

总结

1、如果希望记住元素的添加顺序,需要存储重复的元素,又要频繁的根据索引查询数据?
用ArrayList集合(有序、可重复、有索引),底层基于数组的。(常用)

2、如果希望记住元素的添加顺序,且增删首尾数据的情况较多?
用LinkedList集合(有序、可重复、有索引),底层基于双链表实现的。

3. 如果不在意元素顺序,也没有重复元素需要存储,只希望增删改查都快?
用HashSet集合(无序,不重复,无索引),底层基于哈希表实现的。 (常用)

4.如果希望记住元素的添加顺序,也没有重复元素需要存储,且希望增删改查都快?
用LinkedHashSet集合(有序,不重复,无索引), 底层基于哈希表和双链表。

5. 如果要对元素进行排序,也没有重复元素需要存储?且希望增删改查都快?
用TreeSet集合,基于红黑树实现。

集合的并发修改异常

使用迭代器遍历集合时,又同时在删除集合中的数据,程序就会出现并发修改异常的错误

        Iterator<String> iter = list.iterator();
        while (iter.hasNext()){
            String name = iter.next();
            if(name.contains("李")){
                list.remove(name);
            }
        }

这是因为:删除的时候下标会继续向后所以会漏删

        for (int i = 0; i < list.size(); i++) {
            String name = list.get(i);
            if(name.contains("李")){
                list.remove(name);
            }
        }
        System.out.println(list);
        [王麻子, 小李子, 李爱花, 张全蛋, 晓李, 李玉刚]
		[王麻子, 李爱花, 张全蛋, 李玉刚]

        for (int i = 0; i < list.size(); i++) {
            String name = list.get(i);
            if(name.contains("李")){
                list.remove(name);
                i--;
            }
        }
        System.out.println(list);
        [王麻子, 小李子, 李爱花, 张全蛋, 晓李, 李玉刚]
		[王麻子, 张全蛋]

由于增强for循环遍历集合就是迭代器遍历集合的简化写法,因此,使用增强for循环遍历集合,又在同时删除集合中的数据时,程序也会出现并发修改异常的错误
lamda foreach表达式本身其实也是利用增强for循环

怎么保证遍历集合同时删除数据时不出bug?

使用迭代器遍历集合,但用迭代器自己的删除方法删除数据即可。
迭代器自带的
iter.remove();
删除当前的元素

如果能用for循环遍历时:可以倒着遍历并删除;或者从前往后遍历,但删除元素后做i --操作。

        Iterator<String> iter = list.iterator();
        while (iter.hasNext()){
            String name = iter.next();
            if(name.contains("李")){
                iter.remove();
            }
        }
        System.out.println(list);

集合进阶

可变参数

就是一种特殊形参,定义在方法、构造器的形参列表里,格式是:数据类型…参数名称;
传递不定数量的某种类型的参数

public static void test(int ...nums){

}

可变参数的特点和好处

特点:可以不传数据给它;可以传一个或者同时传多个数据给它;也可以传一个数组给它。
好处:常常用来灵活的接收数据。

其实就是一个数组

注意事项

一个形参列表中,只能有一个可变参数,无论类型,都只能有一个
可变参数必须放在形参列表的最后面

public class test01 {
    public static void main(String[] args) {
        test("kehao",1,2,3,4,5,6);
    }
    public static  void test(String name,int ... num){
        System.out.println(num.length);
        System.out.println(name);
        System.out.println(Arrays.toString(num));
    }
}

collections

image.png

这个sort是只能对默认的整形规则来进行排序,但是对于我们自己自定义的规则,我们并不能

斗地主的做牌,洗牌,发牌,排序,看牌

本身我的做法有很大问题,在面对这汇总类型固定,数量固定的牌组构建,我并没有想到直接利用数组来进行构建,而是用了多层循环来遍历,导致这个代码看起来臃肿不堪

    public Room() {
        //本身在构建对象的时候我们就可以自己产生一副牌
        String[] color = new String[]{"红桃","方片","黑桃","梅花"};
        List<Card> temp = new ArrayList<>();
        //从3到10
        for (int i=3;i<11;i++){
            for (String s : color) {
                Card card = new Card(String.valueOf(i),s,i-2);
                temp.add(card);
            }
        }
        //3 到 j
        for (char c = 'J';c<='K';c++){
            for (String s : color) {
                Card card = new Card(String.valueOf(c),s,(int)(c)-65);
                temp.add(card);
            }
        }
        for (int i=1;i<=2;i++){
            for (String s : color) {
                Card card = new Card(String.valueOf(i),s,i+11);
                temp.add(card);
            }
        }
        Card joker = new Card("大王","红色",15);
        temp.add(joker);
        Card joker1 = new Card("小王","黑色",14);
        temp.add(joker1);
        this.cards.addAll(temp);
    }

较为合适的代码

    public Room() {
        //本身在构建对象的时候我们就可以自己产生一副牌
        String[] color = new String[]{"红桃","方片","黑桃","梅花"};
        String[] points = new String[]{"3","4","5","6","7","8","9","10","J","Q","K","J","2"};
        //从3到10
        int size=0;
        for (String point : points) {
            size++;
            for (String s : color) {
                Card c= new Card(point,s,size);
                cards.add(c);
            }
        }
        Card joker = new Card("大王","红色",++size);
        Card joker1 = new Card("小王","黑色",++size);
        Collections.addAll(cards,joker,joker1);
    }

Map集合

Map集合是双列集合

Map集合称为双列集合,格式:{key1=value1 , key2=value2 , key3=value3 , …}, 一次需要存一对数据做为一个元素.
Map集合的每个元素“key=value”称为一个键值对/键值对对象/一个Entry对象,Map集合也被叫做“键值对集合”
Map集合的所有键是不允许重复的,但值可以重复,键和值是一一对应的,每一个键只能找到自己对应的值

Map集合的键不能重复,Map集合的值可以重复

Map集合在什么业务场景下使用

{商品1=2 , 商品2=3 , 商品3 = 2 , 商品4= 3}
需要存储一一对应的数据时,就可以考虑使用Map集合来做
image.png

Map集合体系的特点

注意:Map系列集合的特点都是由键决定的,值只是一个附属品,值是不做要求的

HashMap(由键决定特点): 无序、不重复、无索引; (用的最多)
LinkedHashMap (由键决定特点):由键决定的特点:有序、不重复、无索引。
TreeMap (由键决定特点):按照键大小默认升序排序、不重复、无索引。

经典代码
Map<String,Integer> map = new HashMap<>();

        Map<String,Integer> map = new HashMap<>();
        map.put("黑丝",2);
        map.put("白丝",1);
        map.put("白丝",3);   //后面重复的数据会覆盖前面的数据
        map.put("渔网",3);
        System.out.println(map);

map集合的常用方法

image.png

因为这个键值是无序,无索引,不重复,所以自然要将这个放到这个set集合中0

因为值是可以重复的,所以把这个值放到这个collection集合中

        Set<String> keys = map.keySet();
        System.out.println(keys);
        Collection<Integer> values = map.values();
        System.out.println(values);

将一个集合的元素倒入到另一个集合中去

aeabc734a7c7636ac5faa76dfa2818bd.jpg

map集合的遍历方式

image.png

键找值

我们利用keyset方法来获得所有键的集合,然后根据这个getkey获得对应的值

image.png

键值对

本质是利用这个增强for循环来进行遍历,但是对于增强for循环,我们要有其对那个的数据类型,为了方便遍历,java封装了一个entry对象,我们可以通过这个来进行遍历

image.png

image.png

Set< Map.Entry<String, Double > entries = map.entrySet();

for (Map.Entry<String, Double> entry : entries) {
	tring key = entry.getKey();
	double value = entry.getValue();
	System.out.println(key + "====>" + value);
}

lambda表达式

image.png

查看源码就是这个foreach 使用这个匿名内部类,然后内部的实现方式是通过第二种的entry方式来进行实现的.

        map.forEach((k,v)->{
            System.out.println(k+"-->"+v);
        });

只要是存储意义对应的数据的时候,我们就可以考虑用map集合

Map的三个常见实现类

HashMap

HashMap集合的底层原理
HashMap跟HashSet的底层原理是一模一样的,都是基于哈希表实现的。

实际上:原来学的Set系列集合的底层就是基于Map实现的,只是Set集合中的元素只要键数据,不要值数据而已。

public HashSet() {
	map = new HashMap<>();
}

哈希表

  1. JDK8之前,哈希表 = 数组+链表
  2. JDK8开始,哈希表 = 数组+链表+红黑树
  3. 哈希表是一种增删改查数据,性能都较好的数据结构。

image.png

HashMap集合是一种增删改查数据,性能都较好的集合
但是它是无序,不能重复,没有索引支持的(由键决定特点

HashMap的键依赖hashCode方法和equals方法保证键的唯一

如果键存储的是自定义类型的对象,可以通过重写hashCode和equals方法,这样可以保证多个对象内容一样时,HashMap集合就能认为是重复的。

LinkedMap

image.png

底层数据结构依然是基于哈希表实现的,只是每个键值对元素又额外的多了一个双链表的机制记录元素顺序(保证有序)。

实际上:原来学习的LinkedHashSet集合的底层原理就是LinkedHashMap。

TreeMap

特点:不重复、无索引、可排序(按照键的大小默认升序排序,只能对键排序)
原理:TreeMap跟TreeSet集合的底层原理是一样的,都是基于红黑树实现的排序

TreeMap集合同样也支持两种方式来指定排序规则
让类实现Comparable接口,重写比较规则。
TreeMap集合有一个有参数构造器,支持创建Comparator比较器对象,以便用来指定比较规则

Stream

image.png

image.png

处理方法就是

  1. 获取流
  2. 中间处理
  3. 流的终结

image.png

  1. filter是限制条件
  2. foreach是进行遍历

常见数据类型获取Stream的方法

list

        List<String> list = new ArrayList<>();
        Collections.addAll(list,"刘德华","古天乐","德玛西亚","艾欧尼亚","德云色");
        Stream<String> stream = list.stream();
        stream.filter(s->s.contains("德")).forEach(System.out::println);

set

Set<String> set = new HashSet<>();  
Collections.addAll(set,"刘德华","古天乐","德玛西亚","艾欧尼亚","德云色");  
Stream<String> stream1 = set.stream();  
stream1.filter(s->s.length()==3).forEach(System.out::println);

map

Map<String, Double> map = new HashMap<>();  
map.put("古力娜扎",172.3);  
map.put("迪丽热巴",168.3);  
map.put("马尔扎哈",166.3);  
map.put("卡尔扎巴",168.3);  
//由于map集合不属于collection,所以我们不能直接使用这个Stream流  
Set<String> set1 = map.keySet();  
Stream<String> stream2 = set1.stream();  
Collection<Double> values = map.values();  
Stream<Double> stream3 = values.stream();
Set<Map.Entry<String, Double>> entries = map.entrySet();  
Stream<Map.Entry<String, Double>> stream4 = entries.stream();  
stream4.filter(s->s.getKey().contains("巴")).forEach(s-> System.out.println(s));

数组

String[] views = {"五台山","玄武湖","故宫","蔡明园","夫子庙","灵隐寺","西湖"};  
Stream<String> stream5 = Arrays.stream(views);  
Stream<String> views1 = Stream.of(views);

常见的中间方法

间方法指的是调用完成后会返回新的Stream流,可以继续使用(支持链式编程)

image.png

比较难以理解的是
sort
对于我们重写的这个sort方法,当我们简略缩写的时候我们
stream.filter(s->s>30).sorted((Double o1, Double o2)-> (int)(o2-o1)).forEach(s-> System.out.println(s));

这个return是不在使用的

distinct,如果是我们自己自定义的对象,如果我们向去除重复的元素我们可以通过重写对象中的equals和hashcode方法来进行书写代码

map
map就是让这个流变成它给出的元素的流,比如说一个对象流我们只需要用其中一个属性值,那么我们就可以用这个

常用的终结方法

image.png

max和min都可以重写匿名内部类
最后这个返回的是optionnal类型的对象,因此我们并不能直接获取,我们要在其后面再加入这个get来实现

        Student student = students.stream().min(((o1, o2) -> o2.getAge() - o1.getAge())).get();
        System.out.println(student);

收集Stream流:就是把Stream流操作后的结果转回到集合或者数组中去返回。

就是我们处理的结果,传递到这个集合和数组容器之中去

Stream流:方便操作集合/数组的手段; 集合/数组:才是开发中的目的。

image.png

List<Student> collect = students.stream().filter(s -> s.getAge() > 30).collect(Collectors.toList());  
System.out.println(collect);

image.png

流的注意事项

流只能收集一次,不能重复使用

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

集合的知识 的相关文章

随机推荐

  • 成功解决:ModuleNotFoundError: No module named ‘amp_C‘

    在使用transformers时 在调用Trainer的时候遇到了这个问题 原因是apex包有问题 这里有解决apex安装包的多一些教程 https blog csdn net Xidian185 article details 12274
  • react讲解(组件,生命周期以及受控组件)

    文章目录 前言 一 组件的通信原理 state 和 setState 二 组件分类 1 类组件 2 组件中父子组件的通信 代码示例 A组件代码 B组件代码 2 跨组件通信 A组件代码如下 C组件代码如下 三 组件的生命周期
  • Elasticsearch内存那些事儿

    Elasticsearch 内存分配设置详解 前言 该给 ES 分配多少内存 为什么是给 ES 分配服务器的一半内存 为什么内存使用率不断升高 没有释放 为何经常有某个 field 的数据量超出内存限制的异常 为何感觉上没多少数据 也会经常
  • TCP协议 ---可靠传输的各种机制

    目录 一 可靠 确认应答机制 保证数据可靠 有序的到达对端 超时重传机制 二 效率 2 1 提高自身发送数据量 滑动窗口机制 滑动窗口的发送缓冲区是环形队列 滑动窗口的大小会变化吗 滑动窗口的nb之处 滑动窗口丢包问题 2 2 对方的接受能
  • twrp3.3.0刷n9002_【笔刷】iPad Procreate5.0新笔刷分享,巨好用!

    01 Procreate 趣味复古蜡笔纹理笔刷15款 适用软件 Procreate5 0以上 适用系统 ipad系统 笔刷格式 brushset 素材大小 93MB 笔刷视频演示 赠送15款笔刷 29款色卡 一张纸张背景 02 Procre
  • 树莓派Pico

    9 2 在MS Windows上构建 在Microsoft Windows 10或Windows 11上安装工具链与其他平台有些不同 然而安装后 RP2040的构建代码基本类似 Tips 什么是工具链 工具链是指一系列软件 逐个使用这一系列
  • docker run中-v参数的用法解释

    作用 挂载宿主机的一个目录 如 docker run it v 宿主机目录 容器目录 镜像名 bin bash 这里 it是参数作用是 i 以交互模式运行容器 通常与 t 同时使用 t 为容器重新分配一个伪输入终端 通常与 i 同时使用 就
  • keil编译时候产生的错误(Error: L6200E: Symbol....)解决方法

    今天分享一个自己在做实验时候发现的一个错误问题 查了一下网上也有人遇到这样的问题 就拿出来分享了一下自己遇到的情况 首先看keil的错误提示 如图所示 可以看到两个报错为 Error L6200E Symbol usart3 init mu
  • CV Code

    点击我爱计算机视觉标星 更快获取CVML新技术 计算机视觉技术发展迅速 很多时候 可悲的不是我们没有努力 而是没有跟上时代的步伐 努力coding终于出来结果了 却发现早就有人开源了 效果还比自己写的好 CV君汇总了最近过去的一周新出的开源
  • 【Matlab】将Matlab里的几个变量一个存成csv文件

    注意 几个变量类型可以不同 但是长度必须相同 举例说明 1 比如说在workspace里已经有两个变量 a和b如图 每个变量为1列 想把这两个变量存成一个csv文件 2 先给这两个变量名起个名字 分别为A B 存入 datacolumns
  • 数据属性类型

    数据集由数据对象组成 一个数据对象代表一个实体 数据对象又称样本 实例 数据点或对象 属性 attribute 是一个数据字段 表示数据对象的一个特征 属性向量 或特征向量 是用来描述一个给定对象的一组属性 属性有不同类型 标称属性 nom
  • python面试题

    文章目录 Python面试基础题小汇总 1 Python是如何进行内存管理的 2 什么是lambda函数 它有什么好处 3 Python里面如何实现tuple和list的转换 4 请写出一段Python代码实现删除一个list里面的重复元素
  • 常用的运算放大器电路

    在线仿真网站 http scratch trtos com circuitjs html 一 反向比例放大电路 二 同向比例放大电路 三 电压跟随器 四 反向求和运算电路 五 同向求和运算电路 六 加减法运算放大器 七 差分放大器 八 积分
  • 关于自制CMSIS_DAP离线下载器下载算法的代码说明:“0xE00ABE00, 0x062D780D, 0x24084068, 0xD3000040, 0x1E644058, 0x1C49D1FA“

    关于自制CMSIS DAP离线下载器下载算法的代码说明 0xE00ABE00 0x062D780D 0x24084068 0xD3000040 0x1E644058 0x1C49D1FA 在自制CMSIS DAP离线下载器的时候 利用FLM
  • Mysql篇-第2章,什么是脏读、幻读、不可重复读?如何处理?

    一 Mysql进行事务并发控制时经常遇到的问题 脏读 在事务进行中 读到了其他事务未提交的数据 举个例子 有一个table表 如果执行顺序如下 这种情况下左边查询的结果会是101 正是因为读取到了另一个事务未提交的数据 幻读 在一个事务中
  • selenium 获取cookie 并使用

    selenium 获取cookie 参数设置 以获取阿里云cookie范例 from selenium import webdriver import json url https account aliyun com login logi
  • 使用Python的方式理解Golang的结构体struct

    Go源码 package GoTools import fmt 定义结构体存储密码 type Config struct password string func InitConfig password string Config c ne
  • Vue用户进行页面切换(路由跳转)时,动态改变路由的动画(transition效果)

    当我们在使用Vue Router时 为了用户有更好的视觉效果及体验 我们通常需要实现基于路由的动态过渡效果 github https github com Rise Devin FullStack Product Transport Use
  • retinaface代码讲解_「干货」RetinaFace最强开源人脸识别算法

    看来最早商业化的人脸检测为目标检测算法 依然是各大CV方向AI公司的必争之地 那我们今天主角就是RetinaFace RetinaFace 是今年5月份出现的人脸检测算法 当时取得了state of the art 作者也开源了代码 过去了
  • 集合的知识

    集合 collection集合的常用方法 collection的特点 Collection代表单列集合 每个元素 数据 只包含一个值 Map代表双列集合 每个元素包含两个值 键值对 Collection集合特点 由于collection是一