Java常用集合

1. Collection

在这里插入图片描述

1.1 Set

定义

Set集合不允许包含相同的元素,如果试图将两个相同的元素加入同一个Set集合中,则添加操作失败,add()方法返回false,新元素不会被加入。

上面的内容,完全适用于HashSet、TreeSet和EnumSet三个实现类。

(1) 常用方法
Modifier and TypeMethod and Description
boolean**add(E e) **如果指定的元素不存在,则将其指定的元素添加(可选操作)。
boolean**addAll(Collection<? extends E> c) **将指定集合中的所有元素添加到此集合(如果尚未存在)(可选操作)。
voidclear() 从此集合中删除所有元素(可选操作)。
booleancontains(Object o) 如果此集合包含指定的元素,则返回 true
booleancontainsAll(Collection<?> c) 返回 true如果此集合包含所有指定集合的元素。
booleanequals(Object o) 将指定的对象与此集合进行比较以实现相等。
inthashCode() 返回此集合的哈希码值。
booleanisEmpty() 如果此集合不包含元素,则返回 true
Iterator<E>iterator() 返回此集合中元素的迭代器。
booleanremove(Object o) 如果存在,则从该集合中删除指定的元素(可选操作)。
booleanremoveAll(Collection<?> c) 从此集合中删除指定集合中包含的所有元素(可选操作)。
booleanretainAll(Collection<?> c) 仅保留该集合中包含在指定集合中的元素(可选操作)。
intsize() 返回此集合中的元素数(其基数)。
default Spliterator<E>spliterator() 在此集合中的元素上创建一个 Spliterator
Object[]toArray() 返回一个包含此集合中所有元素的数组。
<T> T[]toArray(T[] a) 返回一个包含此集合中所有元素的数组; 返回的数组的运行时类型是指定数组的运行时类型。
(2) 创建Set
Set<String> set = new HashSet<>();
/* 方法一:通过add()方法添加元素 */
set.add("小勇子");
set.add("一定要好好加油啊!");
System.out.println(set);

/* 方法二:addAll() 批量添加元素 */
String[] str = {"小明", "小红", "小黄"};
// 将Array数组转换为集合
List<String> strList = Arrays.asList(str);
set.addAll(strList);
System.out.println(set);

// 和上面是等价的
List<String> strList2 = new ArrayList<>(Arrays.asList("White", "Red", "Yellow"));
set.addAll(strList2);
System.out.println(set);

/* 方法三:Collection.addAll() 直接将数组添加进集合里 */
String[] numStr = {"1", "2", "3"};
Collections.addAll(set, numStr);
System.out.println(set);

注意这里的Arrays.asList() 使用!

(3) 遍历Set
/* 方法一: 集合类通用遍历方式,用迭代器迭代 */
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

/* 与上面的方法等价 */
for (Iterator it = set.iterator(); it.hasNext(); ) {
    System.out.println(it.next());
}

/* for循环遍历 */
for (String val : set) {
    System.out.println(val);
}
1.1.1 HashSet类

在这里插入图片描述

1.1.1.1 LinkedHahsSet类

在这里插入图片描述

1.1.2 TreeSet类

在这里插入图片描述

1.1.3 EnumSet类

在这里插入图片描述

1.2 List

在这里插入图片描述

在集合类中,List是最基础的一种集合:它是一种有序列表。

List的行为和数组几乎完全相同:List内部按照放入元素的先后顺序存放,每个元素都可以通过索引确定自己的位置,List的索引和数组一样,从0开始。

数组和List类似,也是有序结构,如果我们使用数组,在添加和删除元素的时候,会非常不方便。例如,从一个已有的数组{'A', 'B', 'C', 'D', 'E'}中删除索引为2的元素:

┌───┬───┬───┬───┬───┬───┐
│ A │ B │ C │ D │ E │   │
└───┴───┴───┴───┴───┴───┘
              │   │
          ┌───┘   │
          │   ┌───┘
          │   │
          ▼   ▼
┌───┬───┬───┬───┬───┬───┐
│ A │ B │ D │ E │   │   │
└───┴───┴───┴───┴───┴───┘

这个“删除”操作实际上是把'C'后面的元素依次往前挪一个位置,而“添加”操作实际上是把指定位置以后的元素都依次向后挪一个位置,腾出来的位置给新加的元素。这两种操作,用数组实现非常麻烦。

参考:https://www.liaoxuefeng.com/wiki/1252599548343744/1265112034799552

1.2.1 ArrayList类

因此,在实际应用中,需要增删元素的有序列表,我们使用最多的是ArrayList。实际上,ArrayList在内部使用了数组来存储所有元素。例如,一个ArrayList拥有5个元素,实际数组大小为6(即有一个空位):

size=5
┌───┬───┬───┬───┬───┬───┐
│ A │ B │ C │ D │ E │   │
└───┴───┴───┴───┴───┴───┘

当添加一个元素并指定索引到ArrayList时,ArrayList自动移动需要移动的元素:

size=5
┌───┬───┬───┬───┬───┬───┐
│ A │ B │   │ C │ D │ E │
└───┴───┴───┴───┴───┴───┘

然后,往内部指定索引的数组位置添加一个元素,然后把size1

size=6
┌───┬───┬───┬───┬───┬───┐
│ A │ B │ F │ C │ D │ E │
└───┴───┴───┴───┴───┴───┘

继续添加元素,但是数组已满,没有空闲位置的时候,ArrayList先创建一个更大的新数组,然后把旧数组的所有元素复制到新数组,紧接着用新数组取代旧数组:

size=6
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ A │ B │ F │ C │ D │ E │   │   │   │   │   │   │
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘

现在,新数组就有了空位,可以继续添加一个元素到数组末尾,同时size1

size=7
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ A │ B │ F │ C │ D │ E │ G │   │   │   │   │   │
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘

可见,ArrayList把添加和删除的操作封装起来,让我们操作List类似于操作数组,却不用关心内部元素如何移动。

(1) 常用方法

我们考察List<E>接口,可以看到几个主要的接口方法:

  • 在末尾添加一个元素:boolean add(E e)
  • 在指定索引添加一个元素:boolean add(int index, E e)
  • 删除指定索引的元素:int remove(int index)
  • 删除某个元素:int remove(Object e)
  • 获取指定索引的元素:E get(int index)
  • 获取链表大小(包含元素的个数):int size()

但是,实现List接口并非只能通过数组(即ArrayList的实现方式)来实现,另一种LinkedList通过“链表”也实现了List接口。在LinkedList中,它的内部每个元素都指向下一个元素:

        ┌───┬───┐   ┌───┬───┐   ┌───┬───┐   ┌───┬───┐
HEAD ──>│ A │ ●─┼──>│ B │ ●─┼──>│ C │ ●─┼──>│ D │   │
        └───┴───┘   └───┴───┘   └───┴───┘   └───┴───┘

我们来比较一下ArrayListLinkedList

ArrayListLinkedList
获取指定元素速度很快需要从头开始查找元素
添加元素到末尾速度很快速度很快
在指定位置添加/删除需要移动元素不需要移动元素
内存占用较大

通常情况下,我们总是优先使用ArrayList

(2) 创建List
  • 使用ArrayListLinkedList

  • List接口可以添加重复的元素

  • List中允许添加null

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("apple"); // size=1
        list.add("pear"); // size=2
        list.add("apple"); // 允许重复添加元素,size=3
      	list.add(null); // size=4
        System.out.println(list.size());
        String second = list.get(3); // null
        System.out.println(second);
    }
}
  • 还可以通过List接口提供的of()方法,根据给定元素快速创建List
List<Integer> list = List.of(1, 2, 5);

但是List.of()方法不接受null值,如果传入null,会抛出NullPointerException异常。

(3) 遍历List
  • **方法一:**使用for循环根据索引配合get(int)方法遍历:
import java.util.List;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
      	// 方法一:使用for循环,根据索引配合get(int)方法遍历
        List<String> list = Arrays.asList("apple", "pear", "banana");
        for (int i=0; i<list.size(); i++) {
            String s = list.get(i);
            System.out.println(s);
        }
    }
}

但这种方式并不推荐,一是代码复杂,二是因为get(int)方法只有ArrayList的实现是高效的,换成LinkedList后,索引越大,访问速度越慢。

  • 方法二: 使用迭代器Iterator来访问List。(下面第二个代码块常用!)

Iterator本身也是一个对象,但它是由List的实例调用iterator()方法的时候创建的。Iterator对象知道如何遍历一个List,并且不同的List类型,返回的Iterator对象实现也是不同的,但总是具有最高的访问效率。

Iterator对象有两个方法:boolean hasNext()判断是否有下一个元素,E next()返回下一个元素。因此,使用Iterator遍历List代码如下:

import java.util.Iterator;
import java.util.List;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
      	// 这种方式不是平时最常用的,下面的才是
        List<String> list = Arrays.asList("apple", "pear", "banana");
        for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
            String s = it.next();
            System.out.println(s);
        }
    }
}

有童鞋可能觉得使用Iterator访问List的代码比使用索引更复杂。但是,要记住,通过Iterator遍历List永远是最高效的方式。并且,由于Iterator遍历是如此常用,所以,Java的for each循环本身就可以帮我们使用Iterator遍历。把上面的代码再改写如下:

import java.util.List;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
      	// foreach循环进行遍历是平时最常用的
        List<String> list = Arrays.asList("apple", "pear", "banana");
        for (String s : list) {
            System.out.println(s);
        }
    }
}

实际上,只要实现了Iterable接口的集合类都可以直接用for each循环来遍历,Java编译器本身并不知道如何遍历集合对象,但它会自动把for each循环变成Iterator的调用,原因就在于Iterable接口定义了一个Iterator<E> iterator()方法,强迫集合类必须返回一个Iterator实例。

(4) List 转换为 Array

List变为Array有三种方法。

  • 第一种是调用toArray()方法直接返回一个Object[]数组.
import java.util.List;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("apple", "pear", "banana");
        Object[] array = list.toArray();
        for (Object s : array) {
            System.out.println(s);
        }
    }
}

这种方法会丢失类型信息,所以实际应用很少。

  • 第二种方式是给toArray(T[])传入一个类型相同的ArrayList内部自动把元素复制到传入的Array中:
import java.util.List;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(12, 34, 56);
        Integer[] array = list.toArray(new Integer[3]);
        for (Integer n : array) {
            System.out.println(n);
        }
    }
}

注意到这个toArray(T[])方法的泛型参数<T>并不是List接口定义的泛型参数<E>,所以,我们实际上可以传入其他类型的数组,例如我们传入Number类型的数组,返回的仍然是Number类型:

import java.util.List;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(12, 34, 56);
        Number[] array = list.toArray(new Number[3]);
        for (Number n : array) {
            System.out.println(n);
        }
    }
}

但是,如果我们传入类型不匹配的数组,例如,String[]类型的数组,由于List的元素是Integer,所以无法放入String数组,这个方法会抛出ArrayStoreException

如果我们传入的数组大小和List实际的元素个数不一致怎么办?根据List接口的文档,我们可以知道:

如果传入的数组不够大,那么List内部会创建一个新的刚好够大的数组,填充后返回;如果传入的数组比List元素还要多,那么填充完元素后,剩下的数组元素一律填充null

实际上,最常用的是传入一个“恰好”大小的数组:

Integer[] array = list.toArray(new Integer[list.size()]);
  • 第三种:更简洁的写法是通过List接口定义的T[] toArray(IntFunction<T[]> generator)方法:
Integer[] array = list.toArray(Integer[]::new);

这种函数式写法我们会在后续讲到。

(5) Array转换为List

参考:https://www.jianshu.com/p/2b113f487e5e

  • 方法一:通过 List.of(T...) 方法最简单
Integer[] array = { 1, 2, 3 };
List<Integer> list = List.of(array);
  • 方法二:通过使用 java.util.Arrays.asList() 方法,常用!(易错,要好好理解!)
import java.util.Arrays;

String[] myArray = { "Apple", "Banana", "Orange" }List<String> myList = Arrays.asList(myArray);

// 或者
List<String> myList = Arrays.asList("Apple", "Orange");

方法二:易错点

后续有时间再补充

1.2.2 Vector类
1.2.2.1 Stack类

1.3 Queue

2. Map

在这里插入图片描述
在这里插入图片描述

Map与Collection在集合框架中属并列存在

Map存储的是键值对

Map存储元素使用put方法,Collection使用add方法

Map集合没有直接取出元素的方法,而是先转成Set集合,再通过迭代获取元素

Map集合中键要保证唯一性

也就是Collection是单列集合, Map 是双列集合。

(1) 常用方法
Modifier and TypeMethod and Description
voidclear() 从该地图中删除所有的映射(可选操作)
default Vcompute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) 尝试计算指定键的映射及其当前映射的值(如果没有当前映射, null
default VcomputeIfAbsent(K key, Function<? super K,? extends V> mappingFunction) 如果指定的键尚未与值相关联(或映射到 null ),则尝试使用给定的映射函数计算其值,并将其输入到此映射中,除非 null
default VcomputeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) 如果指定的密钥的值存在且非空,则尝试计算给定密钥及其当前映射值的新映射
booleancontainsKey(Object key) 如果此映射包含指定键的映射,则返回 true
booleancontainsValue(Object value) 如果此地图将一个或多个键映射到指定的值,则返回 true
Set<Map.Entry<K,V>>entrySet() 返回此地图中包含的映射的Set视图
booleanequals(Object o) 将指定的对象与此映射进行比较以获得相等性
default voidforEach(BiConsumer<? super K,? super V> action) 对此映射中的每个条目执行给定的操作,直到所有条目都被处理或操作引发异常
Vget(Object key) 返回到指定键所映射的值,或 null如果此映射包含该键的映射
default VgetOrDefault(Object key, V defaultValue) 返回到指定键所映射的值,或 defaultValue如果此映射包含该键的映射
inthashCode() 返回此地图的哈希码值
booleanisEmpty() 如果此地图不包含键值映射,则返回 true
Set<K>keySet() 返回此地图中包含的键的Set视图
default Vmerge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction) 如果指定的键尚未与值相关联或与null相关联,则将其与给定的非空值相关联
Vput(K key, V value) 将指定的值与该映射中的指定键相关联(可选操作)
voidputAll(Map<? extends K,? extends V> m) 将指定地图的所有映射复制到此映射(可选操作)
default VputIfAbsent(K key, V value) 如果指定的键尚未与某个值相关联(或映射到 null )将其与给定值相关联并返回 null ,否则返回当前值。
Vremove(Object key) 如果存在(从可选的操作),从该地图中删除一个键的映射
default booleanremove(Object key, Object value) 仅当指定的密钥当前映射到指定的值时删除该条目
default Vreplace(K key, V value) 只有当目标映射到某个值时,才能替换指定键的条目。
default booleanreplace(K key, V oldValue, V newValue) 仅当当前映射到指定的值时,才能替换指定键的条目
default voidreplaceAll(BiFunction<? super K,? super V,? extends V> function) 将每个条目的值替换为对该条目调用给定函数的结果,直到所有条目都被处理或该函数抛出异常
intsize() 返回此地图中键值映射的数量
Collection<V>values() 返回此地图中包含的值的Collection视图
(2) 创建Map
/* ------------------------- 添加元素 ------------------------ */
Map<String, Integer> map1 = new HashMap<>();
/* 方法一:put()方法添加元素 */
map1.put("jack", 20);
map1.put("rose", 18);
map1.put("lucy", 17);
map1.put("java", 25);
map1.put("jack", 30);   // 添加重复的键值(值不同),会返回几何中原有(重复键)的值
System.out.println("map1: " + map1);

Map<String, Integer> map2 = new HashMap<>();
map2.put("小明", 100);
map2.put("小红", 90);
System.out.println("map2: " + map2);

/* 方法二:putAll()方法添加元素 */
map1.putAll(map2);
System.out.println("map1: " + map1);

/* ------------------------- 删除元素 ------------------------ */
/* 方法一:remove()方法删除指定key元素*/
map1.remove("rose");
System.out.println("mpa1: " + map1);

/* 方法二:清空所有集合 */
map2.clear();
System.out.println("map2: " + map2);

/* ------------------------- 获取元素 ------------------------ */
/* 方法一:get()获取元素 */
System.out.println("value: " + map1.get("小明"));
(3) 遍历Map
/* ---------------------------------------------------------- */
/* ------------------------- 遍历元素 ------------------------ */
/* ---------------------------------------------------------- */
/* 方法一:增强for循环遍历,使用keySet()遍历,分别获取key和value。
 * Set<K> keySet() 返回所有key对象的Set集合
 */
for (String key : map1.keySet()) {
    System.out.println(key + ": " + map1.get(key));
}

/* 方法二:增强for循环遍历,使用entrySet()遍历 */
for (Map.Entry<String, Integer> entry : map1.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

/* ------------------- 主要看前面两种方法 ------------------ */

/* 方法三:使用迭代器遍历,使用keySet()遍历,分别获取key和value。
 * Set<K> keySet() 返回所有key对象的Set集合
 */
// 和上面的等价的
// Set<String> ks = map1.keySet();
// Iterator<String> it = ks.iterator();
// 上面两句可以合并为下面一句
 Iterator<String> it = map1.keySet().iterator();
while (it.hasNext()) {
    String key = it.next();
    System.out.println(key + ": " + map1.get(key));
}

/* 方法四:使用entrySet()遍历
 * public static interface Map.Entry<K, V>
 * 通过Map中的entrySet()方法,获取存放Map.Entry<K, V>对象Set集合
 * Set<Map.Entry<K, V>> entrySet()
 * 面向对象的思想将map集合中的键和值映射关系打包为一个对象,就是Map.Entry,
 * 将该对象存入Set集合,Map.Entry是一个对象,那么该对象具备的getKey,getValue获得键和值。
 */
// 返回的Map.Entry对象的Set集合 Map.Entry包含了key和value对象
// Set<Map.Entry<String, Integer>> es = map1.entrySet();
// Iterator<Map.Entry<String, Integer>> iterater = es.iterator();
// 上面两句等价于下面的一句
Iterator<Map.Entry<String, Integer>> iterater = map1.entrySet().iterator();
while (iterater.hasNext()) {
    // 返回的是封装了key和value对象的Map.Entry对象
    Map.Entry<String, Integer> en = iterater.next();
    // 获取Map.Entry对象中封装的key和value对象
    System.out.println(en.getKey() + " " + en.getValue());
}

/* 方法五:使用迭代器遍历,通过values()获取所有值,不能获取到key对象
 * Collection<V> values()
 */
Collection<Integer> vs = map1.values();
Iterator<Integer> iterator = vs.iterator();
while (iterator.hasNext()) {
    Integer value = iterator.next();
    System.out.println("value: " + value);
}

3. 常用排序

3.1 List排序

3.2 Set排序

3.3 Map排序

4. Reference

  • https://blog.youkuaiyun.com/qq_43437465/article/details/89437637
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值