1、集合框架概述
(1)集合框架作用
在实际开发中,我们经常会对一组相同类型的数据进行统一管理操作。到目前为止,我们可以使用数组结构、链表结构、二叉树结构来实现。
数组的最大问题在于数组中的元素个数是固定的,要实现动态数组,必竟还是比较麻烦, 自己实现链表或二叉树结构来管理对象更是不方便。
在JDK1.2版本后,JAVA完整的提供了类集合的概念,封装了一组强大的、非常方便的集合 框架API,让我们在开发中大大的提高了效率。 集合中分为三大接口:
Collection、Map、Iterator
(2)Collection接口
Collection 层次结构 中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。
一些 collection 允许有重复的元素,而另一些则不允许。
一些 collection 是有序的,而另一些则是无序的。
JDK 不提供此接口的任何直接 实现:它提供更具体的子接口(如 Set 和 List)实现。此接口通常用来传递 collection,并在需要最大普遍性的地方操作这些collection。 接口的定义:
public interface Collection<E>extends Iterable<E>
2、集合框架List接口
(1)List接口
public interface List extends Collection<E>
有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
(2)ArrayList
public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, Serializable
List 接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有 元素。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。
- 使用add来添加元素,可以指定位置
- 使用size来获取长度大小
- 使用get来获取相应的数值
- 使用remove来删除某个元素
- 使用contains来查看是否包含某个元素
- 使用set来用指定元素替换列表中指定位置的元素
- 使用toArray将集合转换成数组
(3)Vector
public class Vector<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, Serializable
Vector 类可以实现可增长的对象数组。 与数组一样, 它包含可以使用整数索引进行访问的组件。 但是, Vector 的大小可以根据需要增大或缩小, 以适应创建 Vector 后进行添加或移除项的操作
与ArrayList最大的区别是线程是安全的,适合多线程访问时使用,但在单线程效率较低
(4)LinkedList
public class LinkedList<E> extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, Serializable
List 接口的链接列表实现。 实现所有可选的列表操作, 并且允许所有元素(包括 null)。除了实现 List 接口外, LinkedList 类还为在列表的开头及结尾 get、 remove 和 insert 元素提供了统一的命名方法。
ListDemo
import java.util.ArrayList;import java.util.LinkedList;import java.util.List;import java.util.Vector;/*** Collection接口:用于存储单个对象的集合* List接口:* 1、有序的,可以重复* 2、允许多个 null 元素* 3、具体的实现有常用的:ArrayList,Vector,LinkedList* 在实际开发中,我们如何选择list的具体实现?* 1、安全性问题 Vector* 2、是否频繁插入,删除操作 LinkedList* 3、是否是存储后遍历 ArrayList** Set接口:* @description*/public class ListDemo {/*** LinkedList* 1、实现原理,采用双向链表结构实现* 2、适合插入,删除操作,性能高**/private static void linkedList(){LinkedList<String> list = new LinkedList<>();list.add("王老师");list.add("李老师");list.add("张老师");list.add("毕老师");for(int i=0;i<list.size();i++){System.out.println(list.get(i));}}/*** Vector* 1、实现原理,采用动态对象数组实现,默认构造方法创建了一个大小为10的对象数组* 2、扩充的算法:当增量为0时,扩充为原来大小的2倍,当增量大于0时,扩充为原来大小+增量* 3、不适合删除或插入操作* 4、为了防止数组动态扩充次数过多,建议创建Vector时,给定初始容量* 5、线程安全,适合在多线程访问时使用,在单线程下使用效率较低* 面试题 :Vector与ArrayList的区别?*/private static void vector(){Vector<String> v = new Vector<>();v.add("王老师");v.add("李老师");v.add("张老师");v.add("毕老师");for(int i=0;i<v.size();i++){System.out.println(v.get(i));}}/*** ArrayList(1.8)* 1、实现原理,采用动态对象数组实现,默认构造方法创建了一个空数组* 2、第一次添加元素,扩展容量为10,之后的扩充算法:原来数组大小+原来数组的一半* 3、不适合进行删除或插入操作* 4、为了防止数组动态扩充次数过多,建议创建ArrayList时,给定初始容量* 5、多线程中使用不安全,适合在单线程访问时使用,效率较高* JDK1.2开始*/private static void arrayList(){//使用集合来存储多个不同类型的元素(对象),那么在处理时会比较麻烦,实际开发中,不建议//这样使用,我们应该在一个集合中存储相同的类型对象List<String> list = new ArrayList<>();list.add("王老师");list.add("李老师");list.add(1,"张老师");list.add("毕老师");// list.add(10);//遍历集合int size = list.size();for(int i=0;i<size;i++){System.out.println(list.get(i));}System.out.println(list.contains("王老师"));list.remove("毕老师");System.out.println(list.size());list.set(0,"赵老师");int index = list.indexOf("李老师");System.out.println(index);String[] strings = list.toArray(new String[list.size()]);for(String str:strings){System.out.println(str);}System.out.println("--------------");String[] array = list.toArray(new String[]{});for(String s: array){System.out.println(s);}System.out.println("---------------");for(String str:list){System.out.println(str);}}public static void main(String[] args) {// arrayList();vector();// linkedList();}}
3、 集合框架Set接口
(1)Set接口
public interface Set<E> extends Collection<E>
一个不包含重复元素的 collection。 更确切地讲, set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2, 并且最多包含一个 null 元素。 正如其名称所暗示的, 此接口模仿了数学上的 set 抽象。
(2)HashSet
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable
类实现 Set 接口, 由哈希表(实际上是一个 HashMap 实例) 支持。 它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。 此类允许使用 null 元素
hashCode深入分析:
hashcode() 方法, 在object类中定义如下:
public native int hashCode();
hashCode是本地方法, 它的实现是根据本地机器相关, 当然我们可以在自己写的类中覆盖hashcode()方法, 比如String、 Integer、 Double… 等等这些类都是覆盖了hashcode()方法的。
在java的集合中, 判断两个对象是否相等的规则是:可通过重写hashCode以及equals
- 判断两个对象的hashCode是否相等如果不相等, 认为两个对象也不相等, 结束如果相等, 转入2
- 判断两个对象用equals运算是否相等如果不相等, 认为两个对象也不相等如果相等, 认为两个对象相等
(equals()是判断两个对象是否相等的关键)
没有get方法
(3)TreeSet
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, Serializable
基于 TreeMap 的 NavigableSet 实现。 使用元素的自然顺序对元素进行排序, 或者根据创建 set 时提供的 Comparator 进行排序, 具体取决于使用的构造方法。
(4)LinkedHashSet
public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, Serializable
具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。 此实现与 HashSet 的不同之外在于, 后者维护着一个运行于所有条目的双重链接列表。 此链接列表定义了迭代顺序,即按照将元素插入到 set 中的顺序(插入顺序) 进行迭代。 注意, 插入顺序不受在 set 中重新插入的 元素的影响。 (如果在 s.contains(e) 返回 true 后立即调用 s.add(e), 则元素 e 会被重新插入到 set s 中。 )
SetDemo
import java.util.HashSet;import java.util.LinkedHashSet;import java.util.Set;import java.util.TreeSet;/*** Set接口* 1、无序的(不保证顺序)* 2、不允许重复元素* HashSet、TreeSet、LinkedHashSet** 如果要排序,选择treeSet* 如果不要排序,也不用保正顺序选择HashSet* 不要排序,要保正顺序,选择LinkedHashSet* @description*/public class SetDemo {/*** 哈希表和链接列表实现,* 维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,即按照将元素插入到 set 中的顺序(插入顺序)进行迭代。*/private static void linkedHashSet(){LinkedHashSet<Cat> set = new LinkedHashSet<>();Cat c1 = new Cat("miaomiao",5,1);Cat c2 = new Cat("huahua",2,2);Cat c3 = new Cat("tom",5,3);Cat c4 = new Cat("miaomiao",3,1);set.add(c1);set.add(c2);set.add(c3);set.add(c4);for(Cat c: set){System.out.println(c);}}/*** 有序的,基于TreeMap(二叉树数据结构),对象需要比较大小,通过对象比较器来实现,* 对象比较器还可以用来去除重复元素,如果自定义的数据类,没有实现比较器接口,将无法添加到TreeSet集合中。*/private static void treeSet(){TreeSet<Cat> tree = new TreeSet<>(new CatComparator());Cat c1 = new Cat("miaomiao",5,1);Cat c2 = new Cat("huahua",2,2);Cat c3 = new Cat("tom",5,3);Cat c4 = new Cat("miaomiao",3,1);tree.add(c1);tree.add(c2);tree.add(c3);tree.add(c4);System.out.println(tree.size());for(Cat c: tree){System.out.println(c);}}/*** HashSet* 1、实现原理,基于哈希表(HashMap)实现* 2、不允许重复,可以有一个NULL元素* 3、不保证顺序恒久不变* 4、添加元素时把元素作为HashMap的key存储,HashMap的value使用一个固定的object对象* 5、排除重复元素是通过equals来检查对象是否相同* 6、判断两个对象是否相同,先判断两个对象的hashCode是否相同(如果两个对象的hashCode相同,不一定是同一个对象,如果不同,那一定不是* 同一个对象),如果不同,则两个对象不是同一个对象,如果相同,还要进行equals判断,equals相同则是同一个对象,不同则不是同一个对比象。* 7、自定义对象要认为属性值都相同时为同一个对象,有这种需求时,那么我们要重写对象所在类的hashCode和equals方法。** 小结* (1)哈希表的存储结构:数组+链表,数组里的每个元素以链表的形式存储* (2)如何把对象存储到哈希表中,先计算对象的hashCode值,再对数组的长度求余数,来决定对象要存储在数组中的哪个位置* (3)解决hashSet中的重复值使用的方式是,参考第6点*/private static void hashSet(){Set<String> set = new HashSet<>();set.add("飞飞");set.add("备备");set.add("亮亮");set.add("关关");set.add("曹操");set.add("亮亮");System.out.println(set.size());String[] names = set.toArray(new String[]{});for(String s: names){System.out.println(s);}Cat c1 = new Cat("miaomiao",5,1);Cat c2 = new Cat("huahua",2,2);Cat c3 = new Cat("tom",5,3);Cat c4 = new Cat("miaomiao",5,1); //c4与c1使用equals不同Set<Cat> cats = new HashSet<>();cats.add(c1);cats.add(c2);cats.add(c3);cats.add(c4);//cats.add(c1);System.out.println(cats.size());for(Cat c: cats){System.out.println(c);}System.out.println("c1="+c1.hashCode());System.out.println("c2="+c2.hashCode());System.out.println("c3="+c3.hashCode());System.out.println("c4="+c4.hashCode());}public static void main(String[] args) {// hashSet();// treeSet();linkedHashSet();}}
Cat
public class Cat {private String name;private int age;private int id;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public int getId() {return id;}public void setId(int id) {this.id = id;}@Overridepublic String toString() {return "Cat [name=" + name + ", age=" + age + ", id=" + id + "]";}public Cat(String name, int age, int id) {super();this.name = name;this.age = age;this.id = id;}public Cat() {super();// TODO Auto-generated constructor stub}@Overridepublic int hashCode() {final int prime = 31; //系数 31*1+5 * 31+1 11111int result = 1;result = prime * result + age;result = prime * result + id;result = prime * result + ((name == null) ? 0 : name.hashCode());return result;}@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;Cat other = (Cat) obj;if (age != other.age)return false;if (id != other.id)return false;if (name == null) {if (other.name != null)return false;} else if (!name.equals(other.name))return false;return true;}}
CatComparator
package com.vince;import java.util.Comparator;public class CatComparator implements Comparator<Cat> {@Overridepublic int compare(Cat o1, Cat o2) {return o1.getAge()-o2.getAge();}}
4、 集合框架Iterator接口
(1)集合输出
前面我们已经学习了集合的基本操作, 很多情况下, 我们需要把集合的内容进行输出, 也就是遍历集合。
遍历集合的方式有以下几种:
- Iterator
- ListIterator
- Enumeration
- foreach
其中Iterator的使用率最高, 在JDK1.5后新增的foreach也被大量使用。
(2)Iterator接口
public interface Iterator<E>
对 collection 进行迭代的迭代器。 迭代器取代了 Java Collections Framework 中的 Enumeration。

(3)ListIterator
public interface ListIterator<E>extends Iterator<E>
系列表迭代器, 允许程序员按任一方向遍历列表、 迭代期间修改列表, 并获得迭代器在列表中的当前位置

(4)Enumeration
public interface Enumeration<E>
实现 Enumeration 接口的对象, 它生成一系列元素, 一次生成一个。 连续调用nextElement 方法将返回一系列的连续元素。
注: 此接口的功能与 Iterator 接口的功能是重复的。 此外, Iterator 接口添加了一个可选的移除操作, 并使用较短的方法名。 新的实现应该优先考虑使用 Iterator 接口而不是Enumeration 接口。

(5)forEach
在前面的知识讲解中, 我们使用forEach来输出数组的内容, 那么也可以输出集合中的内容。 在使用forEach输出的时候一定要注意的是, 创建集合时要指定操作泛型的类型。
List<Integer> numbers = new ArrayList<>();
JDK1.8新特性:
//no.1numbers.forEach((Integer integer) -> {System.out.println(integer);});//no.2numbers.forEach(integer -> {System.out.println(integer);});//no.3numbers.forEach(integer -> System.out.println(integer));//no.4numbers.forEach(System.out::println);//no.5numbers.forEach(new MyConsumer());
IteratorDemo
import java.util.ArrayList;import java.util.Arrays;import java.util.Collection;import java.util.Enumeration;import java.util.Iterator;import java.util.List;import java.util.Vector;import java.util.function.Function;import java.util.function.Predicate;import java.util.function.Supplier;/*** 集合的输出(迭代)* @description*/public class IteratorDemo {//断言接口private static void predicateTest(){List<String> list = Arrays.asList("Larry", "Moe", "Curly","Tom","QF_vince");List<String> result = filter(list,(s)->s.contains("o"));result.forEach(System.out::println);}private static List<String> filter(List<String> list,Predicate<String> p){List<String> results = new ArrayList<>();for (String s : list) {if(p.test(s)){ //测试是否符合要求results.add(s);}}return results;}//Supplier 代表结果供应商private static void supplierTest(){List<Integer> list = getNums(10,()->(int)(Math.random() * 100));list.forEach(System.out::println);}private static List<Integer> getNums(int num,Supplier<Integer> sup){List<Integer> list = new ArrayList<>();for(int i=0;i<num;i++){list.add(sup.get());}return list;}//Function表示接受一个参数并产生结果的函数private static void functionTest(){String s = strToUpp("qf_vince",(str)->str.toUpperCase());System.out.println(s);}public static String strToUpp(String str,Function<String,String> f){return f.apply(str);}/*** JDK1.8新的迭代方法*/private static void foreach(){List<String> list = new ArrayList<>();list.add("tom");list.add("jack");list.add("job");list.add("lily");//Consumerlist.forEach(s->System.out.println(s));//list.forEach(System.out::println);}private static void enumeration(){Vector<String> vs = new Vector<>();vs.add("tom");vs.add("jack");vs.add("bob");vs.add("lily");Enumeration<String> es = vs.elements();while(es.hasMoreElements()){System.out.println(es.nextElement());}}//foreach(1.5后)private static void foreach(Collection<Cat> c){for(Cat cat: c){System.out.println(cat);}}//iterator(1.5之前统一的迭代集合方式)private static void iterator(Collection<Cat> c){Iterator<Cat> iter = c.iterator();while(iter.hasNext()){System.out.println(iter.next());}}public static void main(String[] args) {List<Cat> list = new ArrayList<>();Cat c1 = new Cat("miaomiao",5,1);Cat c2 = new Cat("huahua",2,2);Cat c3 = new Cat("tom",5,3);Cat c4 = new Cat("miaomiao",3,1);list.add(c1);list.add(c2);list.add(c3);list.add(c4);// iterator(list);// foreach(list);// enumeration();// foreach();// functionTest();// supplierTest();// predicateTest();String[] strs={"ni","hao","aho","oerh"};List<String> li=Arrays.asList(strs); //使用Arrays.asList()制作的集合,其长度一定不能增加,故要将其转换// List<String> li=Arrays.asList("ni","hao","aho","oerh");ArrayList<String> li1=new ArrayList<>(li);li1.add("qwe");li1.forEach(System.out::println);List<String> li2=new ArrayList<>();for(String str:li1){if(str.contains("o")){li2.add(str);}}li2.forEach(System.out::println);}}
5、 JDK1.8新特性
Consumer接口 消费者接口
Function
Supplier接口 代表结果供应商。
Predicate接口 断言接口
(1)Stream概念
Stream是元素的集合, 这点让Stream看起来有些类似Iterator;
可以支持顺序和并行的对原Stream进行汇聚的操作;
我们可以把Stream当成一个高级版本的Iterator。 原始版本的Iterator, 用户只能一个一个的遍历元素并对其执行某些操作; 高级版本的Stream, 用户只要给出需要对其包含的元素执行什么操作, 比如“ 过滤掉长度大于10的字符串” 、 “ 获取每个字符串的首字母”等, 具体这些操作如何应用到每个元素上, 就给Stream就好了!
(2)Stream常见操作

import java.util.ArrayList;import java.util.Arrays;import java.util.List;import java.util.Optional;import java.util.stream.Collectors;import java.util.stream.Stream;/*** Stream接口:不是存储数据结构,数据源可以是一个集合,为了函数式编程创造 ,* 惰式执行,数据只能被消费一次** 两种类型的操作方法:* 1、中间操作(生成一个Stream)* 2、结束操作(执行计算操作)* @description*/public class StreamDemo {public static void main(String[] args) {Stream<String> stream = Stream.of("good","good","study","day","day","up");//foreach方法//stream.forEach((str)->System.out.println(str));//stream.forEach(System.out::println);//filter 过滤//stream.filter((s)->s.length()>3).forEach(System.out::println);//distinct 去重//stream.distinct().forEach(s->System.out.println(s));//map 映射//stream.map(s->s.toUpperCase()).forEach(s->System.out.println(s));//flatMap 展平// Stream<List<Integer>> ss = Stream.of(Arrays.asList(1,2,3),Arrays.asList(4,5));// ss.flatMap(list->list.stream()).forEach(System.out::println);//reduce 归纳// Optional<String> opt = stream.reduce((s1,s2)->s1.length()>=s2.length()?s1:s2);// System.out.println(opt.get());//collect 将stream转为List// List<String> list = stream.collect(Collectors.toList());// list.forEach(s->System.out.println(s));//集合转为流,筛选后,再转为数组//数组转为流,获取最小值List<Integer> li=Arrays.asList(1,5,4,2,3);Integer[] integers = li.stream().filter(s -> s > 2).toArray(Integer[]::new);Optional<Integer> reduce = Arrays.stream(integers).reduce((s1, s2) -> (s1 > s2 ? s1 : s2));System.out.println(reduce.get());//排序后转为数组Integer[] integers1 = li.stream().sorted().toArray(Integer[]::new);//:: 方法引用//引用静态方法 Integer::valueOf//引用对象的方法 list::add//引用构造方法 ArrayList::new}}
6、 集合框架Map接口
(1)Map接口
public interface Map<K,V>
将键映射到值的对象, 一个映射不能包含重复的键; 每个键最多只能映射到一个值。


(2)HashMap
public class HashMap<K,V> extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable
基于哈希表的 Map 接口的实现。 此实现提供所有可选的映射操作, 并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外, HashMap 类与 Hashtable 大致相同。 ) 此类不保证映射的顺序, 特别是它不保证该顺序恒久不变
(3)Hashtable
public class Hashtable<K,V> extends Dictionary<K,V>implements Map<K,V>, Cloneable, Serializable
此类实现一个哈希表, 该哈希表将键映射到相应的值。 任何非 null 对象都可以用作键或值。
为了成功地在哈希表中存储和获取对象, 用作键的对象必须实现 hashCode 方法和 equals 方法
(4)LinkedHashMap
public class LinkedHashMap<K,V>extends HashMap<K,V> implements Map<K,V>
Map 接口的哈希表和链接列表实现, 具有可预知的迭代顺序。 此实现与 HashMap 的不同之处在于, 后者维护着一个运行于所有条目的双重链接列表。
(5)TreeMap
public class TreeMap<K,V> extends AbstractMap<K,V>implements NavigableMap<K,V>, Cloneable, Serializable
基于红黑树(Red-Black tree) 的 NavigableMap 实现。 该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序, 具体取决于使用的构造方法。
(6)1.8 Map接口的新方法
在JDK8中Map接口提供了一些新的便利的方法。 因为在本文中我所提到的所有Map方法都是以默认值方法的方式实现的, 所以现有的Map接口的实现可以直接拥有这些在默认值方法中定义的默认行为, 而不需要新增一行代码。
- getOrDefault(Object, V)
- putIfAbsent(K,V)
- remove(Object key, Object value)
- replace(K,V)
- replace(K,V,V)
- compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)
- computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)
- merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)
MapDemo
package com.vince;import java.util.Collection;import java.util.HashMap;import java.util.Hashtable;import java.util.LinkedHashMap;import java.util.Map;import java.util.Map.Entry;import java.util.Set;import java.util.TreeMap;/*** Map接口:* 1、键值对存储一组对象* 2、Key不能重复(唯一),Value可以重复* 3、具体的实现类:HashMap TreeMap Hashtable LinkedHashMap* 4、HashMap 与 Hashtable的区别?* 5、如何选择使用哪个?* 排序且选择二叉树存储:TreeMap* 不要求排序且单线程:HashMap* 多线程:Hashtable* 保证顺序;LinkedHashMap* 6、数据结构:数组、链表、二叉树(红黑树)、哈希表(数组+链表)、栈、队列* @description*/public class MapDemo {/*** 基于二叉树的红黑树实现*/private static void treeMap(){Map<String,String> map = new TreeMap<>();// map.put("one", "Lily");// map.put("two", "Tom");// map.put("three", "Bin");// map.forEach((key,value)->System.out.println(key+"->"+value));//Map<Dog,String> dogs = new TreeMap<>();dogs.put(new Dog(1,"2ha",3), "dog1");dogs.put(new Dog(1,"wangwang",2), "dog2");dogs.put(new Dog(3,"hsq",4), "dog3");dogs.forEach((key,value)->System.out.println(key+"->"+value));}/*** LinkedHashMap是HashMap的子类,由于HashMap不能保正顺序恒久不变,此类使用一个双重链表来维护* 元素添加的顺序。*/private static void linkedHashMap(){Map<String,String> table = new LinkedHashMap<>();table.put("one", "Lily");table.put("two", "Tom");table.put("three", "Bin");table.forEach((key,value)->System.out.println(key+"->"+value));}/*** JDK1.0开始* 基于哈希表实现(数组+链表)* 默认数组大小为11,加载因子0.75* 扩充方式:原数组大小<<1 (*2) +1* 线程安全的,用在多线程访问时*/private static void hashtable(){Map<String,String> table = new Hashtable<>();table.put("one", "Lily");table.put("two", "Tom");table.put("three", "Bin");table.forEach((key,value)->System.out.println(key+"->"+value));}/*** HashMap的现实原理:* 1、基于哈希表(数组+链表+二叉树(红黑树))1.8JDK* 2、默认加载因子为0.75,默认数组大小是16,加载因子含义:如果空间的75%被存满,则该数组空间已经被存满* 3、把对象存储到哈希表中,如何存储?* 把key对象通过hash()方法计算hash值,然后用这个hash值对数组长度取余数(默认16),来决定该对KEY对象* 在数组中存储的位置 ,当这个位置 有多个对象时,以链表结构存储,JDK1.8后,当链表长度大于8时,链表将转换为* 红黑树结构存储。* 这样的目的,是为了取值更快,存储的数据量越大,性能的表现越明显* 4、扩充原理:当数组的容量超过了75%,那么表示该数组需要扩充,如何扩充?* 扩充的算法是:当前数组容量<<1 (相当于是乘2),扩大1倍, 扩充次数过多,会影响性能,每次扩充表示哈希表重新* 散列(重新计算每个对象的存储位置),我们在开发中尽量要减少扩充次数带来的性能问题。* 5、线程不安全,适合在单线程中使用*/private static void hashMap(){Map<Integer,String> map = new HashMap<>();map.put(1, "Tom");map.put(2, "Jack");map.put(3, "Vince");map.put(4, "Bin");map.put(5, "Lily");System.out.println("size="+map.size());//从MAP中取值System.out.println(map.get(1));//通过key取value//map的遍历//1 遍历EntrySet<Entry<Integer,String>> entrySet = map.entrySet();for(Entry e: entrySet){System.out.println(e.getKey()+"->"+e.getValue());}System.out.println("--------");//2 遍历键Set<Integer> keys = map.keySet();for(Integer i: keys){String value = map.get(i);System.out.println(i+"->"+value);}System.out.println("--------");//3 遍历值Collection<String> values = map.values();for(String value: values){System.out.println(value);}System.out.println("--------");//4 foreachMap<Integer,String> map1=new HashMap<>();map.forEach((key,value)->{if(key>3){map1.put(key+1,value);}});map1.forEach((key,value)->System.out.println(key+"->"+value));System.out.println(map.containsKey(7));//hashInteger key = 1434;System.out.println( 1434 % 16 );}public static void main(String[] args) {hashMap();// hashtable();// treeMap();}}
Dog
package com.vince;public class Dog implements Comparable<Dog>{private int id;private String name;private int age;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Dog(int id, String name, int age) {super();this.id = id;this.name = name;this.age = age;}public Dog() {super();// TODO Auto-generated constructor stub}@Overridepublic String toString() {return "Dog [id=" + id + ", name=" + name + ", age=" + age + "]";}@Overridepublic int compareTo(Dog o) {return this.id-o.id;}}
MapNewMethodDemo
import java.util.HashMap;import java.util.Map;/*** Map接口1.8新增方法介绍* @description*/public class MapNewMethodDemo {public static void main(String[] args) {Map<Integer,String> map = new HashMap<>();map.put(1, "jack");map.put(2, "tom");map.put(3, "lily");String value1 = map.getOrDefault(4, "null");System.out.println(value1);// String s = map.get(5);// if(s==null){// System.out.println();// }else{//// }String val = map.put(3,"vince");System.out.println(val); //返回以前的值map.forEach((key,value)-> System.out.println(key+"->"+value));//只会添加不存在相同key的值String val1 = map.putIfAbsent(3, "vince");System.out.println(val1);map.forEach((k,v)->System.out.println(k+"->"+v));//根据键和值都匹配时才删除map.remove(1, "jack");//替换map中的值map.replace(3, "vince");map.replace(3, "lily", "vince");//计算操作数据map.compute(1, (k,v1)->v1+"1");map.computeIfAbsent(5,(val2)->val2+"test");//合并map.merge(8, "888", (oldVal,newVal)->oldVal.concat(newVal));map.forEach((k,v)->System.out.println(k+"->"+v));}}
7、 Collections工具类
Collections类
Collections工具类提供了大量针对Collection/Map的操作, 总体可分为四类, 都为静态(static) 方法:
(1)排序操作(主要针对List接口相关)
- reverse(List list): 反转指定List集合中元素的顺序
- shuffle(List list): 对List中的元素进行随机排序(洗牌)
- sort(List list): 对List里的元素根据自然升序排序
- sort(List list, Comparator c): 自定义比较器进行排序
- swap(List list, int i, int j): 将指定List集合中i处元素和j出元素进行交换
- rotate(List list, int distance): 将所有元素向右移位指定长度, 如果distance等于size那么结果不变
(2)查找和替换(主要针对Collection接口相关)
- binarySearch(List list, Object key): 使用二分搜索法, 以获得指定对象在List中的索引, 前提是集合已经排序
- max(Collection coll): 返回最大元素
- max(Collection coll, Comparator comp): 根据自定义比较器, 返回最大元素
- min(Collection coll): 返回最小元素
- min(Collection coll, Comparator comp): 根据自定义比较器, 返回最小元素
- fill(List list, Object obj): 使用指定对象填充
- frequency(Collection Object o): 返回指定集合中指定对象出现的次数
- replaceAll(List list, Object old, Object new): 替换
(3)同步控制
Collections工具类中提供了多个synchronizedXxx方法, 该方法返回指定集合对象对应的同步对象, 从而解决多线程并发访问集合时线程的安全问题。 HashSet、 ArrayList、 HashMap都是线程不安全的, 如果需要考虑同步,则使用这些方法。 这些方法主要有: synchronizedSet、 synchronizedSortedSet、 synchronizedList、synchronizedMap、 synchronizedSortedMap。
特别需要指出的是, 在使用迭代方法遍历集合时需要手工同步返回的集合
(4)设置不可变集合
Collections有三类方法可返回一个不可变集合:
- emptyXxx(): 返回一个空的不可变的集合对象
- singletonXxx(): 返回一个只包含指定对象的, 不可变的集合对象。
- unmodifiableXxx(): 返回指定集合对象的不可变视图
(5)其它
- disjoint(Collection c1, Collection c2) - 如果两个指定 collection 中没有相同的元素, 则返回 true。
- addAll(Collection<? super T> c, T… a) - 一种方便的方式, 将所有指定元素添加到指定 collection 中。
- Comparator reverseOrder(Comparator cmp) - 返回一个比较器, 它强行反转指定比较器的顺序。 如果指定比较器为 null, 则此方法等同于 reverseOrder()(换句话说, 它返回一个比较器, 该比较器将强行反转实现Comparable 接口那些对象 collection 上的自然顺序)。
import java.util.ArrayList;import java.util.Collections;import java.util.List;/*** Collections工具类* @description*/public class CollectionsDemo {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("jack");list.add("tom");list.add("lily");list.add("lily");System.out.println(list); //[jack, tom, lily, lily]//(1)排序操作//反转数组// Collections.reverse(list); //[lily, lily, tom, jack]//随机排序// Collections.shuffle(list); //[lily, lily, jack, tom]//sort按自然的升序排序// Collections.sort(list); //[jack, lily, lily, tom]//再多传一个排序对象// Collections.sort(list, c);//交换两个值// Collections.swap(list, 0, 2); //[lily, tom, jack, lily]//旋转,将所有元素向右移动指定位置// Collections.rotate(list, 1); //[lily, jack, tom, lily]//(2)查找替换//找某个值// System.out.println(Collections.binarySearch(list, "tom")); //1//返回最大值// System.out.println(Collections.max(list)); //tom//返回最小值// System.out.println(Collections.min(list)); //jack//填充// Collections.fill(list, "bin"); //[bin, bin, bin, bin]//返回集合中指定对象出现的次数// System.out.println(Collections.frequency(list, "lily")); //2//替换// Collections.replaceAll(list, "lily", "bin"); //[jack, tom, bin, bin]//(3)同步控制// List<String> syncList = Collections.synchronizedList(new ArrayList<String>());//(4)设置不可变集合// List<String> sList = Collections.emptyList();// sList.add("bin"); //会报错//(5)反转后排序:倒序// Collections.sort(list,Collections.reverseOrder()); //[tom, lily, lily, jack]}public static List<String> query(){List<String> list = null;//查不到,返回一个空集合return Collections.emptyList();}}
8、Optional容器类(JDK1.8)
Optional容器类(JDK1.8)
这是一个可以为null的容器对象。 如果值存在则isPresent()方法会返回true, 调用get()方法会返回该对象
- of:为非null的值创建一个Optional。
- ofNullable:为指定的值创建一个Optional, 如果指定的值为null, 则返回一个空的Optional。
- isPresent:如果值存在返回true, 否则返回false。
- get:如果Optional有值则将其返回, 否则抛出NoSuchElementException。
- ifPresent:如果Optional实例有值则为其调用consumer, 否则不做处理
- orElse:如果有值则将其返回, 否则返回指定的其它值
- orElseGet:orElseGet与orElse方法类似, 区别在于得到的默认值。 orElse方法将传入的字符串作为默认值,orElseGet方法可以接受Supplier接口的实现用来生成默认值。
- orElseThrow:如果有值则将其返回, 否则抛出supplier接口创建的异常。
- map:如果有值, 则对其执行调用mapping函数得到返回值。 如果返回值不为null, 则创建包含mapping返回值的Optional作为map方法返回值, 否则返回空Optional。
- flatMap:如果有值, 为其执行mapping函数返回Optional类型返回值, 否则返回空Optional。 flatMap与map(Funtion) 方法类似, 区别在于flatMap中的mapper返回值必须是Optional。 调用结束时,flatMap不会对结果用Optional封装。
- filter:如果有值并且满足断言条件返回包含该值的Optional, 否则返回空Optional
package com.vince;import java.util.Optional;/*** Optional JDK1.8容器类* @description*/public class OptionalDemo {public static void main(String[] args) {//创建Optional对象的方式Optional<String> optional = Optional.of("bin");// Optional<String> optional2 = Optional.ofNullable("bin");Optional<String> optional3 = Optional.empty();System.out.println(optional.isPresent()); //trueSystem.out.println(optional.get()); //binoptional.ifPresent((value)->System.out.println(value)); //如果有值则调用 //binSystem.out.println(optional.orElse("无值")); //如果有值则返回,否则返回其他值 //binSystem.out.println(optional.orElseGet(()->"default")); //设定一个默认值 //bin// try {// optional3.orElseThrow(Exception::new);// } catch (Exception e) {// e.printStackTrace();// }Optional<String> optional4 = optional.map((value)->value.toUpperCase()); //BINSystem.out.println(optional4.orElse("no found"));optional4 = optional.flatMap((value)->Optional.of(value.toUpperCase()+"-flatMap")); //BIN-flatMapSystem.out.println(optional4.orElse("no found"));optional4 = optional.filter((value)->value.length()>3); //这个值的长度小于3System.out.println(optional4.orElse("这个值的长度小于3"));}}
9、 Queue、Deque接口
队列(Queue) 是一种特殊的线性表, 是一种先进先出( FIFO) 的数据结构。 它只允许在表的前端( front) 进行删除操作, 而在表的后端( rear) 进行插入操作。 进行插入操作的端称为队尾, 进行删除操作的端称为队头。 队列中没有元素时, 称为空队列。
LinkedList是Queue接口的实现类
- boolean add(E e) : 将指定的元素插入此队列(如果立即可行且不会违反容量限制) , 在成功时返回 true, 如果当前没有可用的空间, 则抛出 IllegalStateException。
- E element() : 获取, 但是不移除此队列的头。
- boolean offer(E e) : 将指定的元素插入此队列(如果立即可行且不会违反容量限制),当使用有容量限制的队列时, 此方法通常要优于 dd(E), 后者可能无法插入元素, 而只是抛出一个异常。
- E peek() : 获取但不移除此队列的头; 如果此队列为空, 则返回 null。
- E poll() : 获取并移除此队列的头, 如果此队列为空, 则返回 null。
- E remove() : 获取并移除此队列的头。
Deque: 一个线性 collection, 支持在两端插入和移除元素。
此接口既支持有容量限制的双端队列, 也支持没有固定大小限制的双端队列。
接口定义在双端队列两端访问元素的方法。 提供插入、 移除和检查元素的方法
import java.util.Deque;import java.util.LinkedList;import java.util.Queue;import java.util.Stack;/*** Queue接口:队列,是一种先进先出的线性数据结构(排队)* LinkedList类实现了queue接口* 请求队列,消息队列,任务** Deque接口:双端队列* Stack:堆栈 :先进后出* @description*/public class QueueDequeDemo {private static void stack(){//堆栈:先进后出Stack<String> s = new Stack<>();//压栈s.push("Bin");s.push("Tom");s.push("Lily");System.out.println(s.pop()); //LilySystem.out.println(s.peek()); //TomSystem.out.println(s.pop()); //TomSystem.out.println(s.pop()); //Bin}private static void deque(){//双端队列Deque<String> deque = new LinkedList<>();deque.add("小花");deque.add("小黑");deque.add("小小");deque.add("小白");deque.add("小丽");System.out.println(deque.getFirst()); //小花System.out.println(deque.getLast()); //小丽}private static void queue(){//队列:先进先出Queue<String> queue = new LinkedList<>();//往队列里添加元素queue.add("小花");queue.add("小黑");queue.add("小小");queue.add("小白");queue.add("小丽");//队列的长度大小System.out.println(queue.size()); //5//获取队列的头,但不移除,若为空返回nullSystem.out.println(queue.peek()); //小花System.out.println(queue.size()); //5//获取队列的头,并删除System.out.println(queue.poll()); //小花System.out.println(queue.size()); //4System.out.println(queue);queue.offer("小明");System.out.println(queue);}public static void main(String[] args) {// queue();// deque();stack();}}
10、对象一对多与多对多关系
OneToManyDemo
/*** 对象的一对多关系** @description*/public class OneToManyDemo {public static void main(String[] args) {Teacher t1 = new Teacher("张老师",18,"女");Student s1 = new Student("小李",10);Student s2 = new Student("小王",12);Student s3 = new Student("小赵",11);//关联关系t1.getStudents().add(s1);t1.getStudents().add(s2);t1.getStudents().add(s3);s1.setTeacher(t1);s2.setTeacher(t1);s3.setTeacher(t1);print(t1);}private static void print(Teacher t1) {System.out.println(t1.getName());// for(Student s: t1.getStudents()){// System.out.println(s);// }t1.getStudents().forEach(System.out::println);}}
Student
/**** @description*/public class Student {private int sid;public int getSid() {return sid;}public void setSid(int sid) {this.sid = sid;}private String sname;private int age;private Teacher teacher;public Teacher getTeacher() {return teacher;}public void setTeacher(Teacher teacher) {this.teacher = teacher;}public String getSname() {return sname;}public void setSname(String sname) {this.sname = sname;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Student(String sname, int age) {super();this.sname = sname;this.age = age;}public Student() {super();// TODO Auto-generated constructor stub}@Overridepublic String toString() {return "Student [sname=" + sname + ", age=" + age + "]";}}
Teacher
import java.util.HashSet;//一个老师对应多个学生public class Teacher {private String name;private int age;private String sex;private HashSet<Student> students = new HashSet<>();public HashSet<Student> getStudents() {return students;}public void setStudents(HashSet<Student> students) {this.students = students;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}@Overridepublic String toString() {return "Teacher [name=" + name + ", age=" + age + ", sex=" + sex + "]";}public Teacher() {super();// TODO Auto-generated constructor stub}public Teacher(String name, int age, String sex) {super();this.name = name;this.age = age;this.sex = sex;}}
StudentAndCourse
public class StudentAndCourse {private int id;private int sid;private int cid;public int getId() {return id;}public void setId(int id) {this.id = id;}public int getSid() {return sid;}public void setSid(int sid) {this.sid = sid;}public int getCid() {return cid;}public void setCid(int cid) {this.cid = cid;}}
11、迭代器设计模式
提供一个方法按顺序遍历一个集合内的元素, 而又不需要暴露该对象的内部表示。
应用场景:
- 访问一个聚合的对象, 而不需要暴露对象的内部表示
- 支持对聚合对象的多种遍历
- 对遍历不同的对象, 提供统一的接口
迭代器模式的角色构成:
- 迭代器角色(Iterator) :定义遍历元素所需要的方法, 一般来说会有这么三个方法: 取得下一个元素的方法next(), 判断是否遍历结束的方法hasNext(), 移出当前对象的方法remove()
- 具体迭代器角色(Concrete Iterator) : 实现迭代器接口中定义的方法, 完成集合的迭代
- 容器角色(Aggregate): 一般是一个接口, 提供一个iterator()方法, 例如java中的Collection接口,List接口, Set接口等
- 具体容器角色(ConcreteAggregate) : 就是抽象容器的具体实现类, 比如List接口的有序列表实现ArrayList, List接口的链表实现LinkedList, Set接口的哈希列表的实现HashSet等
Iterator
package iterator;/*** 迭代器的接口* @description*/public interface Iterator {public boolean hasNext();public Object next();}
ConcreteIterator
package iterator;/*** 迭代器接口的具体实现类* @description*/public class ConcreteIterator implements Iterator{private MyList list = null;private int index;//迭代器的指针public ConcreteIterator(MyList list) {this.list = list;}@Overridepublic boolean hasNext() {if(index>=list.getSize())return false;else return true;}@Overridepublic Object next() {Object obj = list.get(index);index++;return obj;}}
MyList
package iterator;/*** 容器的接口* @description*/public interface MyList {void add(Object e);Object get(int index);Iterator iterator();int getSize();}
ConcreteAggregate
package iterator;/*** 容器接口的具体实现类* @description*/public class ConcreteAggregate implements MyList{private Object[] elements;//对象数组private int size;private int index;public ConcreteAggregate() {elements = new Object[100];}@Overridepublic void add(Object e) {elements[index++] = e;size++;}@Overridepublic Object get(int index) {return elements[index];}@Overridepublic Iterator iterator() {return new ConcreteIterator(this);}@Overridepublic int getSize() {return size;}}
Test
package iterator;/*** @description*/public class Test {public static void main(String[] args) {MyList list = new ConcreteAggregate();list.add("刘备");list.add("张飞");list.add("关羽");list.add("曹操");list.add("诸葛亮");Iterator iter = list.iterator();while(iter.hasNext()){System.out.println(iter.next());}}}
12、guava对集合的支持
版本下载: https://repo1.maven.org/maven2/com/google/guava/guava/
Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库, 例如: 集合 [collections] 、 缓存 [caching] 、 原生类型支持 [primitives support] 、 并发库 [concurrency libraries] 、 通用注解 [common annotations] 、 字符串处理 [string processing] 、 I/O 等等。 所有这些工具每天都在被Google的工程师应用在产品服务中。
Guava对JDK集合的扩展, 这是Guava最成熟和为人所知的部分。
- 不可变集合: 用不变的集合进行防御性编程和性能提升。
- 新集合类型: multisets, multimaps, tables,等
- 强大的集合工具类: 提供java.util.Collections中没有的集合工具
- 扩展工具类: 让实现和扩展集合类变得更容易, 比如创建Collection的装饰器, 或实现迭代器
各种功能
- 只读设置
- 函数式编程:过滤器
- 函数式编程:转换
- 组合式函数编程
- 加入约束: 非空、 长度验证
- 集合操作: 交集、 差集、 并集
- Multiset: 无序可重复,可计算个数
- Multimap key可以重复
- BiMap: 双向Map(bidirectional Map) 键与值不能重复
- 双键的Map —>Table —->rowKey+columnKye+value
package com.guava;import java.text.SimpleDateFormat;import java.util.*;import java.util.stream.Collectors;import com.google.common.collect.*;import org.junit.Test;import com.google.common.base.Function;import com.google.common.base.Functions;import com.google.common.collect.Sets.SetView;import com.google.common.collect.Table.Cell;public class GuavaDemo {//双键的Map -->Table --->rowKey+columnKye+value@Testpublic void testGuava10(){Table<String, String, Integer> table = HashBasedTable.create();table.put("jack", "java", 80);table.put("tom", "php", 70);table.put("bin", "java", 59);table.put("lily", "ui", 98);Set<Cell<String,String,Integer>> cells = table.cellSet();for(Cell c: cells){System.out.println(c.getRowKey()+"-"+c.getColumnKey()+"-"+c.getValue());}//jack-java-80//tom-php-70//bin-java-59//lily-ui-98}//BiMap:双向Map(bidirectional Map) 键与值不能重复@Testpublic void testGuava9(){BiMap<String, String> map = HashBiMap.create();map.put("finally_test", "18201583398");map.put("bin_test", "18388881521");String name = map.inverse().get("18201583398"); //通过value取keySystem.out.println(name);System.out.println(map.inverse().inverse()==map); //true}// Multimap:key可重复@Testpublic void testGuava8() {Map<String,String> map = new HashMap<>();map.put("Java从入门到精通", "bin");map.put("Android从入门到精通", "bin");map.put("PHP从入门到精通", "jack");map.put("笑看人生", "vince");Multimap<String, String> mmap = ArrayListMultimap.create();Iterator<Map.Entry<String, String>> iter = map.entrySet().iterator();while(iter.hasNext()){Map.Entry<String, String> entry = iter.next();mmap.put(entry.getValue(), entry.getKey());}Set<String> keySet = mmap.keySet();for(String key: keySet){Collection<String> values = mmap.get(key);System.out.println(key+"->"+values);}//bin->[Java从入门到精通, Android从入门到精通]//vince->[笑看人生]//jack->[PHP从入门到精通]}// Multiset:无序可重复@Testpublic void testGuava7() {String s = "good good study day day up";String[] ss = s.split(" ");HashMultiset<String> set = HashMultiset.create();for (String str : ss) {set.add(str);}Set<String> set2 = set.elementSet();for (String str : set2) {System.out.println(str + ":" + set.count(str));}//study:1//up:1//good:2//day:2}// 集合操作:交集、差集、并集@Testpublic void testGuava6() {Set<Integer> set1 = Sets.newHashSet(1, 2, 3);Set<Integer> set2 = Sets.newHashSet(3, 4, 5);// 交集SetView<Integer> v1 = Sets.intersection(set1, set2);// v1.forEach(System.out::println);// 差集SetView<Integer> v2 = Sets.difference(set1, set2);// v2.forEach(System.out::println);// 并集SetView<Integer> v3 = Sets.union(set1, set2);v3.forEach(System.out::println);}/*** 加入约束:非空、长度验证*/@Testpublic void testGuava5() {// Set<String> set = Sets.newHashSet();// 14版可用// Constraint<String> constraint = new Constraint<>(){// @Override// public String checkElement(String element){//// }// }//检查参数// Preconditions.checkArgument(expression);// Preconditions.checkNotNull(reference)}/*** 组合式函数*/@Testpublic void testGuava4() {List<String> list = Lists.newArrayList("java", "php", "jack", "h5", "javascript");Function<String, String> f1 = new Function<String, String>() {@Overridepublic String apply(String t) {return t.length() > 4 ? t.substring(0, 4) : t;}};Function<String, String> f2 = new Function<String, String>() {@Overridepublic String apply(String t) {return t.toUpperCase();}};Function<String, String> f = Functions.compose(f1, f2);Collection<String> coll = Collections2.transform(list, f);coll.forEach(System.out::println);// System.out.println(list);// list=list.stream().filter((l)->(l.length()>=4)).map((l)->(l.toUpperCase())).collect(Collectors.toList());// System.out.println(list);}/*** 转换*/@Testpublic void testGuava3() {Set<Long> timeSet = Sets.newHashSet(20121212L, 20170520L, 20160808L);Collection<String> timeCollect = Collections2.transform(timeSet,(e) -> new SimpleDateFormat("yyyy-MM-dd").format(e));timeCollect.forEach(System.out::println);}/*** 过滤器*/@Testpublic void testGuava2() {List<String> list = Lists.newArrayList("java", "php", "jack", "h5", "javascript");Collection<String> result = Collections2.filter(list, (e) -> e.startsWith("j"));result.forEach(System.out::println);// List<String> li= Arrays.asList("java", "php", "jack", "h5", "javascript");// li= li.stream().filter((e) -> (e.startsWith("j"))).collect(Collectors.toList());// System.out.println(li);}/*** 设置 只读*/@Testpublic void testGuava1() {System.out.println("test guava");// List<String> list = Arrays.asList("jack","tom","lily","bin");// list.add("vince");List<String> list = new ArrayList<>();list.add("jack");list.add("tom");list.add("lily");list.add("bin");//变为只读// List<String> readList = Collections.unmodifiableList(list);// readList.add("vince");//guava实现只读ImmutableList<String> iList = ImmutableList.of("jack","tom","lily","bin");iList.add("vince");}}
