Java里集合的选择与使用(上半部分)

简介

在Java中,集合是用于存储和操作一组对象的数据结构。Java提供了丰富的集合框架,主要包括(List)列表,集(Set),映射(Map),队列(Queue),先说结论

  • 如果需要随机访问和快速的插入/删除操作,选择 ArrayList

  • 如果需要频繁插入/删除操作,选择 LinkedList

  • 如果不允许重复元素并且顺序很重要,选择 LinkedHashSet 或者 TreeSet

  • 如果需要进行键值对的存储和检索,选择 HashMap 或者 LinkedHashMap

  • 如果需要按照键的顺序存储键值对,选择 TreeMap

下面我们分别介绍。

List(列表)

允许重复元素,有序。常见的实现类有 ArrayListLinkedListVector

ArrayList

ArrayList 是在 Java 编程中常用的集合类之一,它提供了便捷的数组操作,并在动态性、灵活性和性能方面取得了平衡。如果需要频繁在中间插入和删除元素,或者需要在多线程环境中使用,可能需要考虑其他集合实现。

特点

1.动态大小: ArrayList大小是动态可变的,可以根据需要自动增加或缩小。这与 Vector 相似,但相对于 LinkedList,它的随机访问效率更高。

2.随机访问高效 由于 ArrayList 基于动态数组实现,可以通过索引直接访问元素,因此在需要频繁随机访问元素的场景下,ArrayList 的性能通常优于 LinkedList

3.适用于大部分场景: 在大多数情况下,ArrayList 是一个通用、高效的集合类。它适用于存储和随机访问元素,但不适用于在中间或开头频繁插入和删除元素的情况。

4.非同步: ArrayList 不是线程安全的,不支持多线程并发操作。如果需要在多线程环境中使用,可以考虑使用 Vector(这个比较落后了,所以就不提了)使用 Collections.synchronizedList 方法包装 ArrayList

使用方法

首先先引用,然后初始化。

import java.util.ArrayList;//引入ArrayList类

public class Test01 {
    ArrayList<String> objectName = new ArrayList<>();//初始化
}

objectName:对象名。

ArrayList<String>:这<>里面的是泛型数据类型,用于设置 objectName 的数据类型,只能为引用数据类型。

一点示范

import java.util.ArrayList;//引入ArrayList类

public class Test01 {
    public static void main(String[] args) {
        ArrayList<String> objectName = new ArrayList<>();//初始化
        objectName.add("Changsha");//add()
        System.out.println(objectName);
        objectName.add("Shenyang");
        System.out.println(objectName);
        objectName.set(0,"Shanghai");//set()
        System.out.println(objectName);
        System.out.println(objectName.get(1));//get()
        objectName.remove(1);//remove()
        System.out.println(objectName);
    }
}

运行结果

用法表格

1add()将元素插入到指定位置的 arraylist 中
2addAll()添加集合中的所有元素到 arraylist 中
3clear()删除 arraylist 中的所有元素
4clone()复制一份 arraylist
5contains()判断元素是否在 arraylist
6get()通过索引值获取 arraylist 中的元素
7indexOf()返回 arraylist 中元素的索引值
8removeAll()删除存在于指定集合中的 arraylist 里的所有元素
9remove()删除 arraylist 里的单个元素
10size()返回 arraylist 里元素数量
11isEmpty()判断 arraylist 是否为空
12subList()截取部分 arraylist 的元素
13set()替换 arraylist 中指定索引的元素
14sort()对 arraylist 元素进行排序
15toArray()将 arraylist 转换为数组
16toString()将 arraylist 转换为字符串
17ensureCapacity()设置指定容量大小的 arraylist
18lastIndexOf()返回指定元素在 arraylist 中最后一次出现的位置
19retainAll()保留 arraylist 中在指定集合中也存在的那些元素
20containsAll()查看 arraylist 是否包含指定集合中的所有元素
21trimToSize()将 arraylist 中的容量调整为数组中的元素个数
22removeRange()删除 arraylist 中指定索引之间存在的元素
23replaceAll()将给定的操作内容替换掉数组中每一个元素
24removeIf()删除所有满足特定条件的 arraylist 元素
25forEach()遍历 arraylist 中每一个元素并执行特定操作

LinkedList

特点

1.双向链表结构:这是它与ArrayList最主要的差别, LinkedList底层数据结构是双向链表,每个节点都包含对前一个和后一个元素的引用。

2.动态大小: 类似于 ArrayListLinkedList大小也是动态可变的,可以根据需要自动增加或缩小。

3.插入和删除效率高: 由于链表结构,LinkedList 在中间插入和删除元素的操作比 ArrayList 效率更高。因此在任意位置插入或者删除元素时,不需要搬移元素,效率比较高

4.非随机访问效率相对较低:ArrayList 不同,LinkedList随机访问效率相对较低。如果需要频繁随机访问元素,ArrayList 可能更适合。

5.迭代效率: 在迭代时,LinkedList 的性能较差。由于访问节点需要跳跃指针,相比于数组的连续存储,会增加迭代的开销。

6.占用更多内存: 由于每个节点都需要存储额外的引用,相对于 ArrayListLinkedList 在内存占用上可能会更多

7.非同步: LinkedList不是线程安全的,不支持多线程并发操作。如果需要在多线程环境中使用,可以考虑使用 Collections.synchronizedList 方法包装 LinkedList

8.特定场景的优势: 在某些特定的场景中,如实现栈、队列或双端队列等数据结构时,LinkedList 可能更为适用。

使用方法

同样是引用和初始化

import java.util.LinkedList;

public class Test01 {
    public static void main(String[] args) {
        LinkedList<String> linkedList = new LinkedList<>();
        
    }
}

linkedList:对象名

LinkedList<String>:这<>里面的是泛型数据类型,用于设置 linkedList的数据类型,只能为引用数据类型。

一些示范

import java.util.LinkedList;

public class Test01 {
    public static void main(String[] args) {
        LinkedList<String> linkedList = new LinkedList<>();
        linkedList.add("Apple");
        linkedList.add("Banana");
        linkedList.add("Orange");
        System.out.println(linkedList);
// 在指定位置插入元素
        linkedList.add(1, "Grapes");
        System.out.println(linkedList);
        //获取链表中的元素:
        String element = linkedList.get(0);
        System.out.println(element);
        //更新链表中的元素
        linkedList.set(0, "NewElement");
        System.out.println(linkedList);
    }
}

运行结果

常见用法

1

linkedList.add("Element");

在链表末尾添加元素
2linkedList.add(index, "Element");在指定位置插入元素
3linkedList.get(index);获取链表中的元素
4linkedList.set(index, "NewElement");更新链表中的元素
5linkedList.remove(index);删除指定位置的元素
6int size = linkedList.size();获取链表的大小
7linkedList.isEmpty();判断链表是否为空
8linkedList.contains("Element");查找元素是否存在
9linkedList.getFirst();linkedList.getLast();获取第一个和最后一个元素
10linkedList.removeFirst(); linkedList.removeLast();删除第一个和最后一个元素
11

Iterator<String> iterator = linkedList.iterator();

while (iterator.hasNext()) {

String element = iterator.next(); // 处理元素

}

迭代器遍历链表
12

ListIterator<String> iterator = linkedList.listIterator(linkedList.size()); while (iterator.hasPrevious()) {

String element = iterator.previous(); // 处理元素

}

反向遍历链表

HashMap

特点

  1. 键值对存储: HashMap 使用键值对(Key-Value)的方式存储数据。每个键都关联着一个唯一的值。这使得通过键来查找、插入和删除元素非常高效。

  2. 无序性: HashMap 不保证元素的顺序,即元素不会按照插入顺序或其他顺序存储。如果需要有序的键值对集合,可以考虑使用 LinkedHashMap

  3. 允许 null 键和值 HashMap 允许键和值都为 null。这意味着你可以将 null 作为键或值插入到 HashMap 中。

  4. 非同步性: HashMap 不是线程安全的。在多线程环境下,可能需要通过同步措施或者使用 ConcurrentHashMap 等线程安全的集合来确保安全性。

  5. 初始容量和负载因子: HashMap 允许指定初始容量和负载因子。初始容量是 HashMap 在创建时的容量,负载因子是用来控制 HashMap 在容量不足时扩容的程度。

  6. 性能高效: 在非多线程环境下,HashMap 提供快速的查找、插入和删除操作。时间复杂度为 O(1),但在某些情况下,可能会退化为 O(n)。(好的性能需要减少和处理好哈希冲突(链地址法和开放地址法))

  7. 容量动态调整: HashMap 在元素数量达到一定阈值时会自动进行扩容,以保持其性能。负载因子决定了什么时候进行扩容。

  8. 重复键处理: 如果插入了重复的键,新值会覆盖旧值。这意味着同一个键对应的值是唯一的。

使用方法

引用和初始化

import java.util.HashMap;

public class Test01 {
    public static void main(String[] args) {
        //初始化HashMap
        HashMap<String,Integer> hashMap = new HashMap<>();

    }
}

hashMap:对象名

HashMap<String>:这<>里面的是泛型数据类型,用于设置 hashMap的数据类型,只能为引用数据类型。

一些示范

import java.util.HashMap;

public class Test01 {
    public static void main(String[] args) {
        //初始化HashMap
        HashMap<String,Integer> hashMap = new HashMap<>();
        //put(Object key, Object value)
        hashMap.put("one",1);//对应键one放入了一个值1
        hashMap.put("two",2);//对应键two放入了一个值2
        System.out.println(hashMap);
        //get(Object key)
        System.out.println(hashMap.get("one"));//返回与指定键相关联的值,如果该键不存在,则返回null。
        //size()
        System.out.println(hashMap.size());//返回HashMap中键值对的数量。
        //remove(Object key)
        hashMap.remove("one");//删除HashMap中与指定键相关联的值,并返回被删除的值。
        System.out.println(hashMap);
        //clear()
        hashMap.clear();//删除HashMap中所有键值对。
        System.out.println(hashMap);

    }
}

运行结果

常见用法

1put(key, value)插入键值对,将指定的键和值关联起来。如果键已存在,则新值会替换旧值。
2get(key)获取指定键对应的值。如果键不存在,返回 null。
3containsKey(key)检查是否包含指定的键。
4containsValue(value)检查是否包含指定的值。
5remove(key)移除指定键及其关联的值。
6size()返回 HashMap 中键值对的数量。
7isEmpty()检查 HashMap 是否为空。
8keySet()返回包含所有键的 Set 集合。
9values()返回包含所有值的 Collection 集合。
10entrySet()返回包含所有键值对的 Set 集合。
11putAll(map)将另一个 Map 的所有键值对插入到当前 HashMap。
12clear()移除所有键值对,使 HashMap 变为空。

LinkedHashMap

特点

LinkedHashMap的基本特点和HashMap大致相同,主要的不同点是:它采用了一个双向链表保存插入时的顺序。并且,它构造时有一个叫accessOrder的参数,设定为true的时候可以会启用按照访问的顺序排序。通过这个可以实现LRU(Least Recently Used 又称缓存策略),通过这个就可以实现你手机电脑里开了很多程序,内存不够用时,自动移除你前面的进程。(对,你打手游另外开了几个应用,回来发现游戏退了就是因为这个)。

初始化

import java.util.LinkedHashMap;

public class Test01 {
    public static void main(String[] args) {
        LinkedHashMap<String,Integer> linkedHashMap = new LinkedHashMap<>(16, 0.75f,true);
    }
}

三个参数分别为初始容量,负载因子达到多少时扩容,是否按照访问顺序排序

使用方法

下面演示怎么实现简单的LRU缓存策略

import java.util.LinkedHashMap;
import java.util.Map;
//1。首先你要创建一个类继承LinkedHashMap类
public class Test01<K, V> extends LinkedHashMap<K, V> {
    private static final int MAX_ENTRIES = 3; // 设置最大的条目数量

    // 2.调用父类构造函数并传入参数
    public Test01() {
        super(MAX_ENTRIES, 0.75f, true);//这里true就表示可以启用按照访问的顺序排序
    }

    // 3.重写 removeEldestEntry 方法,控制是否移除最旧的条目
    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > MAX_ENTRIES;
    }

    public static void main(String[] args) {
//4.这里就通过Test01类构造这个重写了removeEldestEntry的LinkedHashMap
        Test01<Integer, String> lruCache = new Test01<>();
        lruCache.put(1, "One");
        lruCache.put(2, "Two");
        lruCache.put(3, "Three");

        System.out.println("Initial LinkedHashMap: " + lruCache);
//这里就实现了LRU缓存策略了1,“One”键值对被移除
        lruCache.put(4, "Four"); // 超出最大条目数,会触发 removeEldestEntry 移除最旧的条目

        System.out.println("LRU Cache after adding one more entry: " + lruCache);
    }
}

运行结果

常见用法

大多与HashMap一致,可翻看前面的内容食用,这里列出一些不同的

  1. putIfAbsent(key, value): 如果指定的键尚未与值关联(或映射到 null),则将它与给定的值关联,并返回 null;如果该键已经有与之关联的非 null 值,则不进行任何操作,并返回当前值。

  2. removeEldestEntry(Entry<K,V> eldest): 当重写此方法时,可以在每次添加新条目后删除最旧的条目。这是实现 LRU 缓存的一个常见用法。如果希望限制 LinkedHashMap 的大小,可以重写此方法来返回 true,然后在插入新元素后移除最旧的元素。

  3. replace(oldKey, oldValue, newKey, newValue): 替换指定键和关联值的条目,如果当前键值对匹配给定的键和值。如果不匹配,则不进行替换。

  4. replaceAll(BiFunction<K, V, V> function): 对每个条目的值应用给定的函数,并将结果替换为原始值。

  5. merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction): 将指定键的值与给定值按照给定的 remapping 函数进行合并。如果指定键的值不存在,则将给定值关联到该键;如果指定键的值存在,则使用 remapping 函数计算新值并关联到该键。

先发一半,后面的内容过会发。

  • 14
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值