集合相关知识汇总

从事Java开发工作阶段的知识分享



前言

凑闲暇之余整理了所有关于集合的知识,会不断更新,帮助自己学习归纳,也希望能够帮助到有需要的人。
总结内容整合复制了各种网络资料,仅供学习
因为也是在不断学习的阶段,如果有不对的地方还希望各位大佬指点。


一、常用的集合类

主要分为三类

Map: HashMap、LinkedHashMap、TreeMap
Set:HashSet、LinkedHashSet、TreeSet
List:ArrayList、LinkedList

二、知识汇总

1.ArrayList和LinkedList内部实现大致是怎样的?他们之间的区别和优缺点?

  • ArrayList:内部使用数组的形式实现了存储,利用数组的下标进行元素的访问,因此对元素的随机访问速度非常快。因为是数组,所以ArrayList在初始化的时候,有初始⼤小10,插入新元素的时候,会判断是否需要扩容,扩容的步长是0.5倍原容量,扩 容方式是利用数组的复制,因此有一定的开销。

  • LinkedList:内部使用双向链表的结构实现存储,LinkedList有一个内部类作为存放元素的单元,⾥面有三个属性,用来存放元 素本身以及前后2个单元的引用,另外LinkedList内部还有一个header属性,用来标识起始位置,LinkedList的第一个单元和最后⼀ 个单元都会指向header,因此形成了一个双向的链表结构

  • ArrayList查找较快,插入、删除较慢,LinkedList查找较慢,插入、删除较快。

2.Hashmap实现原理理?如何保证HashMap线程安全?

a. HashMap简单说就是它根据键的hashCode值存储数据,大多数情况下可以直接定位到它的值,因而具有很快的访问速度, 但遍历顺序却是不确定的。

b. HashMap基于哈希表,底层结构由数组来实现,添加到集合中的元素以“key–value”形式保存到数组中,在数组中key-- value被包装成一个实体来处理—也就是上面Map接口中的Entry。

c. 在HashMap中,Entry[]保存了集合中所有的键值对,当我们需要快速存储、获取、删除集合中的元素时,HashMap会根据hash算法来获得“键值对”在数组中存在的位置,以来实现对应的操作方法。

d. HashMap底层是采⽤数组来维护的.Entry静态内部类的数组

/**
 * The TABLE, resized AS NECESSARY. Length MUST ALWAYS be A power of two.
 */
 TRANSIENT Entry[] TABLE;
 
 STATIC CLASS Entry<K,V> implements MAP.Entry<K,V> {
 	FINAL K key;
	V VALUE;
	Entry<K,V> next;
		FINAL int HASH;
		……
 }

e. HashMap添加元素:将准备增加到map中的对象与该位置上的对象进⾏比较(equals方法),如果相同,那么就将该位置上的那个对象(Entry类型)的value值替换掉,否则沿着该Entry的链继续重复上述过程,如果到链的最后任然没有找到与此对象相同的对象,那么这个 时候就会被增加到数组中,将数组中该位置上的那个Entry对象链到该对象的后面(先hashcode计算位置,如果找到相同位置便替换值,找不到则重复hashcode计算,直到最后在添加到hashmap最后面)

f. HashMap是基于哈希表的Map接口的非同步实现,允许null键值,但不保证映射的顺序;底层使用数组实现,数组中的每项是一个链表;存储时根据key的hash算法来决定其存储位置;数组扩容需要重新计算扩容后每个元素在数组中的位置很耗性能;

g. ConcurrentHashMap是HashMap的线程安全实现,允许多个修改操作同时进行(使用了锁分离技术),它使用了多个锁来控制对hash表的不同段进行的修改,每个段其实就是一个小的hashtable,它们有⾃己的锁。使⽤用了多个子hash表(段Segment),允许多个读操作并发进行,读操作并不不需要锁,因为它的HashEntry几乎是不可变的:

3. HashMap与HashTable的区别

1.线程安全上,hashtable是同步的线程安全;hashmap是非同步的线程不安全,可接受null的值和value(hashtable不允许);

2.对单线程来说,hashTable效率低

3、线程安全的类:vector(比arrayList多了同步机制,效率低不建议使⽤用)、stack(堆栈类,先进后出)、hashtable(比hashmap
多了同步机制)、enumeration(枚举类)

4. 集合删除:

注意: List底层为数组,删除时数组元素下标会被改变

  • 跌代器调用.next()方法时,会检测是否有被修改过
  • 如果要删除集合中的元素一定要用跌代器的remove()方法.

5. hashmap在jdk1.8中的改动

  • Jdk1.8以前是进行四次扰动计算,可能从速度功效各⽅面考虑,jdk1.8变成扰动一次,低16位和高16位进行异或计算。取模的时候考虑取模运算的速度比较慢,改用与操作优化效率,很巧妙,hash table就没设计的这么好。

  • JDK1.8⾥对hashmap最⼤的改变是引入了红黑树,这一点在hash不均匀并且元素个数很多的情况时,对hashmap的性能提升非常⼤。Hashmap的底层实现是使用一个entry数组存储,默认初始⼤小16,不过jdk8换了名字叫node,可能是因为引入了树,叫node更合适吧,另外我也不喜欢entry这个名字,不能望文生义,我在刚学的时候还以为是什么神秘的东⻄呢,其实就是个键值对象而已。Node里有next引用指向下一个节点,因为hashmap解决冲突的思路是拉链法。

  • 另外变化比较大的还有扩容机制,也就是resize方法。

6. Java8实现List转map、分组、过滤等

  • Java8的新特性,可以使一些数据处理更简洁高效
  • 首先,准备一个Fruit类
public class Fruit {
    private Integer id;
    private String name;
    private BigDecimal money;
    private Integer num;
}
  • 然后,添加一些数据
	List<Fruit> fruitList = new ArrayList<>();//存放fruit对象集合

    Fruit fruit1 =  new Fruit(1,"Apple1",new BigDecimal("3.25"),10);
    Fruit fruit12 = new Fruit(1,"Apple2",new BigDecimal("1.35"),20);
    Fruit fruit2 =  new Fruit(2,"banana",new BigDecimal("2.89"),30);
    Fruit fruit3 =  new Fruit(3,"orange",new BigDecimal("9.99"),40);

    fruitList.add(fruit1);
    fruitList.add(fruit12);
    fruitList.add(fruit2);
    fruitList.add(fruit3);

1. 分组

	/**
     * 分组
     * List里面的对象元素,以某个属性来分组,例如,以id分组,将id相同的放在一起:
     * List 以ID分组 Map<Integer,List<Fruit>>
     */
    Map<Integer, List<Fruit>> groupBy = fruitList.stream().collect(Collectors.groupingBy(Fruit::getId));
    System.err.println("groupBy:"+groupBy);

2. List转Map

	/**
      * 2、List转Map
      * id为key,fruit对象为value,可以这么做:
      *
      * List -> Map
      * 需要注意的是:
      * toMap 如果集合对象有重复的key,会报错Duplicate key ....
      * fruit1,fruit12的id都为1。
      * 可以用 (k1,k2)->k1 来设置,如果有重复的key,则保留key1,舍弃key2
      */
    Map<Integer, Fruit> fruitMap = fruitList.stream().collect(Collectors.toMap(Fruit::getId, a -> a,(k1,k2)->k1));

3. 过滤Filter

/**
  * 过滤Filter
  * 
  * 从集合中过滤出来符合条件的元素:
  */
  List<Fruit> filterList = fruitList.stream().filter(a -> a.getName().equals("banana")).collect(Collectors.toList());
  System.err.println("filterList:"+filterList);

4. 求和

  /**
   * 求和
   * 
   * 将集合中的数据按照某个属性求和:
   * 计算 总金额
   */
    BigDecimal totalMoney = fruitList.stream().map(Fruit::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
    System.err.println("totalMoney:"+totalMoney);

5. 去重

import static java.util.Comparator.comparingLong;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toCollection;
/**
  * 去重
  * 根据id去重
  */
  List<Fruit> unique = fruitList.stream().collect(
          collectingAndThen(
                  toCollection(() -> new TreeSet<>(comparingLong(Fruit::getId))), ArrayList::new)
  );
### Java集合框架知识点总结 Java集合框架是一个强大的工具集,提供了多种接口和实现类来存储和操作一组对象。以下是关于Java集合框架的一些重要知识点: #### 1. 集合框架的核心接口 集合框架主要包括以下几个核心接口: - **Collection**:这是集合层次结构中的根接口[^1]。 - **List**:有序集合,允许重复元素。常用实现类有`ArrayList`、`LinkedList`等[^2]。 - **Set**:不包含重复元素的集合。常用实现类有`HashSet`、`TreeSet`等。 - **Queue**:主要用于保存等待处理的对象。常见实现类有`PriorityQueue`等。 #### 2. Map 接口及其实现 除了上述基于单一类型的集合外,还有键值对形式的数据结构——Map: - **Map** 是一种关联数组的形式,其中每个键最多映射到一个值。 - 常见实现类包括 `HashMap` 和 `TreeMap`。 #### 3. 泛型与通配符的应用 为了增强类型安全性和灵活性,在使用集合时通常会结合泛型一起使用。需要注意的是: - 使用 `<T>` 定义具体类型参数可以避免强制类型转换带来的风险。 - 当不确定确切类型时可采用通配符机制,如 `<?>`, `<? extends T>`, 或者 `<? super T>` 来表示不同范围内的类型约束[^5]。 #### 4. 性能考量 不同的集合类适用于不同的场景需求。例如: - 如果需要频繁随机访问,则应优先考虑 `ArrayList`;如果更多涉及增删操作则更适合选用 `LinkedList`。 - 对于哈希表而言,默认初始容量以及负载因子的选择会影响其性能表现。 ```java // 创建并初始化列表实例 List<String> stringList = new ArrayList<>(); stringList.add("Example"); // 利用 HashMap 存储键值对关系 Map<Integer, String> map = new HashMap<>(); map.put(1, "Value"); System.out.println(map.get(1)); ``` #### 5. 数据库交互优化 通过某些高级框架(比如SSM),我们可以进一步简化传统 JDBC 编程过程中繁琐的操作流程,并且还能享受到诸如结果集自动转化为实体对象等功能所带来的便利之处[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值