【博学谷学习记录】超强总结,用心分享 | 集合

【博学谷IT技术支持】

集合

  • 集合根据存储分为单列集合java.util.Collection和双列结合java.util.Map
  • 集合的长度是可变的,
  • 集合只能存引用数据类型,如果要存基本数据类型,需要存对应的包装类。
  • 数组可以存基本数据类型和引用数据类型

Collection

单列集合的跟接口, 用于存储一系列符合某种规则的元素。有两个子接口:java.until.Listjava.util.Set

List的特点

元素有序,元素可重复

List接口的主要实现类有java.util.ArrayListjava.util.LinkedList

Collection集合常用方法

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

 Collection<String> collection = new ArrayList<>();
  • boolean add(E e) 添加元素
collection.add("aaa");
collection.add("bbb");
collection.add("ccc");
System.out.println(collection);  // [aaa, bbb, ccc]
  • boolean remove(Object o) 从集合中移除指定的元素
boolean aaa = collection.remove("aaa");
 boolean bbb = collection.remove("bbb");
System.out.println(aaa); // true
System.out.println(bbb); // true
System.out.println(collection); // [ccc]
  • boolean removeIf(Object o) 根据条件进行移除
 // 判断为true删除,为false 不删
collection.removeIf((String s) -> {
    return s.length() == 3;
});
System.out.println(collection);// []
  • void clear() 清空集合中的元素
collection.clear();
System.out.println(collection); // []
  • boolean contains(Object o) 判断集合中是否存在指定的元素
boolean a = collection.contains("a");
System.out.println(a);// false
  • boolean isEmpty() 判断集合是否为空
System.out.println(collection.isEmpty());// false
  • int size() 集合的长度,也就是集合中元素的个数
System.out.println(collection.size());// 3

迭代器

迭代器, 集合的专用遍历方式 Iterator, iterator ():返回此集合中元素的迭代器, 通过集合对象的iterator() 方法得到

Iterator中的常用方法

  • boolean hasNext ():判断当前位置是否有元素可以被取出
  • E next():获取当前位置的元素, 将迭代器对象移向 下一个索引位置
		ArrayList<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");

        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            String next = it.next();
            if ("b".equals(next)) {
                // 指向谁,删除谁
                it.remove();
            }
        }
        System.out.println(list); // [a, c, d, e]

增强for循环

  • 介绍
    它是JDK5之后出现的,其内部原理是一个Iterator迭代器 实现Iterable接口的类才可以使用迭代器和增强for 简化数组和Collection集合的遍历
  • 格式for
    (集合/数组中元素的数据类型 变量名 : 集合/数组名)
		ArrayList<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");
        list.add("f");
        for (String str:list) {
            // 1,数据类型一定是集合或者数组中元素的类型
            // 2,str仅仅是一个变量名而已,在循环的过程中,依次表示集合或者数组中的每一个元素
            // 3,list就是要遍历的集合或者数组
            System.out.println(str);// a b c d e f 
        }
        System.out.println(list); // [a, b, c, d, e, f]

list集合

list的特点

  • 存取有序
  • 可以重复
  • 有索引
    方法:
  • void add(int index,E element) 在此集合中的指定位置插入指定的元素
  • E remove(int index) 删除指定索引处的元素,返回被删除的元素
  • E set(int index,E element) 修改指定索引处的元素,返回被修改的元素
  • E get(int index) 返回指定索引处的元素

数据结构

  • 栈结构:先进后出
  • 队列结构:先进先出
    数组和链表的特点
  • 数组结构:查询快、增删慢
  • 队列结构:查询慢、增删快

ArrayList集合:底层是数组结构实现,查询快、增删慢
LinkedList集合:底层是链表结构实现,查询慢、增删快

LinkedList集合的特有功能【应用】

LinkedList<String> list = new LinkedList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        list.add("ddd");
  • public void addFirst(E e) 在该列表开头插入指定的元素
list.addFirst("first"); 
System.out.println(list);// [first, aaa, bbb, ccc, ddd]
  • public void addLast(E e) 将指定的元素追加到此列表的末尾
list.addLast("last");
System.out.println(list); // [first, aaa, bbb, ccc, ddd, last]
  • public E getFirst() 返回此列表中的第一个元素
String first = list.getFirst();
System.out.println(first); // first
  • public E getLast() 返回此列表中的最后一个元素
String last = list.getLast();
System.out.println(last); // last
  • public E removeFirst() 从此列表中删除并返回第一个元素
String s = list.removeFirst();
System.out.println(s); // first
  • public E removeLast() 从此列表中删除并返回最后一个元素
String s1 = list.removeLast();
System.out.println(s1); //  last

泛型

泛型是JDK5中引入的特性,它提供了编译时类型安全检测机制

-泛型的好处
把运行时期的问题提前到了编译期间
避免了强制类型转换

泛型的定义格式

 - 泛型类:修饰符 class 类名<类型> { }
 - 泛型方法:修饰符 <类型> 返回值类型 方法名(类型 变量名) { }
 - 泛型接口:修饰符 interface 接口名<类型> { }

第一种demo

interface Genericity<E> {
    public abstract void method(E e);
}
// 实现类
class GennericityImpl<E> implements Genericity<E> {
    @Override
    public void method(E e) {
        System.out.println(e);
    }
}

GennericityImpl<String> gennericity = new GennericityImpl<>();
gennericity.method("泛型接口的一种实现方式");

第二种demo

// 定义实现类时,直接明确泛型的具体类型
class GennericityImpl2 implements Genericity<Integer> {
    @Override
    public void method(Integer integer) {
        System.out.println(integer);
    }
}
GennericityImpl2 gennericityImpl2 = new GennericityImpl2();
gennericityImpl2.method(10);

类型通配符

  • ArrayList<?>: 表示元素类型未知的ArrayList,它的元素可以匹配任何的类型,但是并不能把元素添加到ArrayList中了,获取出来的也是父类类型
  • ArrayListList <? extends Number>: 它表示的类型是Number或者其子类型
  • ArrayListList <? super Number>: 它表示的类型是Number或者其父类型

Set的特点

元素无序
不可重复
可以将元素按照规则进行排序:
	-TreeSet():根据其元素的自然排序进行排序
	-TreeSet(Comparator comparator) :根据指定的比较器进行排序

Set接口的主要实现类有java.util.HashSetjava.util.TreeSet

 		TreeSet<Integer> ts = new TreeSet<>();
        ts.add(11);
        ts.add(22);
        ts.add(44);
        ts.add(33);
        System.out.println(ts); // [11, 22, 33, 44]

自然排序Comparable的使用

1.用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
2.自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法
3.写接口中的compareTo方法,一定要注意排序规则必须按照要求的主要条件和次要条件来写
public class Student implements Comparable<Student>{
    private String name;
    private int age;

    ...

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Student o) {
        int result = this.age - o.age;
        result = result == 0 ? this.name.compareTo(o.getName()) : result;
        return result;
    }
}

比较器排序Comparator的使用

比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法

        TreeSet<Teacher> ts = new TreeSet<>(new Comparator<Teacher>() {
            @Override
            public int compare(Teacher o1, Teacher o2) {
                // o1 现在要存入的元素  o2表示已经存入的集合元素
                int result = o1.getAge() - o2.getAge();
                result = result == 0? o1.getName().compareTo(o2.getName()):result;
                return result;
            }

        });
        Teacher t1 = new Teacher("zhangsan", 23);
        Teacher t2 = new Teacher("lisi", 24);
        Teacher t3 = new Teacher("wangwu", 25);
        Teacher t4 = new Teacher("zhangsan", 24);

        ts.add(t1);
        ts.add(t2);
        ts.add(t3);
        ts.add(t4);
        System.out.println(ts);

总结

  • 自然排序: 自定义类实现Comparable接口,重写compareTo方法,根据返回值进行排序
  • 比较器排序: 创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行 排序
  • 在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,必须使用比较器排序

两种方式中关于返回值的规则

  • 如果返回值为负数,表示当前存入的元素是较小值,存左边
  • 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
  • 如果返回值为正数,表示当前存入的元素是较大值,存右边

数据结构

二叉树

二叉树分为二叉树,二叉查找树,平衡二叉树三种。

二叉树的特点

二叉树中,任意一个节点的度要小于等于2

节点: 在树结构中,每一个元素称之为节点 
度: 每一个节点的子节点数量称之为度

在这里插入图片描述

二叉查找树

二叉查找树,又称二叉排序树或者二叉搜索树
每一个节点上最多有两个子节点
左子树上所有节点的值都小于根节点的值
右子树上所有节点的值都大于根节点的值
  • 添加节点规则

    小的存左边
    大的存右边
    一样的不存

平衡二叉树

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

旋转机制:
当添加一个节点后,平衡被破坏   
  • 左旋
    就是将根节点的右侧往左拉,原先的右子节点变成新的父节点,并把多余的左子节点出让,给已经降级 的根节点当右子节点
  • 右旋
    就是将根节点的左侧往右拉,左子节点变成了新的父节点,并把多余的右子节点出让,给已经降级根节 点当左子节点

四种旋转情况
左左: 当根节点左子树的左子树有节点插入,导致二叉树不平衡
左右: 当根节点左子树的右子树有节点插入,导致二叉树不平衡
右右: 当根节点右子树的右子树有节点插入,导致二叉树不平衡
右左:当根节点右子树的左子树有节点插入,导致二叉树不平衡

如何旋转:如果两个相同,则向相反方向旋转,如果不同则按给出的方向移动。例如左左、右右,两个都相同,则旋转方向分别是整体向右旋和向左旋,不同的如左右和右左,则先左子树向左旋,再整体右旋,右左则是先右子树向右旋,再整体向左旋转。

红黑树

红黑树的规则

1. 每一个节点或是红色的,或者是黑色的 
2. 根节点必须是黑色 
3. 如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点 (Nil)是黑色的
4. 如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连 的情况)
5. 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点

红黑树添加节点后如何保持红黑规则

添加节点时,默认为红色,效率高

根节点位置
	直接变为黑色
	
非根节点位置
 	父节点为黑色 
 			不需要任何操作,默认红色即可
 	父节点为红色
 			叔叔节点为红色
 				1. 将"父节点"设为黑色,将"叔叔节点"设为黑色 
 				2. 将"祖父节点"设为红色 
 				3. 如果"祖父节点"为根节点,则将根节点再次变成黑色
 			叔叔节点为黑色 
 				1. 将"父节点"设为黑色 
 				2. 将"祖父节点"设为红色 
 				3. 以"祖父节点"为支点进行旋转

hashSet的特点

底层数据结构是哈希表
存取无序 
不可以存储重复元素 
没有索引,不能使用普通for循环遍历

哈希值

哈希值简介 
		是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值 
如何获取哈希值 
		Object类中的public int hashCode():返回对象的哈希码值 
哈希值的特点 
		同一个对象多次调用hashCode()方法返回的哈希值是相同的 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
理解

JDK1.8以前

  • 数组+链表
    1.创建一个默认长度为16,默认加载因子为0.75的数组,数组名为table
    2.根据元素的哈希值跟数组的长度计算出应存入的位置
    3.判断当前位置是否为null,如果为null直接存入
    4.如果位置不为null,表示有元素,则调用equals方法比较属性值
    5.如果一样,则不存,如果不一样,则存入数组,老元素挂在新元素下面

JDK1.8以后

  • 节点个人小于等于8: 数组+ 链表
  • 节点个数多于8个,数组+红黑树
    1.创建一个默认长度为16,默认加载因子为0.75的数组,数组名为table
    2.根据元素的哈希值跟数组的长度计算出应存入的位置
    3.判断当前位置是否为null,如果为null直接存入
    4.如果位置不为null,表示有元素,则调用equals方法比较属性值
    5.如果一样,则不存,如果不一样,则存入数组,老元素挂在新元素下面

总结:HashSet集合存储自定义类型元素,要想实现元素的唯一,要求必须重写hashCode方法和equals方法

Map集合

Map集合概述

interface Map<K,V> K:键的类型;V:值的类型

Map集合的特点

  • 双列集合,一个键对应一个值
  • 键不可以重复,值可以重复

基本功能

  • V put(K key,V value) 添加元素
  • V remove(Object key) 根据键删除键值对元素
  • void clear() 移除所有的键值对元素
  • boolean containsKey(Object key) 判断集合是否包含指定的键
  • boolean containsValue(Object value) 判断集合是否包含指定的值
  • boolean isEmpty() 判断集合是否为空
  • int size() 集合的长度,也就是集合中键值对的个数
Map<String, String> map = new HashMap<>();

        map.put("001", "zhangsan");
        map.put("002", "wangwu");
        map.put("003", "zhaoliu");
        String zhaoliu1 = map.put("001", "zhaoliu1");
        System.out.println(zhaoliu1); // zhangsan
        System.out.println(map);// {001=zhaoliu1, 002=wangwu, 003=zhaoliu}

        String remove = map.remove("001");
        System.out.println(remove);//zhaoliu1
        System.out.println(map);//{002=wangwu, 003=zhaoliu}

        boolean b = map.containsKey("002");
        System.out.println(b);// true

        boolean wangwu = map.containsValue("wangwu");
        System.out.println(wangwu);//true

        boolean empty = map.isEmpty();
        System.out.println(empty);//false

        int size = map.size();
        System.out.println(size); //false

获取功能

  • V get(Object key) 根据键获取值
  • Set keySet() 获取所有键的集合
  • Collection values() 获取所有值的集合
  • Set> entrySet() 获取所有键值对对象的集合
Map<String, String> map = new HashMap<>();

        map.put("001", "zhangsan");
        map.put("002", "wangwu");
        map.put("003", "zhaoliu");
        // 获取所有键的集合
        Set<String> strings = map.keySet();
        for (String string : strings) {
            String s = map.get(string);
            System.out.print(string + "==" + s+"#");// 001==zhangsan#002==wangwu#003==zhaoliu#
        }
        System.out.println();
        // 获取所有的值的集合
        Collection<String> values = map.values();
        for (String value : values) {
            System.out.print(value+" "); // zhangsan wangwu zhaoliu 
        }


Map<String, String> map = new HashMap<>();
        map.put("张无忌", "赵敏");
        map.put("郭靖", "黄蓉");
        map.put("杨过", "小龙女");
        System.out.println(map);

        Set<Map.Entry<String, String>> entries = map.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.print("("+key + ":" + value+")"+"||");//(杨过:小龙女)||(郭靖:黄蓉)||(张无忌:赵敏)||
        }

HashMap集合

  • HashMap底层是哈希表结构的
  • 依赖hashCode方法和equals方法保证键的唯一
  • 如果键要存储的是自定义对象,需要重写hashCode和equals方法

TreeMap集合

  • TreeMap底层是红黑树结构
  • 依赖自然排序或者比较器排序,对键进行排序
  • 如果键存储的是自定义对象,需要实现Comparable接口或者在创建TreeMap对象时候给出比较器排序规则

第一种

TreeMap<Student, String> tm = new TreeMap<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                int result = o2.getAge() - o1.getAge();
                result = result == 0 ? o2.getName().compareTo(o1.getName()) : result;
                return result;
            }
        });

        Student s1 = new Student("xaiohei", 23);
        Student s2 = new Student("dapang", 22);
        Student s3 = new Student("xiaomei", 22);

        tm.put(s1, "江苏");
        tm.put(s2, "北京");
        tm.put(s3, "天津");

        tm.forEach((Student key, String value) -> {
            System.out.println(key + "---" + value);
            /*
            *  Student{name='xaiohei', age=23}---江苏
            *  Student{name='xiaomei', age=22}---天津
            *  Student{name='dapang', age=22}---北京
            * */
        });
第二种
import java.util.Objects;

public class Student implements Comparable<Student>{
	.....

    @Override
    public int compareTo(Student o) {
        int result = this.getAge() - o.getAge();
        result = result == 0 ? this.getName().compareTo(o.getName()) : result;
        return result;
    }
}

可变参数

  • 可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了
  • 方法的参数类型已经确定,个数不确定,我们可以使用可变参数
修饰符 返回值类型 方法名(数据类型… 变量名) { }

不可变集合

在List、Set、Map接口中,都存在of方法,可以创建一个不可变的集合 
	这个集合不能添加,不能删除,不能修改 
	但是可以结合集合的带参构造,实现集合的批量添加 
	
在Map接口中,还有一个ofEntries方法可以提高代码的阅读性 
	首先会把键值对封装成一个Entry对象,再把这个Entry对象添加到集合当中

Stream流

Stream流的思想

获取Stream流==》中间方法 ==》终结方法

生成Stream流的方式

Collection体系集合
使用默认方法stream()生成流, default Stream stream()

Map体系集合
把Map转成Set集合,间接的生成流

数组
通过Arrays中的静态方法stream生成流

同种数据类型的多个数据
通过Stream接口的静态方法of(T... values)生成流
private static void extracted3() {
        //同种数据类型的多个数据
        Stream.of(1,2,3,4,5,6,7).forEach(s -> System.out.println(s));
    }

    private static void extracted2() {
        // 数组
        int[] arr = {1,2,3,4,5};
        Arrays.stream(arr).forEach(s -> System.out.println(s));
    }

    private static void extracted1() {
        // 双列集合
        HashMap<String, Integer> hm = new HashMap<>();
        hm.put("zhangsan", 23);
        hm.put("lisi", 24);
        hm.put("wangwu", 25);
        hm.put("zhaoliu", 26);
        hm.put("qianqi", 27);
        // 间接获取Stream流
        /*keySet
        * 先过去所有的键,
        * 再把这个Set集合中所有的键放到Stream流中
        * */
        hm.keySet().stream().forEach(s -> System.out.println(s));
        /*entrySet
        * 先获取到所有的键值对对象
        * 再把这个Set集合种所有的键值对对象放到Stream流中
        * */
        hm.entrySet().stream().forEach(s -> System.out.println(s));
    }

    private static void extracted() {
        // 单列集合
        ArrayList<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");

        //Stream<String> stream = list.stream();
        //stream.forEach(s -> System.out.println(s));
        list.stream().forEach(s -> System.out.println(s));
    }

Stream流中间操作方法

  • Stream filter(Predicate predicate) 用于对流中的数据进行过滤
  • Stream limit(long maxSize) 返回此流中的元素组成的流,截取前指定参数个数的数据
  • Stream skip(long n) 跳过指定参数个数的数据,返回由该流的剩余元素组成的流
  • static Stream concat(Stream a, Stream b) 合并a和b两个流为一个流
  • Stream distinct() 返回由该流的不同元素(根据Object.equals(Object) )组 成的流
        ArrayList<String> list = new ArrayList<>();
        list.add("林青霞");
        list.add("张曼玉");
        list.add("王祖贤");
        list.add("王祖贤");
        list.add("柳岩");
        list.add("张敏");
        list.add("张无忌");

        list.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                boolean result = s.startsWith("张");
                return result;
            }
        }).forEach(s -> System.out.println(s));
        System.out.println("------");
        list.stream().filter((String s) -> {
            boolean result = s.startsWith("张");
            return result;
        }).forEach(s -> System.out.println(s));
        System.out.println("------");
        //Stream filter(Predicate predicate) 用于对流中的数据进行过滤
        list.stream().filter(s -> s.startsWith("张")).forEach(s -> System.out.println(s));

        System.out.println("------");
        //Stream limit(long maxSize) 返回此流中的元素组成的流,截取前指定参数个数的数据
        list.stream().limit(2).forEach(s -> System.out.println(s));

        System.out.println("------skip");
        //跳过指定参数个数的数据,返回由该流的剩余元素组成的流
        list.stream().skip(2).forEach(s -> System.out.println(s));


        ArrayList<String> list1 = new ArrayList<String>();
        list1.add("林青霞");
        list1.add("张曼玉");
        list1.add("王祖贤");
        list1.add("柳岩");
        list1.add("张敏");
        list1.add("张无忌");
        // static Stream concat(Stream a, Stream b) 合并a和b两个流为一个流
        Stream<String> stream = list.stream();
        Stream<String> stream1 = list1.stream();
        Stream<String> concat = Stream.concat(stream1, stream);
        System.out.println("------concat");
        concat.forEach(s -> System.out.println(s));

        //Stream distinct() 返回由该流的不同元素(根据Object.equals(Object) )组 成的流
        // 依赖hashCode和equals方法
        System.out.println("------distinct");
        list.stream().distinct().forEach(s -> System.out.println(s));

Stream流终结操作方法

  • void forEach(Consumer action) 对此流的每个元素执行操作
  • long count() 返回此流中的元素数
ArrayList<String> list = new ArrayList<String>();
        list.add("林青霞");
        list.add("张曼玉");
        list.add("王祖贤");
        list.add("柳岩");
        list.add("张敏");
        list.add("张无忌");

        long count = list.stream().filter(s -> s.startsWith("张")).count();
        list.stream().filter(s -> s.startsWith("张")).forEach(s -> System.out.println(s));
        System.out.println(count);

Stream流的收集操作

  • public static Collector toList() 把元素收集到List集合中
  • public static Collector toSet() 把元素收集到Set集合中
  • public static Collector toMap(Function keyMapper,Function valueMapper) 把元素收集到Map集合 中
ArrayList<String> list = new ArrayList<>();
        list.add("zhangsan,23");
        list.add("lisi,24");
        list.add("wangwu,25");

        Map<String, Integer> collect = list.stream().filter(s -> {
            String[] split = s.split(",");
            int i = Integer.parseInt(split[1]);
            return i >= 24;
        }).collect(Collectors.toMap(
                s -> {
                    return s.split(",")[0];
                },
                s -> {
                    return Integer.parseInt(s.split(",")[1]);
                }
        ));
        System.out.println(collect);
posted @ 2023-06-01 09:46  牛牛牛牛牛牛牛牛  阅读(10)  评论(0编辑  收藏  举报