0%

集合-3

Map

★Map接口和常用方法

  • public interface Map<K,V>

    Map 接口实现类的特点:

    1. MapCollection 并列存在。用于保存具有映射关系的数据:**Key-Value**(双列元素)。

    2. Map 中的 keyvalue 可以是任何引用类型的数据,会封装到 HashMap$Node 对象中。

    3. Map 中的 key 不允许重复,原因和 HashSet 一样,前面分析过源码。

      **注意:当 key 重复时,后面 putvalue 会替换前面 putvalue**,例如:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      package com.f.chapter14.map;

      import java.util.HashMap;
      import java.util.Map;

      /**
      * @author fzy
      * @date 2023/6/21 14:50
      */
      public class Map01 {
      public static void main(String[] args) {
      Map map = new HashMap();
      map.put("num1", "java");
      map.put("num2", "go");
      map.put("num1", "python");
      System.out.println(map);
      }
      }

      输出结果为:

      1
      {num1=python, num2=go}
    4. Map 中的 value 可以重复

    5. Mapkey 可以为 nullvalue 也可以为 null

      注意 key 可以为 null,但是只能有一个;value 可以为 null,但是可以多个。

    6. 经常使用 String 类作为 Mapkey

    7. keyvalue 之间存在单向一对一关系,即通过指定的 key 总能找到对应的 value

    8. 如下图是 Map 存放数据的 key-value 示意图,一对 key-value 最终是放在一个 HashMap$Node 中的,又因为 Node 实现了 Entry 接口:static class Node<K,V> implements Map.Entry<K,V> ,所以有些书上也说“一对 k-v 就是一个 Entry”。

      ![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C14-7.jpg)

      另外,key-value 为了方便程序员的遍历,还会创建 entrySet 集合transient Set<Map.Entry<K,V>> entrySet;,该集合存放的元素的类型为 HashMap$Node,该类型实现了 Entry 接口,而一个 Entry 对象就有 key(指向 HashMap$Node 中的 key)、value(指向 HashMap$Node 中的 value)。

      • 当把 HashMap$Node 对象存放到 entrySet 后,就方便了我们的遍历,因为 Map.Entry 提供了重要的方法:getKeygetValue

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        package com.f.chapter14.map;

        import java.util.HashMap;
        import java.util.Map;
        import java.util.Set;

        /**
        * @author fzy
        * @date 2023/6/21 14:50
        */
        public class Map01 {
        public static void main(String[] args) {
        Map map = new HashMap();
        map.put("num1", "java");
        map.put("num2", "go");
        map.put("num1", "python");
        map.put("num3", "php");
        map.put(1, "hello");
        map.put(2, "world");
        Set set = map.entrySet();
        System.out.println(set.getClass()); //class java.util.HashMap$EntrySet
        for (Object obj : set) {
        //System.out.println(obj.getClass()); //class java.util.HashMap$Node
        Map.Entry entry = (Map.Entry) obj;
        System.out.println(entry.getKey() + " " + entry.getValue());
        }
        }
        }
      • 最后回到这张图,一对 k-v 存储到 EntrySet 中的流程如下:

        ![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C14-8.jpg)

        如果我们只想得到 key 的集合,也可以只使用 Set set = map.keySet();

        如果我们只想得到 value 的集合,也可以只使用 Collection values = map.values();

  • Map 接口的常用方法:

    1. put(K key, V value):向 map 中添加一对 k-v ,如果键 key 重复,则会对 value 进行覆盖替换。 -> 增 + 改
    2. remove(Object key):根据键删除映射关系。 -> 删
    3. get(Object key):根据键获取值。 -> 查
    4. size():获取 k-v 对的个数。
    5. isEmpty():判断 map 是否为空。
    6. clear():清空 map
    7. containsKey(Object key):查找键是否存在。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    package com.f.chapter14.map;

    import java.util.HashMap;
    import java.util.Map;

    /**
    * @author fzy
    * @date 2023/6/22 9:39
    */
    public class MapMethod01 {
    public static void main(String[] args) {
    Map map = new HashMap();

    //1.`put(K key, V value)`:向 `map` 中添加一对 `k-v` ,如果键 `key` 重复,则会对 `value` 进行覆盖替换。
    map.put("num1", "三国演义");
    map.put("num2", "石头记");
    map.put("num2", "红楼梦"); //“红楼梦”替换了“石头记”
    map.put("num3", "水浒传");
    map.put("num4", "西游记");
    map.put(null, "四大名著");
    System.out.println(map); //{null=四大名著, num1=三国演义, num4=西游记, num3=水浒传, num2=红楼梦} -> 注意是无序的

    //2.`remove(Object key)`:根据键删除映射关系。
    map.remove(null);
    System.out.println(map); //{num1=三国演义, num4=西游记, num3=水浒传, num2=红楼梦}

    //3.`get(Object key)`:根据键获取值。
    System.out.println(map.get("num2")); //红楼梦

    //4.`size()`:获取 `k-v` 对的个数。
    System.out.println(map.size()); //4

    //5.`isEmpty()`:判断 `map` 是否为空。
    System.out.println(map.isEmpty()); //false

    //6.`clear()`:清空 `map`。
    map.clear();
    System.out.println(map); //{}

    //7.`containsKey(Object key)`:查找键是否存在。
    System.out.println(map.containsKey(null)); //false
    }
    }
Map六大遍历方式
  • 分别是 KeySetvaluesentrySet增强 foriterator 遍历。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package com.f.chapter14.map;

import java.util.*;

/**
* @author fzy
* @date 2023/6/22 10:50
*/
public class MapTraversal {
public static void main(String[] args) {
Map map = new HashMap();
map.put("num1", "三国演义");
map.put("num2", "石头记");
map.put("num2", "红楼梦"); //“红楼梦”替换了“石头记”
map.put("num3", "水浒传");
map.put("num4", "西游记");
map.put(null, "四大名著");

//1.先取出所有的 Key,再通过 Key 取出对应的 Value (增强for)
System.out.println("---------第一种方式---------");
Set keySet = map.keySet();
for (Object key : keySet) {
System.out.println(key + " - " + map.get(key));
}

//2.先取出所有的 Key,再通过 Key 取出对应的 Value (迭代器iterator)
System.out.println("---------第二种方式---------");
Iterator keyIterator = keySet.iterator();
while (keyIterator.hasNext()) {
Object key = keyIterator.next();
System.out.println(key + " - " + map.get(key));
}

//3.只取出所有的 Value (增强for)
System.out.println("---------第三种方式---------");
Collection values = map.values();
for (Object val : values) {
System.out.println(val);
}

//4.只取出所有的 Value (迭代器iterator)
System.out.println("---------第四种方式---------");
Iterator valIterator = values.iterator();
while (valIterator.hasNext()) {
Object val = valIterator.next();
System.out.println(val);
}

//5.通过 EntrySet 来获取 k-v (增强for)
System.out.println("---------第五种方式---------");
Set entrySet = map.entrySet();
for (Object obj : entrySet) {
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() + " - " + entry.getValue());
}

//6.通过 EntrySet 来获取 k-v (迭代器iterator)
System.out.println("---------第六种方式---------");
Iterator entryIterator = entrySet.iterator();
while (entryIterator.hasNext()) {
Map.Entry entry = (Map.Entry) entryIterator.next();
System.out.println(entry.getKey() + " - " + entry.getValue());
}
}
}
★★★Map.Entry相关说明
  • Map.Entry 相关再做一个说明:

    • 首先要明确,Entry 是一个接口,而且是 Map 接口的内部接口。而 HashMap 的静态内部类 Node 是实现了 Map.Entry 接口。

    • **真正存放 k-v 的地方是 HashMap$Node**,即 HashMap 的静态内部类 Node

    • EntrySet 是用于方便程序员对 map 中的元素进行遍历,因为 Map.Entry 有两个抽象方法:getKeygetVal,通过这两个方法我们可以获取 map 中的 k-v 的具体的内容。

    • 因为 HashMap$Node 实现了 Map.Entry 接口,所以其实现了 getKeygetVal 方法,按理来说,在进行遍历时(以第五种方式为例),我们可以直接用 HashMap.Node node = (HashMap.Node) obj; ,然后再使用 node.getKey()node.getVal() 来获取 k-v 值,但实际是不行的。原因在于 NodeHashMap 的静态内部类,并没有被 public 关键字修饰,所以无法在包外被使用。

      所以采用 Map.Entry 的方式 (Map.Entry entry = (Map.Entry) obj;)来调用 entry.getKey()entry.getVal() 方法,这里是用到了接口的多态(obj 实际的运行类型是 HashMap$Node )。

★★★HashMap
  • public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
  1. HashMapMap 接口使用频率最高的实现类。
  2. HashMapkey-value 对的方式来存储数据(HahsMap$Node类型)。
  3. key 不能重复,但是值可以重复,允许使用 null 键和 null 值。
  4. 如果添加相同的 key,则会覆盖原来的 key-val,等同于修改。(key不会替换,val会替换)
  5. HashSet 一样,不保证存入和取出的顺序一样,因为底层是以 hash 表的方式来存储的。
  6. HashMap 没有实现同步,因此是线程不安全的,方法没有做同步互斥的操作,没有 synchronized
HashMap底层机制
  • 因为 HashSet 底层就是 HashMap,所以前面介绍的 HashSet 底层机制本质就是 HashMap 的底层机制,即 HashMap 底层是(数组+链表+红黑树)

    ![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C14-9.jpg)

  • 扩容机制:和 HashSet 的扩容机制是完全一样的,还是再简单总结一遍:

    1. HashMap 底层维护了 Node 类型的数组 HashMap$Node[] table,默认为 null
    2. 当创建 HashMap 对象时,先将加载因子 (loadfactor) 初始化为 0.75,且 table 初始为 null。
    3. 当添加 key-val 时,通过 key 的哈希值得到在 tablel 的索引。然后判断该索引处是否有元素,如果没有元素直接添加。如果该索引处有元素,继续判断该元素的 key 和准备加入的 key 是否相等,如果相等,则直接替换 val;如果不相等需要判断是树结构还是链表结构,做出相应处理。如果添加时发现容量不够,则需要扩容。
    4. 第 1 次添加,则需要扩容 table 容量为16,临界值 (threshold) 为12。
    5. 以后再扩容,则需要扩容 table 容量为原来的 2 倍,即 32,临界值为原来的 2 倍,即24,依次类推。
    6. 在 Java8 中,如果一条链表的元素个数超过 TREEIFY THRESHOLD(默认是8),并且 table 的大小>= MIN TREEIFY CAPACITY(默认64),就会进行树化(红黑树)。
Hashtable
  • public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable
  1. 存放的元素是键值对:即 K-V
  2. Hashtable 的键和值都不能为 null,否则会抛出 NullPointerException 异常。
  3. Hashtable 使用方法基本上和 HashMap 一样。
  4. Hashtable线程安全的(synchronized),HashMap 是线程不安全的。
Hashtable扩容机制
  1. Hashtable 底层有数组 Hashtable$Entry[],初始化大小为 11,加载因子为 0.75,初始 threshold 为 8(11*0.75,丢弃小数部分)。

    1
    2
    3
    public Hashtable() {
    this(11, 0.75f);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public Hashtable(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
    throw new IllegalArgumentException("Illegal Capacity: "+
    initialCapacity);
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
    throw new IllegalArgumentException("Illegal Load: "+loadFactor);

    if (initialCapacity==0)
    initialCapacity = 1;
    this.loadFactor = loadFactor;
    table = new Entry<?,?>[initialCapacity];
    threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
    }
  2. 在添加 k-v 元素时,put 函数会调用 addEntry(hash, key, value, index); 函数来添加。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public synchronized V put(K key, V value) {
    // Make sure the value is not null
    if (value == null) {
    throw new NullPointerException();
    }

    // Makes sure the key is not already in the hashtable.
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    Entry<K,V> entry = (Entry<K,V>)tab[index];
    for(; entry != null ; entry = entry.next) {
    if ((entry.hash == hash) && entry.key.equals(key)) {
    V old = entry.value;
    entry.value = value;
    return old;
    }
    }

    addEntry(hash, key, value, index);
    return null;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    private void addEntry(int hash, K key, V value, int index) {
    modCount++;

    Entry<?,?> tab[] = table;
    if (count >= threshold) {
    // Rehash the table if the threshold is exceeded
    rehash();

    tab = table;
    hash = key.hashCode();
    index = (hash & 0x7FFFFFFF) % tab.length;
    }

    // Creates the new entry.
    @SuppressWarnings("unchecked")
    Entry<K,V> e = (Entry<K,V>) tab[index];
    tab[index] = new Entry<>(hash, key, value, e);
    count++;
    }
  3. addEntry 函数中可以看到,如果 (count >= threshold),就会 rehash,即进行扩容。扩容机制为 int newCapacity = (oldCapacity << 1) + 1;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    protected void rehash() {
    int oldCapacity = table.length;
    Entry<?,?>[] oldMap = table;

    // overflow-conscious code
    int newCapacity = (oldCapacity << 1) + 1; //新的容量=(旧的容量*2+1)
    if (newCapacity - MAX_ARRAY_SIZE > 0) {
    if (oldCapacity == MAX_ARRAY_SIZE)
    // Keep running with MAX_ARRAY_SIZE buckets
    return;
    newCapacity = MAX_ARRAY_SIZE;
    }
    Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];

    modCount++;
    threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
    table = newMap;

    for (int i = oldCapacity ; i-- > 0 ;) {
    for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
    Entry<K,V> e = old;
    old = old.next;

    int index = (e.hash & 0x7FFFFFFF) % newCapacity;
    e.next = (Entry<K,V>)newMap[index];
    newMap[index] = e;
    }
    }
    }
Properties
  1. Properties 类继承自 Hashtable 类并且实现了 Map 接口,也是使用一种键值对的形
    式来保存数据。
  2. 它的使用特点和 Hashtable 类似。
  3. Properties 还可以用于从 xxx.properties 文件中,加载数据到 Properties 类对象,
    并进行读取和修改。
  4. 说明:工作后 xxx.properties 文件通常作为配置文件,这个知识点会在 IO 流举例。
TreeMap
  • TreeMapTreeSet 大同小异,因为 TreeSet 底层就是 TreeMap

    • 只不过在 TreeSet 中,k-v 中的 v 都是同一个对象(作为占位作用):private static final Object PRESENT = new Object();
    • TreeMap 中,k-v 中的 v 可以有不同的值。
  • 注意:在 HashMap 中,判断 key 是否相同是通过源码中的

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

    来判断的。

    而在 TreeMap 中,判断 key 是否相同是通过我们传入的 Comparator 对象来判断的。

    1
    2
    3
    4
    5
    6
    TreeMap treeMap = new TreeMap(new Comparator() {
    @Override
    public int compare(Object o1, Object o2) {
    return ((String)o1).compareTo((String)o2);
    }
    });

★★★集合选型规则

  • 开发中如何选择集合实现类:

    在开发中,选择什么集合实现类,主要取决于业务操作特点,然后根据集合实现类特性进行选择,分析如下:

    1. 先判断存储的类型(一组对象或一组键值对 k-v)。
    2. 如果是一组对象:Collection 接口
      • 允许重复:List
        • 增删多:LinkedList(底层维护了一个双向链表)
        • 改查多:ArrayList(底层维护了 Object 类型的可变数组)
      • 不允许重复:Set
        • 无序:HashSet(底层是 HashMap,维护了一个哈希表,即(**数组+链表+红黑树**)。
        • 排序:TreeSet
        • 插入和取出顺序一致:LinkedHashSet (维护数组+双向链表)
    3. 如果是一组键值对:Map 接口
      • 键无序:HashMap(底层是:哈希表。jdk7:数组+链表,**jdk8:数组+链表+红黑树**)
      • 键排序TreeMap
      • 键插入和取出顺序一致:LinkedHashMap
      • 读取文件:Properties

Collections工具类

  • Collections 是一个操作 SetListMap 等集合的工具类。
  • Collections 中提供了一系列静态的方法,用以对集合元素进行排序、查询和修改等操作。
排序操作
  1. reverse(List<?> list):反转 List 中的元素的顺序。
  2. shuffle(List<?> list):对 List 集合元素进行随机排序。
  3. sort(List<T> list) :根据元素的自然顺序对指定 List 集合元素按升序排序。
  4. sort(List<T> list, Comparator<? super T> c) :通过传入 Comparator 来指定排序的方式,对 List 集合元素进行排序。
  5. swap(List<?> list, int i, int j):将 List 中索引为 ij 的集合元素对调位置。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.f.chapter14.collections;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
* @author fzy
* @date 2023/6/23 20:31
*/
public class Collections_Sort {
public static void main(String[] args) {
List list = new ArrayList();
list.add("hello");
list.add("world");
list.add("my");
list.add("computer");
System.out.println(list); //[hello, world, my, computer]

//1.`reverse(List<?> list)`:反转 `List` 中的元素的顺序。
Collections.reverse(list);
System.out.println(list); //[computer, my, world, hello]

//2.`shuffle(List<?> list)`:对 `List` 集合元素进行随机排序。
Collections.shuffle(list);
System.out.println(list); //[computer, world, hello, my]

//3.`sort(List<T> list)` :根据元素的自然顺序对指定 `List` 集合元素按升序排序。
Collections.sort(list);
System.out.println(list); //[computer, hello, my, world]

//4.`sort(List<T> list, Comparator<? super T> c)` :通过传入 `Comparator` 来指定排序的方式,对 `List` 集合元素进行排序。
//下面以”按字符串长度大小排序“为例
Collections.sort(list, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
//这里可以加入一些校验代码
return ((String) o1).length() - ((String) o2).length();
}
});
System.out.println(list); //[my, hello, world, computer]

//5.`swap(List<?> list, int i, int j)`:将 `List` 中索引为 `i` 和 `j` 的集合元素对调位置。
Collections.swap(list, 1, 3);
System.out.println(list); //[my, computer, world, hello]
}
}
查找操作
  1. max(Collection<? extends T> coll):根据元素的自然顺序,返回给定集合中的最大元素。
  2. max(Collection<? extends T> coll, Comparator<? super T> comp):根据传入的 Comparator 指定的顺序,返回给定集合中的最大元素。
  3. min(Collection<? extends T> coll):和 1. 类似。
  4. min(Collection<? extends T> coll, Comparator<? super T> comp):和 2. 类似。
  5. frequency(Collection<?> c, Object o):返回指定集合中指定元素的出现次数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.f.chapter14.collections;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
* @author fzy
* @date 2023/6/23 20:51
*/
public class Collections_Search {
public static void main(String[] args) {
List list = new ArrayList();
list.add("hello");
list.add("world");
list.add("my");
list.add("computer");
System.out.println(list); //[hello, world, my, computer]

//1.`max(Collection<? extends T> coll)`:根据元素的自然顺序,返回给定集合中的最大元素。
System.out.println(Collections.max(list)); //world

//2.`max(Collection<? extends T> coll, Comparator<? super T> comp)`:根据传入的 `Comparator` 指定的顺序,返回给定集合中的最大元素。
//下面以”按字符串长度大小排序“为例
System.out.println(Collections.max(list, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ((String) o1).length() - ((String) o2).length();
}
})); //computer

//3.`min(Collection<? extends T> coll)`:和 `1.` 类似。
System.out.println(Collections.min(list)); //computer

//4.`min(Collection<? extends T> coll, Comparator<? super T> comp)`:和 `2.` 类似。
System.out.println(Collections.min(list, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ((String) o1).length() - ((String) o2).length();
}
})); //my

//5.`frequency(Collection<?> c, Object o)`:返回指定集合中指定元素的出现次数。
System.out.println(Collections.frequency(list, "world")); //1
}
}
修改操作
  1. copy(List<? super T> dest, List<? extends T> src):将 src 中的内容复制到 dest 中。
  2. replaceAll(List<T> list, T oldVal, T newVal):使用 newVal 替换 List 对象的所有 oldVal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.f.chapter14.collections;

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

/**
* @author fzy
* @date 2023/6/23 21:15
*/
public class Collections_Modify {
public static void main(String[] args) {
List list = new ArrayList();
list.add("hello");
list.add("world");
list.add("my");
list.add("computer");
System.out.println(list); //[hello, world, my, computer]

//1.`copy(List<? super T> dest, List<? extends T> src)`:将 `src` 中的内容复制到 `dest` 中。
List newList = new ArrayList();
//Collections.copy 函数在进行拷贝之前,会对 src 和 dest 的长度进行判断,如果 src 的长度大于 dest,
// 就抛出 IndexOutOfBoundsException: Source does not fit in dest 异常
for (int i = 0; i < list.size(); i++) {
newList.add(""); //为了使得 dest 的长度不小于 src 的长度
}
Collections.copy(newList, list);
System.out.println(newList); //[hello, world, my, computer]

//2.`replaceAll(List<T> list, T oldVal, T newVal)`:使用 `newVal` 替换 `List` 对象的所有 `oldVal`。
Collections.replaceAll(list, "hello", "hi");
System.out.println(list); //[hi, world, my, computer]
}
}
---------------The End---------------