Java集合

Java集合框架

集合是用来进行数据存储的容器, 是数据存储的工具类
集合是一个比数组更加强大的容器,和我们自己写的动态数组类似
集合的容器大小是可变的、会自动进行扩容
集合是可以嵌套的, 可以构建更加复杂的数据结构
集合在 java.util 包
集合支持泛型, 泛型主要应用于集合(在集合的学习和使用中,推荐使用泛型)

Java集合的分类

  • 单值集合 Collection : 集合中存储的数据是单值格式
  • 键值对集合 Map : 集合中存储的数据是键值对(数据由2部分组成,键和值)结构

Collection(接口)集合

特点: 存储值的结构是单个的
实现了 Iterable 接口,意味着 可以使用 迭代器、 增强for循环 、 Jdk8 forEach 来进行数据的迭代遍历

Collection 接口中定义的方法

  • size() : 获取集合的长度
  • isEmpty() : 判断 集合中的内容是否为空
  • contains(obj) : 判断传入的 obj 对象是否在集合中存在
  • iterator() : 获取 迭代器 (为了数据遍历)
  • toArray() : 将一个集合转成Object数组
  • toArray(T[]) : 将一个集合转成 T[]
  • add(ele) : 将数据添加到集合的尾部,返回boolean
  • remove(obj) : 删除某一个指定的对象,返回boolean
  • addAll(Collection<? extends E>) : 将一个同类型集合内容添加到当前集合中
  • removeIf(Predicate filter) : 根据条件删除指定集合中的内容
  • clear() : 清空集合
  • stream() : 可以将一个集合转成Steam流,进行可以进行流式处理

Collection集合中定义的方法都和索引没有关系, 因为 Collection 集合 包含Set(和索引不相关的)

Collection 中常见的子类

  • List : 存储单值、有序的(按照存放的顺序,和索引有关系)、数据可重复
  • Set : 存储单值、无序的(不是按照存放的顺序,没有索引相关的操作)、数据不可重复(底层对数据进行判断,如果已存在,则不添加)
List 集合常见的成员方法
  • add(int index, E element) : 向指定的位置添加元素,返回 void

add(ele)方法是 Collection 接口中定义的, Collection包含 List 和 Set, 因为 Set不可重复的,所以 add方法需要通过返回 boolean 来标记是否存储成功
add(int index, E element) 方法是 List中定义的,List 和 有序且 可重复的,所以可以保证数据一定能存入,所以不需要返回 boolean 来标记

  • remove(int index) : 删除指定位置的元素、并返回删除的元素

  • indexOf(obj) : 判断元素在集合中第一次出现的索引位置,如果找不到,则返回 -1

  • lastIndexOf(obj) : 判断元素在集合中最后一次出现的索引位置,如果找不到,则返回 -1

  • subList(from, to) : 截取List集合指定区间内的数据、返回一个截取后的新集合

  • listIterator(): 返回一个基于List集合的迭代器(是Iterator的子类、比Iterator更强大、支持正序和逆序遍历)

  • get(index) : 获取指定位置的元素

  • set(index, ele) : 修改指定位置的元素

  • sort(Comparator) : 进行集合的排序

List集合中提供的方法都可 索引相关

List 集合中常见的静态方法
  • of(E… ele) : 将多个元素快速添加到List集合中,返回一个不可修改的只读 集合

  • copyOf(Collection<? extends E>) : 快速将一个集合构建成 不可修改的只读集合

List 常见的实现类

单值、有序、可重复

  • ArrayList :

底层采用数组进行存储,根据索引查询数据、效率非常高,时间复杂度 O(1), 添加和删除效率较慢、时间复杂度是O(n)

  • LinkedList:

采用链式存储的集合容器, 根据索引查询数据、效率较低,时间复杂度O(n), 添加和删除效率非常高、时间复杂度O(1)

  • CopyOnWriteArrayList:

一个基于高并发操作的集合类、适用于多线程环境

List 常见的遍历方式
  1. fori 遍历
for(int i= 0 ; i < list.size() ; i++ ) {
    list.get(i) ; 
}
  1. 迭代器 iterator 遍历
Iterator it = list.iterator() ;

while(it.hasNext()) {
    it.next(); 
}

  1. 增强for循环
for(Type var : list) {
    // var 
}
  1. JDK8 forEach 循环
list.forEach(s -> System.out.println(s)) ;
  1. ListIterator 迭代器

正序遍历 参考 Iterator ,用法和 Iterator 相同
逆序遍历

ListIterator it = list.listIterator( list.size() ); 

while(it.hasPrevious()) {
    
    it.previous(); 
}

思考题: 现有一个单链的容器、已知某一个节点(非尾节点),但不知道链的首节点, 如果删除 已知的某个节点

public void removeNode(Node<E> node) {

    // 获取 下一个节点 
    Node<E> a = node.next ; 
    
    Node<E> b = a.next ; 
    // 删除 a 节点
    node.value = a.value ; 
    // 将 a 从连上断开
    a.next = null ; 
    node.next = null ;
    // 维护新链表 
   node.next = b ;

}
Set 集合

存放单值、存储的数据是无序的(可能和用户存储的顺序不一致)、且数据不可重复
Set 集合 可以对元素进行去重

Set 集合常见的方法

参考他的父类 Collection

Set集合常见的静态方法

Set.of(…e) : 快速构建一个 Set集合,返回一个不可修改的只读 集合

Set.copyOf(Collection<? extends E>) : 将一个集合转成 Set 集合,返回一个不可修改的只读 集合

Set 集合常见的子类
  • HashSet : 数据无序、且不可重复、采用散列表(存储的方式和hash算法有关系)进行数据存储

  • TreeSet: 数据会自动排序、数据不可重复

  • CopyOnWriteArraySet : 支持高并发环境下的一个Set容器

  • LinkedHashSet : 拥有链表结构和 散列表结构的集合

Set 集合常见的遍历方式
  • 迭代器
  • 增强for循环
  • foreach
HashSet 去重的原理

HashSet 底层是采用 HashMap 实现的

p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))

如果在添加数据的时候,数据的 hashCode 相同 且 内容相同,则会自动去重

HashSet 无序的实现手段
i = (n - 1) & hash

(n - 1) & hash 得到的结果是 0 ~ N-1, 作为 底层数组的索引,来存放元素
根据传入的内容的,计算它的 hash值, 在根据 (n - 1) & hash 得到数据的存储索引位置,而该位置是没有顺序的,只能保证不超过底层数据的索引范围

重写 hashCode 的原则

在 重写 hashCode 的时候,我们通常需要保证 内容相同的 对象,hash值一定相同, 内容不同,那么要尽可能的保证 hash值不相同

Hash冲突的时候,HashSet 是如何解决的

当 两个对象 经过 hash算法 如果得到的结果是一样的,那么 就会产生 Hash冲突
如果发生 hash冲突,那么就会将 新的对象 放到 链表的尾部

TreeSet 排序的原理

TreeSet 存储的对象,默认需要 实现 Comparable 接口, 也可以在构建 TreeSet的时候传入一个 Comparator 对象,设置排序规则
TreeSet 在使用的时候,必须保证 要么存储的对象实现Comparable 接口,要么在构建TreeSet的时候传入Comparator 对象, 二者至少出现一个,
如果二者都不存在,则在运行的时候,会抛出 ClassCastException (类型转换异常)

TreeSet 去重的原理

TreeSet 的去重原理是通过 compareTo 方法进行去重的

Map<K, V> 集合

以键值对结构进行数据存储

如何用程序表示一个用户 ???

a) 定义一个 表示用户的类 、在类中提供 用户相关的 属性
b) 通过 new 关键字 创建一个 该类的对象, 并给 对象中的属性 赋值

可以使用一个 Map 容器 来表示一个用户(对象)

name : zhangsan
sex : 男
age : 18

Map 常见的方法

  • size() : 获取 集合的大小

  • isEmpty() : 判断集合中的内容是否为空

  • containsKey(key) : 判断集合中是否存在某个键

  • containsValue(val) : 判断集合中是否存在某个值

  • put(k ,v) : 将一个键和值 存入到Map容器中, 如果键存在,则覆盖

  • putAll(Map<? extends Key, ? extends V> map) : 合并 map 容器

  • putIfAbsent(K, V) : 当键不存在或者 值为 null, 则 放入 map容器

  • V remove(key) : 根据键删除对应的值

  • V get(key) : 根据键获取对应的值

  • V getOrDefault(key, defaultValue) : 如果键存在,则返回对应的value, 否则 使用 defaultValue 作为结果

  • clear() : 清空集合

Map 的常见实现类

  • HashMap : 键不可重复、且无序 , 值没有要求
  • TreeMap : 键不可重复、会自动排序、值没有要求
  • ConcurrentHashMap : 一个基于高并发的HashMap, 应用于多线程环境

Map的遍历

  • keySet() : 获取 map 中所有的键、并返回一个 Set集合

键遍历

 Set<String> strings = map.keySet();
// 遍历 Set
for(String key : strings) {
    System.out.println("key=" + key+ ", value=" + map.get(key));
}

  • values() : 获取 Map 中所有的值 ,并返回一个 Collection 集合

值遍历 : 缺陷(无法通过值 获取对应的键)

Collection<Integer> values = map.values(); 

for(Integer val : values) {
    System.out.println(value=" + val);
}
  • entrySet() : 获取Map集合的所有键值对, 并返回一个 Set集合

键值对遍历

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

for (Map.Entry<String, Integer> entry : entries) {

    System.out.println(entry.getKey());
    System.out.println(entry.getValue());
}

  • JDK8 forEach遍历
map.forEach((k, v) -> System.out.println(k + "=" + v));

HashMap底层实现原理

HashMap 底层采用了三种数据结构 (数组 + 链表 + 红黑二叉树)
参考文档

HashMap 底层涉及的细节
  1. 散列表table它的默认初始大小是 16 (为什么16???)

16 是2的四次方、是补一个不算大、也不算小的值, 在散列表中计算key的索引通过 (n-1)&hash 公式、为了保证计算的索引不会超过散列表长度

  1. 散列表 table 为什么每次扩容1倍

在散列表中计算key的索引通过 (n-1)&hash 公式,

hash
00010011100101
1111

      0101  ---> index ---> 索引的目的是为了找到 散列表中的元素 

扩容后

00010011100101
11111

     00101  ---> index ----> 扩容后的索引 是 原来的索引 + 扩容的长度  或者 就是 原来的索引 

当在散列表中进行扩容的时候,原来存储在散列中的元素,要么保持原来的位置不动,要么他的新位置 = 原位置 + 扩容的长度
可以尽可能的减少原来散列表中元素的移动次数

  1. HashMap 的扩容因子 0.75 , 它的作用是用来计算最大阀值 = 0.75 * 集合的长度,最大阀值是为了更好的扩容
TreeMap的使用
Hashtable

线程安全的、性能比较低
键和值都不能为 null
底层采用 数组 + Entry 链表

Properties

是 Hashtable 的子类
Properties 是 Property的复数
用来存储的键和值都是字符串类型

Properties 可以用来获取操作系统的环境变量
Properties p = System.getProperties() ;

// 获取 操作系统的名称 

String val = p.getProperty("os.name") ; 

// 添加/修改 某个键的值

p.setProperty(a, b) ; // a , b 都是字符串

Properties 可以专门用来解析 properties(Java语言的配置)文件

properties配置文件 可以放在任意位置, 但通常我们项目中的配置文件放在 classpath 下

工程的 classpath 有如下几个位置

  1. src 所在的目录 (存放java源代码)
  2. resources 所在的目录 (推荐存放的位置)
  3. lib (一般用来存放第三方依赖jar)
  • 采用 类对象 加载 配置文件

类对象加载 资源 默认是从 类所在的位置 加载, 可以 通过 / 强制它从 classpath的根下加载

#  '/' 代表 虚拟路径,指的是从 classpath 位置开始查找文件
#  如果 文件地址 没有以 / 开头, 则 代表 从 类对象所在的类的位置 查找 配置文件
#  如果 文件地址 没有以 / 开头, 则代表是一个相对路径( 相对于 类对象所在的类的位置 的路径  ./ 代表当前位置 ,  ../ 当前上一层目录 )

InputStream in = Test.class.getResourceAsStream("/bean.properties"); 

Properties p = new Properties() ;

p.load(in) ;

  • 采用 类对象的 加载器 加载配置文件

ClassLoader(加载器) 只能从 classpath的根下读取资源

InputStream in = Test.class.getClassLoader().getResourceAsStream("bean.properties"); 

Properties p = new Properties() ;

p.load(in) ;

Optional 类

主要用来解决空指针异常

String s = null ;
s.length(); // —> if (s !=null) s.length();

常见的静态方法
  • Optional.of(e) : 快速将 e 放入到 Optional容器中, 此时的 e 不允许为空,如果为 空,则抛出异常

  • Optional.ofNullable(e) : 快速将 e 放入到 Optional容器中, e 允许为空,如果为空,则返回一个 包含 null 值的 Optional

常见的成员方法

get() : 获取 Optional存放的数据、但如果值为null, 则抛出异常

orElse(defaultValue) : 获取 Optional存放的数据,如果值为null, 则 获取 默认值

isPresent() : 判断 Optional 中存放的内容 不为空

isEmpty() : 判断 Optional 中存放的内容为空

orElseThrow(Supplier) : 获取 Optional的值,如果为空,则抛出一个自己设计的异常

ifPresentOrElse(Consumer, Runnable) : 如果Optional的值不为空,则使用Consumer消费,否则 通过Runnable 进行处理

  • Optional.ofNullable(e) : 快速将 e 放入到 Optional容器中, e 允许为空,如果为空,则返回一个 包含 null 值的 Optional
常见的成员方法

get() : 获取 Optional存放的数据、但如果值为null, 则抛出异常

orElse(defaultValue) : 获取 Optional存放的数据,如果值为null, 则 获取 默认值

isPresent() : 判断 Optional 中存放的内容 不为空

isEmpty() : 判断 Optional 中存放的内容为空

orElseThrow(Supplier) : 获取 Optional的值,如果为空,则抛出一个自己设计的异常

ifPresentOrElse(Consumer, Runnable) : 如果Optional的值不为空,则使用Consumer消费,否则 通过Runnable 进行处理

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值