当前位置 : 首页 » 文章分类 :  开发  »  Java-集合框架基础

Java-集合框架基础

Java集合框架笔记


Java8 Map转List

public static void main(String[] args) {
    Map<String,String> map=new HashMap<>();
    map.put("1","AAAA");
    map.put("2","BBBB");
    map.put("3","CCCC");
    map.put("4","DDDD");
    map.put("5","EEEE");
    List<Object> list= map.entrySet().stream().map(et ->et.getKey()+"_"+et.getValue()).collect(Collectors.toList());
    list.forEach(obj-> System.out.println(obj));
}

Java8 List转Map

public Map<Long, Account> getIdAccountMap(List<Account> accounts) {
    return accounts.stream().collect(Collectors.toMap(Account::getId, account -> account));
}

account -> account是一个返回本身的lambda表达式,其实还可以使用Function接口中的一个默认方法代替,使整个方法更简洁优雅:

public Map<Long, Account> getIdAccountMap(List<Account> accounts) {
    return accounts.stream().collect(Collectors.toMap(Account::getId, Function.identity()));
}

其中Function.identity() 就是遍历 beanList 时对应的当前 TestBean 对象,可以简单的认为就是循环遍历时的当前元素 this。

重复Key处理

public Map<String, Account> getNameAccountMap(List<Account> accounts) {
    return accounts.stream().collect(Collectors.toMap(Account::getUsername, Function.identity(), (key1, key2) -> key2));
}

如不处理,可能报错(java.lang.IllegalStateException: Duplicate key),因为name是有可能重复的。toMap有个重载方法,可以传入一个合并的函数来解决key冲突问题。这里只是简单的使用后者覆盖前者来解决key重复问题。

指定Map类型

public Map<String, Account> getNameAccountMap(List<Account> accounts) {
    return accounts.stream().collect(Collectors.toMap(Account::getUsername, Function.identity(), (key1, key2) -> key2, LinkedHashMap::new));
}

重复value构成List

Map<Integer, List<String>> jdk8MultiMap = beanList
    .stream()
    .filter(o -> o != null) // avoid throws NullPointerException
    .collect(Collectors.toMap(
        // get key
        TestBean::getAge
        // get value
        , (TestBean o) -> Lists.newArrayList(o.getName())
        // 当同一个key遇到不同value时的合并策略
        , (x, y) -> {
            x.addAll(y);
            return x;
        }
        // 当不需要明确具体的Map类型时可省略。默认就是HashMap
        , HashMap::new
    ));

使用java8将list转为map
https://zacard.net/2016/03/17/java8-list-to-map/


Java8遍历Map的几种方式

map.forEach()推荐

map.forEach((k, v) -> System.out.println(“key:value = “ + k + “:” + v));

map.keySet().forEach()

map.keySet().forEach(key -> System.out.println(“map.get(“ + key + “) = “ + map.get(key)));

map.entrySet().forEach()

map.entrySet().forEach(entry -> System.out.println(“key:value = “ + entry.getKey() + “:” + entry.getValue()));

map.values().forEach()

map.values().forEach(System.out::println);

示例

public class LambdaMap {

    private Map<String, Object> map = new HashMap<>();

    @Before
    public void initData() {
        map.put("key1", "value1");
        map.put("key2", "value2");
        map.put("key3", "value3");
        map.put("key4", 4);
        map.put("key5", 5);
        map.put("key5", 'h');
    }


    /**
     * 遍历Map的方式一
     * 通过Map.keySet遍历key和value
     */
    @Test
    public void testErgodicWayOne() {
        System.out.println("---------------------Before JAVA8 ------------------------------");
        for (String key : map.keySet()) {
            System.out.println("map.get(" + key + ") = " + map.get(key));
        }
        System.out.println("---------------------JAVA8 ------------------------------");
        map.keySet().forEach(key -> System.out.println("map.get(" + key + ") = " + map.get(key)));
    }

    /**
     * 遍历Map第二种
     * 通过Map.entrySet使用Iterator遍历key和value
     */
    @Test
    public void testErgodicWayTwo() {
        System.out.println("---------------------Before JAVA8 ------------------------------");
        Iterator<Map.Entry<String, Object>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Object> entry = iterator.next();
            System.out.println("key:value = " + entry.getKey() + ":" + entry.getValue());
        }
        System.out.println("---------------------JAVA8 ------------------------------");
        map.entrySet().iterator().forEachRemaining(item -> System.out.println("key:value=" + item.getKey() + ":" + item.getValue()));
    }

    /**
     * 遍历Map第三种
     * 通过Map.entrySet遍历key和value,在大容量时推荐使用
     */
    @Test
    public void testErgodicWayThree() {
        System.out.println("---------------------Before JAVA8 ------------------------------");
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            System.out.println("key:value = " + entry.getKey() + ":" + entry.getValue());
        }
        System.out.println("---------------------JAVA8 ------------------------------");
        map.entrySet().forEach(entry -> System.out.println("key:value = " + entry.getKey() + ":" + entry.getValue()));
    }

    /**
     * 遍历Map第四种
     * 通过Map.values()遍历所有的value,但不能遍历key
     */
    @Test
    public void testErgodicWayFour() {
        System.out.println("---------------------Before JAVA8 ------------------------------");
        for (Object value : map.values()) {
            System.out.println("map.value = " + value);
        }
        System.out.println("---------------------JAVA8 ------------------------------");
        map.values().forEach(System.out::println); // 等价于map.values().forEach(value -> System.out.println(value));
    }

    /**
     * 遍历Map第五种
     * 通过k,v遍历,Java8独有的
     */
    @Test
    public void testErgodicWayFive() {
        System.out.println("---------------------Only JAVA8 ------------------------------");
        map.forEach((k, v) -> System.out.println("key:value = " + k + ":" + v));
    }
}

Java8中Map的遍历方式总结
https://www.cnblogs.com/homeword/p/7396414.html


Collections工具类

java.util.Collections

public class Collections
extends Object

此类完全由在 collection 上进行操作或返回 collection 的静态方法组成。它包含在 collection 上操作的多态算法,即“包装器”,包装器返回由指定 collection 支持的新 collection,以及少数其他内容。

如果为此类的方法所提供的 collection 或类对象为 null,则这些方法都将抛出 NullPointerException。

此类中所含多态算法的文档通常包括对实现 的简短描述。应该将这类描述视为实现注意事项,而不是规范 的一部分。实现者应该可以随意使用其他算法替代,只要遵循规范本身即可。(例如,sort 使用的算法不一定是合并排序算法,但它必须是稳定的。)

此类中包含的“破坏性”算法,即可修改其所操作的 collection 的算法,该算法被指定在 collection 不支持适当的可变基元(比如 set 方法)时抛出 UnsupportedOperationException。如果调用不会对 collection 产生任何影响,那么这些算法可能(但不要求)抛出此异常。例如,在已经排序的、不可修改列表上调用 sort 方法可能会(也可能不会)抛出 UnsupportedOperationException。

此类是Java Collections Framework的成员。


unmodifiableMap()

public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m) {
        return new UnmodifiableMap<>(m);
}

该方法返回了一个map的不可修改的视图umap, 为用户提供了一种生成只读容器的方法。如果尝试修改该容器umap, 将会抛出UnsupportedOperationException异常。

Collections.unmodifiableMap 能做什么?
我们在类中经常需要返回一个集合,比如mapA。如果直接返回成员变量mapA本身的话,相当于对外暴露了集合的引用,外部就可以随意修改该对象的集合,该对象可能对修改都一无所知,属性却发生了变化。
一种解决方法,就是将该集合修饰为private, 在返回集合的方法中采用Collections.unmodifiableMap(mapA),返回mapA的一个不可变的副本。且该方法要比我们自己去复制一个副本效率要高。

Collections.unmodifiableMap 构造的map真的不可修改吗?
遗憾的是该结论并不总是成立。对于map<key, value>中的内容value, unmodifiableMap仅仅保证的是它的引用不能被修改,如果value对应的是一个可变对象,那么该unmodifiableMap的内容还是可变的。
比如Map<String, Student>中Student的某个字段的内容,还是可以修改的。

Collections.unmodifiableMap
https://www.cnblogs.com/dreamysmurf/p/6253737.html


reverse()

public static void reverse(List<?> list)
反转指定列表中元素的顺序。
此方法以线性时间运行。

  • 参数:list - 元素要被反转的列表。
  • 抛出: UnsupportedOperationException - 如果指定列表或其列表迭代器不支持 set 操作。

shuffle()乱序

rotate()旋转

package crunchify.com.tutorial;

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

/**
 * @author Crunchify.com
 * Best way to Shuffle, Reverse, Copy, Rotate and Swap List in Java8
 *
 */

public class CrunchifyJava8ShuffleList {
    public static void main(String[] args) {

        List<String> CrunchifyList = new ArrayList<String>();

        CrunchifyList.add("Google");
        CrunchifyList.add("Facebook");
        CrunchifyList.add("Twitter");
        CrunchifyList.add("Snap Inc");
        CrunchifyList.add("Crunchify LLC");
        CrunchifyList.add("TechCrunch");
        CrunchifyList.add("Verizon");

        List<String> newList = new ArrayList<String>(CrunchifyList);

        // Print list before any operation.
        System.out.println("Printing result before any Operation: \t" + CrunchifyList);

        // Randomly permutes the specified list using a default source of randomness.
        Collections.shuffle(CrunchifyList);
        System.out.println("Printing result after shuffle(): \t" + CrunchifyList);

        // Reverses the order of the elements in the specified list.
        Collections.reverse(CrunchifyList);
        System.out.println("Printing result after reverse(): \t" + CrunchifyList);

        // Copies all of the elements from one list into another.
        Collections.copy(newList, CrunchifyList);
        System.out.println("Printing result after copy(): \t\t" + newList);

        // Rotates the elements in the specified list by the specified distance.
        Collections.rotate(newList, 2);
        System.out.println("Printing result after rotate(): \t" + newList);

        // Returns the number of elements in this list.
        System.out.println("Printing total count using size(): \t" + newList.size());

        // Swaps the elements at the specified positions in the specified list.
        Collections.swap(newList, 2, 4);
        System.out.println("Printing result after swap(): \t\t" + newList);
    }

}

Arrays工具类

java.util.Arrays

public class Arrays
extends Object

此类包含用来操作数组(比如排序和搜索)的各种方法。此类还包含一个允许将数组作为列表来查看的静态工厂。

除非特别注明,否则如果指定数组引用为 null,则此类中的方法都会抛出 NullPointerException。

此类中所含方法的文档都包括对实现 的简短描述。应该将这些描述视为实现注意事项,而不应将它们视为规范 的一部分。实现者应该可以随意替代其他算法,只要遵循规范本身即可。(例如,sort(Object[]) 使用的算法不必是一个合并排序算法,但它必须是稳定的。)

此类是Java Collections Framework的成员。


asList()

public static <T> List<T> asList(T... a)
返回一个受指定数组支持的固定大小的列表。(对返回列表的更改会“直接写”到数组。)此方法同 Collection.toArray() 一起,充当了基于数组的 API 与基于 collection 的 API 之间的桥梁。返回的列表是可序列化的,并且实现了 RandomAccess。
此方法还提供了一个创建固定长度的列表的便捷方法,该列表被初始化为包含多个元素:
List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");

  • 参数:a - 支持列表的数组。
  • 返回:指定数组的列表视图。

sort(int[])

public static void sort(int[] a)
对指定的 int 型数组按数字升序进行排序。

  • 参数:a - 要排序的数组

实现

  • JSE1.6及之前,该排序算法是一个经过调优的快速排序法,改编自 Jon L. Bentley 和 M. Douglas McIlroy 合著的 Engineering a Sort Function, Software-Practice and Experience Vol. 23(11) P. 1249-1265 (November 1993)。此算法在许多数据集上提供 O(n log(n)) 性能,这导致其他快速排序会降低二次型性能。
  • JSE1.7及之后,默认是一个由 Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch 实现的 Dual-Pivot快速排序。此算法在许多数据集上提供 O(n log(n)) 性能,这导致其他快速排序会降低二次型性能。并且比传统的单pivot快速排序要快。

Map

interface Map<K,V>

java.util.Map<K,V>
public interface Map<K,V>

将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。
Map 接口提供三种collection 视图,允许以键集、值集或键-值映射关系集的形式查看某个映射的内容。

嵌套类Map.Entry<K,V>

public static interface Map.Entry<K,V>
映射项(键-值对)。Map.entrySet 方法返回映射的 collection 视图,其中的元素属于此类。获得映射项引用的唯一 方法是通过此 collection 视图的迭代器来实现。

put()

V put(K key, V value)
将指定的值与此映射中的指定键关联(可选操作)。如果此映射以前包含一个该键的映射关系,则用指定值替换旧值(当且仅当 m.containsKey(k) 返回 true 时,才能说映射 m 包含键 k 的映射关系)。
返回:以前与 key 关联的值,如果没有针对 key 的映射关系,则返回 null。(如果该实现支持 null 值,则返回 null 也可能表示此映射以前将 null 与 key 关联)。

get()

V get(Object key)
返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
更确切地讲,如果此映射包含满足 (key==null ? k==null : key.equals(k)) 的键 k 到值 v 的映射关系,则此方法返回 v;否则返回 null。(最多只能有一个这样的映射关系)。
如果此映射允许 null 值,则返回 null 值并不一定 表示该映射不包含该键的映射关系;也可能该映射将该键显示地映射到 null。使用 containsKey 操作可区分这两种情况。

keySet()

Set<K> keySet()
返回此映射中包含的键的 Set 视图。该 set 受映射支持,所以对映射的更改可在此 set 中反映出来,反之亦然。如果对该 set 进行迭代的同时修改了映射(通过迭代器自己的 remove 操作除外),则迭代结果是不确定的。set 支持元素移除,通过 Iterator.remove、Set.remove、removeAll、retainAll 和 clear 操作可从映射中移除相应的映射关系。它不支持 add 或 addAll 操作。
例:

public void doSomethingWithMap(Map<String,Object> map) {
  for (String key : map.keySet()) {
    Object value = map.get(key);
    // ...
  }
}

entrySet()

Set<Map.Entry<K,V>> entrySet()
返回此映射中包含的映射关系的 Set 视图。该 set 受映射支持,所以对映射的更改可在此 set 中反映出来,反之亦然。如果对该 set 进行迭代的同时修改了映射(通过迭代器自己的 remove 操作,或者通过对迭代器返回的映射项执行 setValue 操作除外),则迭代结果是不确定的。set 支持元素移除,通过 Iterator.remove、Set.remove、removeAll、retainAll 和 clear 操作可从映射中移除相应的映射关系。它不支持 add 或 addAll 操作。
例:

public void doSomethingWithMap(Map<String,Object> map) {
  for (Map.Entry<String,Object> entry : map.entrySet()) {
    String key = entry.getKey();
    Object value = entry.getValue();
    // ...
  }
}

class HashMap<K,V>

java.util.HashMap<K,V>
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable

基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键
HashMap不是线程同步的。

HashMap 的实例有两个参数影响其性能:初始容量 和加载因子。容量 是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。

构造方法
public HashMap()
构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap。


class LinkedHashMap<K,V>

java.util.LinkedHashMap<K,V>
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>

顾名思义LinkedHashMap是比HashMap多了一个链表的结构,与HashMap相比LinkedHashMap维护的是一个具有双重链表的HashMap。
LinkedHashMap支持两种排序:一种是插入排序,一种是使用排序,最近使用的会移至尾部例如 M1 M2 M3 M4,使用M3后为 M1 M2 M4 M3了,LinkedHashMap输出时其元素是有顺序的,而HashMap输出时是随机的。
如果Map映射比较复杂而又要求高效率的话,最好使用LinkedHashMap。
LinkedHashMap不是线程同步的,多线程访问的话可能会造成不同步,所以要用Collections.synchronizedMap来包装一下,从而实现同步。

如果需要输出的顺序和输入时的相同,那么就选用LinkedHashMap。


List

list转逗号分隔字符串

String s = list.stream().map(Object::toString).collect(Collectors.joining(","));
String citiesCommaSeparated = cities.stream().map(String::toUpperCase).collect(Collectors.joining(","));
String citiesCommaSeparated = String.join(",", Arrays.asList("Milan", "London", "New York", "San Francisco"));

遍历时remove防止fail-fast

使用Iterator的remove方法移除当前对象,如果使用List的remove方法,则同样会出现ConcurrentModificationException

/**
 * 使用Iterator的方式也可以顺利删除和遍历
 */
public void iteratorRemove() {
    List<Student> students = this.getStudents();
    System.out.println(students);
    Iterator<Student> stuIter = students.iterator();
    while (stuIter.hasNext()) {
        Student student = stuIter.next();
        if (student.getId() % 2 == 0)
            stuIter.remove();//这里要使用Iterator的remove方法移除当前对象,如果使用List的remove方法,则同样会出现ConcurrentModificationException
    }
    System.out.println(students);
}

如何正确遍历删除List中的元素,你会吗?
http://elim.iteye.com/blog/1523785


interface List<E>

java.util.List
public interface List<E> extends Collection<E>

有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。

size()

int size()
返回列表中的元素数。如果列表包含多于 Integer.MAX_VALUE 个元素,则返回 Integer.MAX_VALUE

isEmpty()

boolean isEmpty()
如果列表不包含元素,则返回 true

contains()

boolean contains(Object o)
如果列表包含指定的元素,则返回 true。更确切地讲,当且仅当列表包含满足 (o==null ? e==null : o.equals(e)) 的元素 e 时才返回 true。

addAll()

boolean addAll(Collection<? extends E> c)
添加指定 collection 中的所有元素到此列表的结尾,顺序是指定 collection 的迭代器返回这些元素的顺序(可选操作)。

  • 抛出:
    • UnsupportedOperationException - 如果列表不支持 addAll 操作
    • ClassCastException - 如果指定 collection 的元素的类不允许它添加到此列表
    • NullPointerException - 如果指定的 collection 包含一个或多个 null 元素,并且该列表不允许 null 元素,或者指定的 collection 为 null
    • IllegalArgumentException - 如果指定 collection 的元素的某些属性不允许它添加此列表

class ArrayList<E>

java.util.ArrayList
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable

List 接口的大小可变数组的实现。
每个 ArrayList 实例都有一个容量。该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。并未指定增长策略的细节,因为这不只是添加元素会带来分摊固定时间开销那样简单。

注意,此实现不是同步的。如果多个线程同时访问一个 ArrayList 实例,而其中至少一个线程从结构上修改了列表,那么它必须 保持外部同步。(结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。)


class LinkedList<E>

java.util.LinkedList
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, Serializable

List 接口的链接列表实现。
除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。
此类实现 Deque 接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作。
注意,此实现不是同步的。


《深入理解Java集合框架》系列文章
http://www.cnblogs.com/CarpenterLee/p/5545987.html

上一篇 Java-日期

下一篇 Spring-概述

阅读
4,424
阅读预计19分钟
创建日期 2015-08-08
修改日期 2018-08-24
类别
百度推荐