Collection单列集合(List集合,Set集合),Map双列集合,Stream流


第八节

一、集合

集合分为两类:一类是单列集合元素是一个一个的,另一类是双列集合元素是一对一对的。

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

二、Collection单列集合

在这里插入图片描述

Collection集合特点

List系列集合:添加的元素是有序、可重复、有索引。

  • ArrayList、LinekdList :有序、可重复、有索引。

Set系列集合:添加的元素是无序、不重复、无索引。

  • HashSet: 无序、不重复、无索引;

  • LinkedHashSet: 有序、不重复、无索引。

  • TreeSet:按照大小默认升序排序、不重复、无索引。


  • Collection规定的方法(功能)是全部单列集合都会继承的。

Collection的常见方法:

方法名说明
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()把集合中的元素,存储到数组中
//8.把集合转换为指定类型的数组,可以使用下面的代码
String[] array1 = c.toArray(new String[c.size()]);
System.out.println(Arrays.toString(array1)); //[java1,java2, java2, java3]

三、集合的遍历方式

1.迭代器

概述

  • 迭代器是用来遍历集合的专用方式(数组没有迭代器),在Java中迭代器的代表是Iterator

Collection集合获取迭代器的方法

方法名称说明
Iterator iterator()返回集合中的迭代器对象,该迭代器对象默认指向当前集合的第一个元素

Iterator迭代器中的常用方法

方法名称说明
boolean hasNext()询问当前位置是否有元素存在,存在返回true ,不存在返回false
E next()获取当前位置的元素,并同时将迭代器对象指向下一个元素处。
Collection<String> c = new ArrayList<>();
Iterator<String> it = c.iterator();
while(it.hasNext()){
    String e = it.next();
    System.out.println(s);
}

1.当调用iterator()方法获取迭代器时,当前指向第一个元素
2.hasNext()方法则判断这个位置是否有元素,如果有则返回true,进入循环
3.调用next()方法获取元素,并将当月元素指向下一个位置,
4.等下次循环时,则获取下一个元素,依此内推

2.增强for循环

  • 增强for可以用来遍历集合或者数组。
  • 增强for遍历集合,本质就是迭代器遍历集合的简化写法。
//格式
for(元素的数据类型 变量名 : 数组或者集合) {//在此处使用变量即可,该变量就是元素
}
//
Collection<String> c = new ArrayList<>();
for(String s: c){
    System.out.println(s); 
}

3.forEach遍历集合
常用方法

方法名称说明
default void forEach(Consumer<? super T> action)结合lambda遍历集合
Collection<String> lists = new ArrayList<>();
 lists.forEach(new Consumer<String>() {
   @Override
   public void accept(String s) {
     System.*out*.println(s);
   }
 });
 // 简化
lists.forEach(s -> {
       System.*out*.println(s);
   });
// 继续简化
lists.forEach(s -> System.out.println(s));*

四、List集合

1.List的方法

  • List集合支持索引,Collection的功能List也都继承了。
方法名称说明
void add(int index,E element)在此集合中的指定位置插入指定的元素
E remove(int index)删除指定索引处的元素,返回被删除的元素
E set(int index,E element)修改指定索引处的元素,返回被修改的元素
E get(int index)返回指定索引处的元素

List遍历

  • 普通for循环(List有索引)
  • 迭代器
  • 增强for
  • Lambda表达式

2.ArrayList
ArrayList底层的原理
ArrayList集合底层是基于数组结构实现的,也就是说当你往集合容器中存储元素时,底层本质上是往数组中存储元素。

  • 查询速度快(根据索引查询数据快):查询数据通过地址值和索引定位,查询任意数据耗时相同。
  • 删除效率低:可能需要把后面很多的数据进行前移。
  • 添加效率极低:可能需要把后面很多的数据后移,再添加元素;或者也可能需要进行数组的扩容。

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

ArrayList集合适合的应用场景
ArrayList适合:根据索引查询数据比如根据随机索引取数据(高效)!或者数据量不是很大时!
ArrayList不适合:数据量大的同时又要频繁的进行增删操作!

想要使用ArrayList存储数据,并对数据进行操作:
-第一步:创建ArrayList容器对象。一般使用空参数构造方法。
-第二步:调用ArrayList类的常用方法对容器中的数据进行操作。

构造器说明
public ArrayList()创建一个空的集合对象
常用方法名说明
public boolean add(E e)将指定的元素添加到此集合的末尾
public void add(int index,E element)在此集合中的指定位置插入指定的元素
public E get(int index)返回指定索引处的元素
public int size()返回集合中的元素的个数
public E remove(int index)删除指定索引处的元素,返回被删除的元素
public boolean remove(Object o)删除指定的元素,返回删除是否成功
public E set(int index,E element)修改指定索引处的元素,返回被修改的元素

3. LinkedList
LinkedList底层原理
基于双链表实现的。
特点:查询慢,增删相对较快,但对首尾元素进行增删改查的速度是极快的。
链表中的结点是独立的对象,在内存中是不连续的,每个结点包含数据值和下一个结点的地址。

方法名称说明
public void addFirst(E e)在该列表开头插入指定的元素
public void addLast(E e)将指定的元素追加到此列表的末尾
public E getFirst()返回此列表中的第一个元素
public E getLast()返回此列表中的最后一个元素
public E removeFirst()从此列表中删除并返回第一个元素
public E removeLast()从此列表中删除并返回最后一个元素

LinkedList的应用场景:可设计队列
队列的特点:先进先出,后进后出

五、Set集合

1.Set集合
Set要用到的常用方法,基本上就是Collection提供的
Set系列集合特点:无序:添加数据的顺序和获取出的数据顺序不一致; 不重复; 无索引
HashSet : 无序、不重复、无索引。
LinkedHashSet:有序、不重复、无索引。
TreeSet:排序、不重复、无索引。

2.哈希值
就是一个int类型的数值,ava中每个对象都有一个哈希值。
Java中的所有对象,都可以调用Obejct类提供的hashCode方法,返回该对象自己的哈希值。

public int hashCode():返回对象的哈希码值。

对象哈希值的特点
同一个对象多次调用hashCode()方法返回的哈希值是相同的。
不同的对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞)。

哈希表是一种增删改查数据性能都较好的结构

3.HashSet集合
HashSet集合底层原理
HashSet集合底层是基于哈希表实现的,哈希表根据JDK版本的不同,也是有点区别的

  • JDK8以前:哈希表 = 数组+链表

创建一个默认长度16,默认加载因为0.75的数组,数组名table
② 根据元素的哈希值跟数组的长度计算出应存入的位置
判断当前位置是否为null,如果是null直接存入,如果位置不为null,表示有元素,则调用equals方法比较属性值,如果一样,则不存,如果不一样,则存入数组。
当数组存满到16*0.75=12时,就自动扩容,每次扩容原先的两倍
JDK 8之前,新元素存入数组,占老元素位置,老元素挂下面
JDK 8开始之后,新元素直接挂在老元素下面

HashSet集合中存储元素时,底层调用了元素的两个方法:一个是hashCode方法获取元素hashCode值(哈希值);另一个是调用了元素的equals方法,用来比较新添加的元素和集合中已有的元素是否相同。

  • 只有新添加元素的hashCode值和集合中以后元素的hashCode值相同、新添加的元素调用equals方法和集合中已有元素比较结果为true, 才认为元素重复。
  • 如果hashCode值相同,equals比较不同,则以链表的形式连接在数组的同一个索引为位置

  • JDK8以后:哈希表 = 数组+链表+红黑树
    JDK8开始后,为了提高性能,当链表的长度超过8时,就会把链表转换为红黑树
    在这里插入图片描述
    在这里插入图片描述

HashSet去重原理
HashSet集合默认不能对内容一样的两个不同对象去重复!
比如内容一样的两个学生对象存入到HashSet集合中去, HashSet集合是不能去重复的!
要想保证在HashSet集合中没有重复元素,需要重写元素类的hashCode和equals方法。

4.LinkedHashSet底层原理

  • LinkedHashSet类是HashSet的子类

底层原理
LinkedHashSet底层采用的是也是哈希表(数组,链表,红黑树)结构,只不过额外新增了一个双向链表来维护元素的存取顺序。

特点
有序、不重复、无索引

5.TreeSet集合
特点:不重复、无索引、可排序(默认升序排序 ,按照元素的大小,由小到大排序)
底层是基于红黑树实现的排序。

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

自定义排序规则
TreeSet集合存储自定义类型的对象时,必须指定排序规则,支持如下两种方式来指定比较规则。
方式一
让自定义的类(如学生类)实现Comparable接口,重写里面的compareTo方法来指定比较规则。


public class Student implements Comparable<Student>{
    private String name;
    private int age;
    private double height;
	//无参数构造方法
    public Student(){}
    //全参数构造方法
    public Student(String name, int age, double height){
        this.name=name;
        this.age=age;
        this.height=height;
    }
    //...get、set、toString()方法自己补上..
    
    //按照年龄进行比较,只需要在方法中让this.age和o.age相减就可以。
    /*
    原理:
    在往TreeSet集合中添加元素时,add方法底层会调用compareTo方法,根据该方法的
    结果是正数、负数、还是零,决定元素放在后面、前面还是不存。
    */
    @Override
    public int compareTo(Student o) {
        //this:表示将要添加进去的Student对象
        //o: 表示集合中已有的Student对象
        return this.age-o.age;
    }
}

方式二
通过调用TreeSet集合有参数构造器,可以设置Comparator对象(比较器对象,用于指定比较规则。)

public TreeSet(Comparator<? super E> comparator)
Set<Student> students = new TreeSet<>(new Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2){
        //需求:按照学生的身高排序
        return Double.compare(o1,o2); 
    }
});

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

  • 如果认为第一个元素 > 第二个元素 返回正整数即可。
  • 如果认为第一个元素 < 第二个元素返回负整数即可。
  • 如果认为第一个元素 = 第二个元素返回0即可,此时Treeset集合只会保留一个元素,认为两者重复。

注意:如果类本身有实现Comparable接口,TreeSet集合同时也自带比较器,默认使用集合自带的比较器排序。

6.总结

1、如果希望记住元素的添加顺序,需要存储重复的元素,又要频繁的根据索引查询数据?
用ArrayList集合(有序、可重复、有索引),底层基于数组的。(常用)
2、如果希望记住元素的添加顺序,且增删首尾数据的情况较多?
用LinkedList集合(有序、可重复、有索引),底层基于双链表实现的。
3.、如果不在意元素顺序,也没有重复元素需要存储,只希望增删改查都快?
用HashSet集合(无序,不重复,无索引),底层基于哈希表实现的。 (常用)
4、如果希望记住元素的添加顺序,也没有重复元素需要存储,且希望增删改查都快?
用LinkedHashSet集合(有序,不重复,无索引), 底层基于哈希表和双链表。
5、如果要对元素进行排序,也没有重复元素需要存储?且希望增删改查都快?
用TreeSet集合,基于红黑树实现。

6.Collection的其他操作

可变参数

  • 可变参数是一种特殊的形式参数,定义在方法、构造器的形参列表处,它可以让方法接收多个同类型的实际参数。
  • 可变参数在方法内部,本质上是一个数组
public class ParamTest{
    public static void main(String[] args){
        //不传递参数,下面的nums长度则为0, 打印元素是[]
        test();	
        
        //传递3个参数,下面的nums长度为3,打印元素是[10, 20, 30]
        test(10,20,30); 
        
        //传递一个数组,下面数组长度为4,打印元素是[10,20,30,40] 
        int[] arr = new int[]{10,20,30,40}
        test(arr); 
    }
    
    public static void test(int...nums){
        //可变参数在方法内部,本质上是一个数组
        System.out.println(nums.length);
        System.out.println(Arrays.toString(nums));
        System.out.println("----------------");
    }
}
  • 一个形参列表中,只能有一个可变参数;否则会报错
  • 一个形参列表中如果多个参数,可变参数需要写在最后;否则会报错

Collections工具类
Collections并不是集合,它比Collection多了一个s,一般后缀为s的类很多都是工具类。这里的Collections是用来操作Collection的工具类。

六、Map双列集合

1.Map集合

Map集合中的每一个元素是以key=value的形式存在的,一个key=value就称之为一个键值对,而且在Java中有一个类叫Entry类,Entry的对象用来表示键值对对象。
所有的Map集合的特点:键不能重复,值可以重复,每一个键只能找到自己对应的值。
在这里插入图片描述

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

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

Map<String, Integer> map = new HashMap<>(); // 一行经典代码。 按照键 无序,不重复,无索引。
Map<String, Integer> map = new LinkedHashMap<>(); // 有序,不重复,无索引。

Map是双列集合的祖宗,它的功能是全部双列集合都可以继承过来使用的。

Map的常用方法如下:

方法名称说明
public V put(K key,V value)添加元素
public int size()获取集合的大小
public void clear()清空集合
public boolean isEmpty()判断集合是否为空,为空返回true , 反之
public V get(Object key)根据键获取对应值
public V remove(Object key)根据键删除整个元素
public boolean containsKey(Object key)判断是否包含某个键
public boolean containsValue(Object value)判断是否包含某个值
public Set keySet()获取全部键的集合
public Collection values()获取Map集合的全部值

Map集合遍历方式

方式1:键找值

先获取Map集合全部的键,再通过遍历键来找值

需要用到的方法

方法名称说明
public Set keySet()获取所有键的集合
public V get(Object key)根据键获取其对应的值
Map<String, Double> map = new HashMap<>();
map.put("张三", 162);
map.put("李四", 169);
map.put("王五", 165);
// 1、获取Map集合的全部键
Set<String> keys = map.keySet();
// System.out.println(keys);
// [张三, 李四, 王五]
//key
// 2、遍历全部的键,根据键获取其对应的值
for (String key : keys) {
	// 根据键获取对应的值
	double value = map.get(key);
	System.out.println(key + "=====>" + value);
}

方式2:键值对

把“键值对“看成一个整体进行遍历

Map提供的方法说明
Set<Map.Entry<K, V>> entrySet()获取所有“键值对”的集合
Map.Entry提供的方法说明
K getKey()获取键
V getValue()获取值
Map<String, Double> map = new HashMap<>();
map.put("张三", 162);
map.put("李四", 169);
map.put("王五", 165);
// map = {张三=169, 李四=183, 王五=165}
// entries = [(张三=169), (李四=183), (王五=165)]
// entry = (蜘蛛精=169.8)	...
// 1、调用Map集合提供entrySet方法,把Map集合转换成键值对类型的Set集合
Set<Map.Entry<String, Double>> entries = map.entrySet();
for (Map.Entry<String, Double> entry : entries) {
	String key = entry.getKey();
	double value = entry.getValue();
	System.out.println(key + "---->" + value);
}

方式3:Lambda

方法名称说明
default void forEach(BiConsumer<? super K, ? super V> action)结合lambda遍历Map集合
map.forEach((k , v) -> {
  System.out.println(k +"----->" + v);
 });

2.HashMap
HashMap集合的特点是由键决定的: 键是无序、不能重复,而且没有索引的

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

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

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

HashMap底层数据结构: 哈希表结构
JDK8之前的哈希表 = 数组+链表
JDK8之后的哈希表 = 数组+链表+红黑树
哈希表是一种增删改查数据,性能相对都较好的数据结构

往HashMap集合中键值对数据时,底层步骤如下
第1步:当你第一次往HashMap集合中存储键值对时,底层会创建一个长度为16的数组
第2步:把键然后将键和值封装成一个对象,叫做Entry对象
第3步:再根据Entry对象的键计算hashCode值(和值无关)
第4步:利用hashCode值和数组的长度做一个类似求余数的算法,会得到一个索引位置
第5步:判断这个索引的位置是否为null,如果为null,就直接将这个Entry对象存储到这个索引位置
如果不为null,则还需要进行第6步的判断
第6步:继续调用equals方法判断两个对象键是否相同
如果equals返回false,则以链表的形式往下挂
如果equals方法true,则认为键重复,此时新的键值对会替换就的键值对。

HashMap底层需要注意这几点:
1.底层数组默认长度为16,如果数组中有超过12个位置已经存储了元素,则会对数组进行扩容2倍
数组扩容的加载因子是0.75,意思是:16*0.75=12
2.数组的同一个索引位置有多个元素、并且在8个元素以内(包括8),则以链表的形式存储
JDK7版本:链表采用头插法(新元素往链表的头部添加)
JDK8版本:链表采用尾插法(新元素我那个链表的尾部添加)
3.数组的同一个索引位置有多个元素、并且超过了8个,则以红黑树形式存储

HashMap底层是基于哈希表实现的
HashMap集合是一种增删改查数据,性能都较好的集合
但是它是无序,不能重复,没有索引支持的(由键决定特点)
HashMap的键依赖hashCode方法和equals方法保证键的唯一
如果键存储的是自定义类型的对象,可以通过重写hashCode和equals方法,这样可以保证多个对象内容一样时,HashMap集合就能认为是重复的。

3.LinkedHashMap
特点:LinkedHashMap集合的特点也是由键决定的:有序的(键存储和取出的顺序是一样的)、不重复、无索引

LinkedHashMap的底层原理,和LinkedHashSet底层原理是一样的。底层多个一个双向链表来维护键的存储顺序。

4.TreeMap
特点:由键决定的,默认按照键的升序排列,键不重复,也是无索引的。

TreeMap集合的底层原理和TreeSet也是一样的,底层都是红黑树实现的。所以可以对键进行排序。

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

七、集合嵌套

// 1、定义一个Map集合存储全部的省份信息,和其对应的城市信息。
Map<String, List<String>> map = new HashMap<>();
List<String> cities1 = new ArrayList<>();
Collections.addAll(cities1, "南京市","扬州市","苏州市" ,"无锡市","常州市");
map.put("江苏省", cities1);

八、Stream

什么是Stream

Stream流,是Jdk8开始新增的一套API (java.util.stream.*),可以用于操作集合或者数组的数据。
优势: Stream流大量的结合了Lambda的语法风格来编程,提供了一种更加强大,更加简单的方式操作集合或者数组中的数据,代码更简洁,可读性更好。

Stream流的使用步骤
1.先得到集合或者数组的Stream流
2.然后调用Stream流的方法对数据进行处理。
3.获取处理的结果。在这里插入图片描述获取Stream流

获取集合的Stream流

Collection提供的如下方法说明
default Stream stream()获取当前集合对象的Stream流

获取数组的Stream流

Arrays类提供的如下 方法说明
public static Stream stream(T[] array)获取当前数组的Stream流
Stream类提供的如下 方法说明
public static Stream of(T… values)获取当前接收数据的Stream流
// 1、如何获取List集合的Stream流?
List<String> names = new ArrayList<>();
Collections.addAll(names, "张三","李四","王五");
Stream<String> stream = names.stream();

// 2、如何获取Set集合的Stream流?
Set<String> set = new HashSet<>();
Collections.addAll(set, "张三","李四","王五");
Stream<String> stream1 = set.stream();
stream1.filter(s -> s.contains("三")).forEach(s -> System.out.println(s));

// 3、如何获取Map集合的Stream流?
Map<String, Double> map = new HashMap<>();
map.put("张三", 172);

Set<String> keys = map.keySet();
Stream<String> ks = keys.stream();

Collection<Double> values = map.values();
Stream<Double> vs = values.stream();

Set<Map.Entry<String, Double>> entries = map.entrySet();
Stream<Map.Entry<String, Double>> kvs = entries.stream();
kvs.filter(e -> e.getKey().contains("三"))
	.forEach(e -> System.out.println(e.getKey()+ "-->" + e.getValue()));

// 4、如何获取数组的Stream流?
String[] names2 = {"张三","李四","王五"};
Stream<String> s1 = Arrays.stream(names2);
Stream<String> s2 = Stream.of(names2);

中间方法指的是:调用完方法之后其结果是一个新的Stream流,于是可以继续调用方法,这样一来就可以支持链式编程

Stream****提供的常用中间方法说明
Stream < T > filter(Predicate<? super T> predicate)用于对流中的数据进行过滤。
Stream < T > sorted()对元素进行升序排序
Stream < T >sorted(Comparator<? super T> comparator)按照指定规则排序
Stream< T > limit(long maxSize)获取前几个元素
Stream< T > skip(long n)跳过前几个元素
Stream< T > distinct()去除流中重复的元素。
< R >Stream< R > map(Function<? super T ,? extends R> mapper)对元素进行加工,并返回对应的新流
static < T > Stream< T > concat(Stream a, Stream b)合并a和b两个流为一个流

终结方法指的是调用完成后,不会返回新Stream了,没法继续使用流了。

Stream提供的常用终结方法说明
void forEach(Consumer action)对此流运算后的元素执行遍历
long count()统计此流运算后的元素个数
Optional < T > max(Comparator<? super T> comparator)获取此流运算后的最大值元素
Optional< T> min(Comparator<? super T> comparator)获取此流运算后的最小值元素

收集Stream流:就是把Stream流操作后的结果转回到集合或者数组中去返回。
Stream流:方便操作集合/数组的手段; 集合/数组:才是开发中的目的。

Stream提供的常用终结方法说明
R collect(Collector collector)把流处理后的结果收集到一个指定的集合中去
Object[] toArray()把流处理后的结果收集到一个数组中去
Collectors工具类提供了具体的收集方式说明
public static Collector toList()把元素收集到List集合中
public static Collector toSet()把元素收集到Set集合中
public static Collector toMap(Function keyMapper , Function valueMapper)把元素收集到Map集合中
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值