Java_4_Lambda_泛型_集合

Lambda表达式

Lambda语法

概念:Lambda表达式是生成函数式接口的匿名实现类实例对象的语法糖,可理解为将一段可传输的代码。 Lambda表达式可以看作是一个匿名函数,也可称为闭包。Lambda表达式可以写出更简洁、更灵活的代码,而其作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

表达式成员:lambda形参列表、箭头、lambda方法体

语法:

  1. 无参,无返回值

    Runnable runnable = () -> System.out.println("Runnable");
    
  2. 一参,无返回值

    Consumer<String> c = x -> System.out.println("Consumer");
    
  3. 两参,多语句

    Comparator<Integer> c = (x,y) -> {
    	System.out.println("Comparator");
    	return Integer.compare(x,y);
    }//形参列表数据类型可不写,JVM可自动根据上下文推断
    
  4. 两参,仅有一条返回值语句

    Comparator<Integer> c = (x,y) -> Integer.compare(x,y);
    
函数式接口

概念:仅有一个抽象方法的接口,称之为函数式接口,可通过Lambda表达式语法快速生成函数式接口的匿名内部实现类的实例(即Lambda本质是函数式接口的实例)。函数式接口可用==@FunctionalInterface==注解以此来检查是否定义为函数式接口,并在Javadoc中声明其为函数式接口。java.util.function提供丰富的函数式接口

四大核心接口:

其他接口:

方法引用

原理:当需要传递给Lambda体的操作,已经有实现的方法了,可使用方法引用。方法引用可视为Lambda深层次的表达,方法引用就是Lambda表达式,是函数式接口的实例,通过方法名字来指向一个方法,可以认为Lambda的语法糖

要求:实现接口的抽象方法的参数列表和返回值类型,与方法引用的方法参数列表与返回值类型一致

语法:

  1. 通过对象对象名::实例方法名
  2. 通过类静态方法类名::静态方法名
  3. 通过类成员方法类名::实例方法

实例:

//1.对象::实例方法名
//Consumer中的void accept(T t)
//ps对象的void println(T t)
PrintStream ps = System.out;
Consumer<String> con1 = ps::prinltn;
con1.accept("lxl");//控制台输出lxl

//2,类::静态方法名
//Comparator中的int compare(T t1,T t2)
Interger中的static int compare(T t1,T t2)
Comparator<Integer> com1 = Integer::compare;
com1.compare(12,13);//返回-1

//3.类::实例方法名
//Comparator中的int compare(T t1,T t2)
//String中的int compareTo(T t)
//String的compareTo方法用t1调用,t2为参数,该情况可使用方法引用
Comparator<String> com2 = String::compareTo;
com2.compare("abc","abd");//返回-1

//Function中的R apply(T t)
//Employee类中的String getName()
//Employee的getName方法用t调用,该情况可使用方法引用
Employee emp = new Employee("lxl")Function<Employee,String> fn = Employee::getName;
fn.apply(emp);//返回lxl


//构造器引用
//Employee中的public Employee()构造器
//Supplier<T>中的T get()返回获取Employee对象
Supplier<Employee> sup = new Supplier<Employee>(){//原始写法
    @Override
    public Employee get(){
        return new Employee();
    }
}
Supplier<Employee> sup = () -> new Employee();//Lambda表达式写法
Supplier<Employee> sup = Employee::new;//构造器引用写法

//Employee中的public Employee(String name)构造器
//Function<T,R>中的R apply(T t)返回获取Employee对象
Function<String,Employee> fn = Employee::new;
//此时fn的Employee apply方法参数列表为String t,对应Employee(String name)构造器

//Employee中的public Employee(int id,String name)构造器
//BiFunction<T,U,R>中的R apply(T t,U u)返回获取Employee对象
BiFunction<Integer,String,Employee> fn = Employee::new;
//此时fn的Employee apply方法参数列表为Integer t,String u
//对应Employee(int id,String name)构造器


//Lambda表达式写法
Function<Integer,String[]> fn1 = length -> new String[length];
//数组引用写法
Function<Integer,String[]> fn2 = Stirng[] :: new;
//即将数组视为一个特殊的类

泛型

泛型介绍
  1. 泛型又称参数化类型,是JDK5.0出现的新特性,解决数据类型安全性问题
  2. 在类声明和实例化时只需指定好具体类型即可
  3. Java泛型若保证编译时没有发出警告,运行时就不会产生
  4. 泛型的作用是:可在类声明时通过一个标识指定类中某个属性的类型,或是某个方法的返回值类型,或是参数类型
  5. 泛型表示的数据类型,是在类对象定义时指定(即是编译期指定)
  6. 泛型仅仅只是针对编译阶段有效!在运行时期是会被擦除的(类型擦除)
泛型注意事项
1.给泛型指定数据类型,必须是引用类型,不可为基本数据类型
2.给泛型指定具体类型后,可以传入该类型或其子类类型
3.泛型的使用形式:
	(1)ArrayList<Integer> list1 = new ArrayList<Integer>();
	(2)List<Integer> list2 = new ArrayList<Integer>();
	(3)ArrayList<Integer> list1 = new ArrayList<>(); 推荐使用简写
4.若定义类时使用了泛型,但是创建类对象时未传入类型,则泛型的数据类型默认为Object
自定义泛型
自定义泛型类
基本语法:
class 类名<T>{}

注意事项:
1.普通成员可以使用泛型(属性、方法)
2.使用泛型的数组,不能初始化
3.静态方法中不能使用类的泛型
4.泛型类的类型,在创建对象时确定(new 类名<泛型类型>)
5.若泛型无指定类型,默认为Object
-------------------------------------------------------------------------
自定义泛型接口
基本语法:
interface 接口名<T,R>{
	public T get(){};
}

注意事项:
1.接口中静态成员也不能使用泛型
2.泛型接口的类型,可在继承接口或实现接口时确定
3.始终不确定泛型的类型,可直到创建对象时确定泛型的类型	
-------------------------------------------------------------------------
自定义泛型方法
基本语法:
修饰符 <T,R>返回类型 方法名(T t,R r){}

注意细节:
1.泛型方法可以定义在普通类中,也可定义在泛型类中
2.当泛型方法被调用时,类型会确定(编译器根据传入参数确定泛型)
3.public void eat(E e){},不是泛型方法,只是使用了泛型
4.泛型方法可以使用类声明的泛型,也可以使用自己声明的泛型
泛型继承与通配符
  1. 泛型不具有继承性
  2. 泛型<?>支持任意的泛型类型
  3. <? extends A>支持A类及其子类,规定泛型上限
  4. <? super A支持A类及其父类,不限于直接父类,规定泛型下限

集合的使用

集合体系图

Collection接口概述

在这里插入图片描述

Iterator迭代器

在这里插入图片描述

List接口及其实现类

List集合选型:

集合底层结构增删效率查询效率
ArrayList可变数组
LinkedList双向链表
  1. 改查多选ArrayList,增删多选LinkedList
  2. 一般开发情况查询较多,大部分选择ArrayList
  3. 根据业务灵活选择
ArrayList类使用与解析

在这里插入图片描述

常用方法:

public boolean add(E e); // 直接在集合的末尾添加元素数据e
public void add(int index,E e); // 在集合指定index索引位置添加元素数据e 
public E get(int index); // 获得集合总指定index索引位置的元素数据
public E remove(int index); // 删除集合中指定index索引位置的元素数据
public boolean remove(Object obj); // 删除集合中指定的元素数据obj
public E set(int index,E e); // 将集合index索引位置的元素数据修改为e
public int size();  // 获得集合里面元素数据的个数!
public int indexOf(Object o);  //返回o在集合中第一次出现的索引,使用equals比较

集合构造器源码:

public ArrayList(int initialCapacity) {
	if (initialCapacity > 0) {
		this.elementData = new Object[initialCapacity];
 } else if (initialCapacity == 0) {
		this.elementData = EMPTY_ELEMENTDATA;
//private static final Object[] EMPTY_ELEMENTDATA = {};
	} else {
		throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
	}
}

public ArrayList() {
	this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
 //private static final Object[] EMPTY_ELEMENTDATA = {};
}

集合扩容源码:

public boolean add(E e) {
  ensureCapacityInternal(size + 1);
  elementData[size++] = e;
  return true;
}

private void ensureCapacityInternal(int minCapacity) {
	ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));//calculateCapacity中判断是否是第一次扩容
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
	if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
  //DEFAULTCAPACITY_EMPTY_ELEMENTDATA为fianl空数组
		return Math.max(DEFAULT_CAPACITY, minCapacity);
  //DEFAULT_CAPACITY为10,判断是否是第一次扩容
	}
	return minCapacity;//返回添加后的集合容量
}

private void ensureExplicitCapacity(int minCapacity) {
	modCount++;//记录集合(elementData)修改次数
	if (minCapacity - elementData.length > 0)
  //判断是否需要扩容
		grow(minCapacity);
}
}

private void grow(int minCapacity) {
	int oldCapacity = elementData.length;
	int newCapacity = oldCapacity + (oldCapacity >> 1);
//扩容算法
	if (newCapacity - minCapacity < 0)
  //判断扩容后容量是否满足需求
		newCapacity = minCapacity;
	if (newCapacity - MAX_ARRAY_SIZE > 0)
  //判断扩容后容量是否大于数组类型最大长度
		newCapacity = hugeCapacity(minCapacity);
//实际上的数组扩容
	elementData = Arrays.copyOf(elementData, newCapacity);
}
Vector类使用与解析

概述:

  1. Vector底层是一个Object数组,protected Object[] elementData;
  2. Vector是线程同步(线程安全),Vector类操作方法带有synchronized
  3. 开发中考虑线程安全时,可使用Vector
  4. 无参构造器生成的Vector对象,默认容量为10;有参构造器生成的Vector对象容量为指定大小;之后每次扩容后的大小为原来的2倍

集合扩容源码:

public synchronized boolean add(E e){
    modCount++;//记录该Vector对象修改次数
    ensureCapacityHelper(elementCount+1);
    //elementCount为数组当前使用的容量
    elementData[elementCount++]=e;
    return true;
}
private void ensureCapacityHelper(int minCapacity){
    if(minCapacity - elementDate.length > 0){
        //minCapacity为增加后的数组容量
        grow(minCapacity);//如果需要容量小于数组长度则扩容
    }
} 
public void grow(int minCapacity){
    int oldCapacity = elementData.length;//记录原来数组长度
    int newCapacity = oldCapacity+((capacityIncrement>0)?capacityIncrement:oldCapacity);//capacityIncrement为0,
    if(newCapacity-minCapacity<0){//判断新容量是否小于需要容量
        newCapacity = minCapacity;
    }
    if(newCapacity - MAX_ARRAY_SIZE>0){
        //判断新容量是否大于数组类型最大长度
        newCapacity = hugeCapacity(minCapacity);
    }
    elementData = Arrays.copyOf(elementData,newCapacity);
    //使用Arrays.copyOf实现扩容 
}
LinkedList类使用与解析

在这里插入图片描述

集合添加与删除源码:

public boolean add(E e) {
	linkLast(e);
	return true;
}
void linkLast(E e) {
	final Node<E> l = last;//保存last引用
	final Node<E> newNode = new Node<>(l, e, null);
 //将e对象为参数生成Node类型对象
	last = newNode;//将新Node对象引用给last变量
	if (l == null)
     //如果最初last为null,新Node对象即为第一个数据
		first = newNode;
	else
     //若非第一个,则将上个Node的next属性指向新Node对象
		l.next = newNode;
	size++;//LinkedList大小+1
	modCount++;//记录该LinkedList对象修改次数
}
public E remove() {
	return removeFirst();
}
public E removeFirst() {
	final Node<E> f = first;//f指向first指向的第一个结点
	if (f == null)//若第一个结点为null,抛出异常
		throw new NoSuchElementException();
	return unlinkFirst(f);//第一结点作为参数,调用unlinkFirst
}
private E unlinkFirst(Node<E> f) {
	final E element = f.item;
	final Node<E> next = f.next;//next指向第二结点
	f.item = null;
	f.next = null; 
	first = next; //first指向第一结点的next属性,即第二结点
	if (next == null)//若第二结点为null,则将last也置空
		last = null;//此时集合为空
	else
		next.prev = null;//第二结点的prev属性置空
	size--;//LinkedList大小-1
	modCount++;
	return element;//返回被删除Node的item,即被删的数据
}
Set接口及其实现类

在这里插入图片描述

Set集合选型:

集合是否有序去重规则
HashSet无序hasCode+equals
LinkedHashSet有序(存取一致)同上
TreeSet默认排序/自定义排序元素compareTo/比较器compare
HashSet类使用与解析

在这里插入图片描述

源码解析:

public HashSet(){
	map = new HashMap<>();//底层给map属性赋值一个新HashMap对象
}

public boolean add(E e) {
	return map.put(e, PRESENT)==null;
//PRESENT为静态属性,值为Object对象,用以占HashMap中k-v中v位置
}

public V put(K key, V value) {
	return putVal(hash(key), key, value, false, true);
}

static final int hash(Object key) {
	int h;
	return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
	//获得key的哈希值并处理,算法尽量避免出现相同的哈希值
}

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
	Node<K,V>[] tab; Node<K,V> p; int n, i;//定义辅助变量
	if ((tab = table) == null || (n = tab.length) == 0)
//table是HashMap中存储结点链表的数组
//该if语句判断table是否为空,为空则进行第一次扩容(16长度)
		n = (tab = resize()).length;        
	//resize方法对table进行扩容
	if ((p = tab[i = (n - 1) & hash]) == null)
  //1.根据key的hash值,计算该key在table表格中的索引
  //2.判断索引的位置是否为空
		tab[i] = newNode(hash, key, value, null);
	//为空则将key作为参数创建Node结点,并将Node结点存储到table中的相应索引的位置
	else {
		Node<K,V> e; K k;
		if (p.hash == hash &&((k == p.key) == key || (key != null && key.equals(k))))
  //判断p指向结点中数据是否与key相同(两个条件):
  //1.p指向结点中hash与被添加的key的hash相同,key与结点中的key属性为同一对象
  //2.key不为空,并调用key的equals与p结点的key比较结果为true
			e = p;
		else if (p instanceof TreeNode)
  //判断 p 是否是红黑树
			e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
		else {//当p指向的数组位置已经是一个链表时进入
			for (int binCount = 0; ; ++binCount) {
      //循环遍历链表
				if ((e = p.next) == null) {//第一循环e指向链表第二元素
         //发现p指向的下一位置为空时,key作为参数创建Node结点,并存储到该位置
					p.next = newNode(hash, key, value, null);
					if (binCount >= TREEIFY_THRESHOLD - 1) 	
             //添加新元素后,判断该链表长度是否大于8
                  treeifyBin(tab, hash);
              	//treeifyBin判断是否满足条件,满足时先table扩容,再将链表转为红黑树
					break;
				}
				if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))
         //发现被添加元素与链表中元素相同时,退出循环
					break;
				p = e;//将p指向后一个Node结点,准备下次循环
			} 
		}
		if (e != null) { 
  //e不为null,就说明元素没有添加成功
			V oldValue = e.value;//e.value是默认的空对象PRESENT
			if (!onlyIfAbsent || oldValue == null)
				e.value = value;
			afterNodeAccess(e);
  	HashMap中该方法为空,由HashMap子类实现并进行业务逻辑
			return oldValue;
		}
	}
	++modCount; //修改次数+1
	if (++size > threshold)
//HashMap的大小+1,并将新大小与临界值进行比较
		resize();
	//若增加元素后的HashMap大小比临界值大,则进行table扩容
	afterNodeInsertion(evict);
//HashMap中该方法为空,由HashMap子类实现并进行业务逻辑
	return null;
}
LinkedHashSet类使用与解析

在这里插入图片描述

源码解读:

static class Entry<K,V> extends HashMap.Node<K,V> {
	Entry<K,V> before, after;//用来存储上个结点,下个结点的地址
	Entry(int hash, K key, V value, Node<K,V> next) {
		super(hash, key, value, next);
	}
}

public LinkedHashSet() {//无参构造器,初始化
	super(16, .75f, true);
}
//上层调用父类的构造器
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
	map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
TreeSet类

原理概述:

  1. TreeSet底层是TreeMap,以红黑树的结构对数据进行存储,查询较快
  2. 若使用无参构造器创建TreeSet,是默认调用元素的compareTo方法进行排序
  3. 使用匿名内部类形式new Comparator(){}作为参数创建TreeSet集合后,可以通过比较器的compare方法中定义排序规则
  4. 不能添加null值

源码解读:

//1.TreeSet构造器将传入的Comparator对象,赋给底层TreeMap的comparator属性
public TreeSet(Comparator<? super E> comparator) {
	this(new TreeMap<>(comparator));
}
public TreeMap(Comparator<? super K> comparator) {
	this.comparator = comparator;
}

//2.调用add时,底层会执行
if (cpr != null) {
	do {
		parent = t;//t为树的根节点
		cmp = cpr.compare(key, t.key);//用传入的比较器进行比较
		if (cmp < 0)
          t = t.left;
      else if (cmp > 0)
          t = t.right;
      else//若出现相同元素,退出并setValue替换值
          return t.setValue(value);
   } while (t != null);
}
Map接口及其实现类

Map集合遍历方法:

Map map = new HashMap();

//第一组:先取出所有的key,通过key得到value
Set keyset = map.keySet();
//第一种方法:增强for
for (Object key : keyset) {
 System.out.println(map.get(key));
}
//第二种:迭代器
Iterator iterator1 = keyset.iterator();
while (iterator1.hasNext()) {
 Object key = iterator1.next();
 System.out.println(map.get(key));
}

//第二组:直接把所有的values取出
Collection values = map.values();
//第一种:增强for
for (Object value : values) {
 System.out.println(value);
}
//第二种:迭代器
Iterator iterator2 = values.iterator();
while (iterator2.hasNext()) {
 Object value = iterator2.next();
 System.out.println(value);
}

//第三组:通过EntrySet获取K-V
Set entryset = map.entrySet();
//第一种:增强for
for (Object entry : entryset) {
 //将Object类型的entry转成Map.Entry类型
 //entry实际运行类型是HashMap$Node
 Map.Entry m = (Map.Entry) entry;
 System.out.println(m.getKey()+"-"+m.getValue());
}
//第二种:迭代器
Iterator iterator3 = entryset.iterator();
while (iterator3.hasNext()) {
 Map.Entry m  = (Map.Entry)iterator3.next();
 //iterator3.next()返回的是HashMap$Node类型
 System.out.println(m.getKey()+"-"+m.getValue());
}
//猜想:不可以使用HashMap.Node的原因是:Node静态成员内部类权限修饰符为默认,而Map.Entry为Map接口中内部接口,接口中的方法都是默认public  abstract修饰且必须为public;接口中的属性都是默认public static final修饰
HashMap类使用与解析

注意事项:

  1. HashMap是Map接口使用频率最高的实现类,K-V对的方式存储数据
  2. Key可为且仅有一个null,不能复用;但Value可重复,允许null键值
  3. 若添加相同key,会覆盖原来的value,等同修改
  4. 与HashSet一样,不保证映射顺序,因其底层为hash表实现存储(数组+链表+红黑树)
  5. 无实现同步,非线程安全,需要线程安全的情况下可以使用ConcurrentHashMap
  6. 扩容机制与HashSet一致,因为其底层就是HashMap,源码可参照HashSet部分

Hashtable类使用与解析

源码分析:

//1.底层由数组Hashtable$Entry[],初始化为11
private transient Entry<?,?>[] table;
public Hashtable() {
	this(11, 0.75f);//无参初始化table长度为11
}

//2.临界值threshold = initialCapacity * 0.75
//initialCapacity为扩容时新数组长度

private void addEntry(int hash, K key, V value, int index) {
	modCount++;
	Entry<?,?> tab[] = table;
//3.扩容:当Hashtable元素个数大于等于临界值会触发扩容,扩容大小为2n+1
	if (count >= threshold) {//count为table数组中元素总数
		rehash();
		tab = table;
		hash = key.hashCode();
		index = (hash & 0x7FFFFFFF) % tab.length;
	}
	@SuppressWarnings("unchecked")
	Entry<K,V> e = (Entry<K,V>) tab[index];
//4.将key,value封装成Hashtable$Entry类型的结点放入table中
	tab[index] = new Entry<>(hash, key, value, e);
	count++;
}

protected void rehash() {
	int oldCapacity = table.length;
	Entry<?,?>[] oldMap = table;
	int newCapacity = (oldCapacity << 1) + 1;//扩容大小为2n+1
	if (newCapacity - MAX_ARRAY_SIZE > 0) {
		if (oldCapacity == MAX_ARRAY_SIZE)
			return;
		newCapacity = MAX_ARRAY_SIZE;
	}
	Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
	modCount++;//table表被修改次数+1
	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接口,也使用一种键值对形式保存数据,使用特点与Hashtable类似
  2. Properties还可从properites文件中,加载数据到Properties类对象并进行读取和修改
  3. 工作中常作为配置文件读取后存储的数据集合

使用方法:

  • getProperty(String key)用指定的键在此属性列表中搜索属性。也就是通过参数 key ,得到 key 所对应的 value。
  • load(InputStream inStream)从输入流中读取属性列表(键和元素对),通过对指定的文件(比如说上面的test.properties 文件)进行装载
  • setProperty(String key,String value)调用Hashtable的方法 put,他通过调用基类的put方法来设置键值对。
  • store(OutputStream out,String comments)以与写入相同的格式,将此Properties表中的属性列表(键值对)写入到指定的文件中去。
  • clear()清除此Properties表所有装载的键值对。
TreeMap类使用与解析

原理概述:

  1. TreeMap底层以红黑树的结构对数据进行存储,查询较快
  2. 若使用无参构造器创建TreeSet,是默认调用元素的compareTo方法进行排序
  3. 使用匿名内部类形式new Comparator(){}作为参数创建TreeSet集合后,可以通过比较器的compare方法中定义排序规则
  4. key不可为null

源码分析:

//1.将传入的实现Comparator接口的匿名内部类,传给TreeMap类中的comparator
public TreeMap(Comparator<? super K> comparator) {
	this.comparator = comparator;
}

//2.第一次使用put方法添加
if (t == null) {
	compare(key, key); //检测key是否为null
	root = new Entry<>(key, value, null);
  size = 1;
  modCount++;
  return null;
}
Collections工具类与接口方法

在这里插入图片描述

在这里插入图片描述

各接口集合选型
  1. 判断存储的类型:(1)一组对象 (2)一组键值对

  2. 一组对象/单列:Collection接口
    允许重复:List接口
    增删多:LinkedList(双向链表)
    改查多:ArrayList(Object类型可变数组),Vector(线程安全)
    不允许重复:Set
    无序:HashSet(底层HashMap)
    排序:TreeSet
    插入与取出顺序一致:LinkedHashSet(底层LinkedHashMap)

  3. 一组键值对:Map
    键无序:HashMap(哈希表(数组+链表+红黑树))
    键排序:TreeMap
    键插入与取出顺序一致:LinkedHashMap(数组+双向链表)
    读取文件:Properties(继承Hashtable)

不可变集合

概述:不可变集合,就是不可被修改的集合。集合的数据项在创建的时候提供,并且在整个生命周期中都不可改变。否则报错。JDK9开始支持!!!

引用场景:如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是个很好的实践。或者当集合对象被不可信的库调用时,不可变形式是安全的。

使用方法:

在List、Set、Map接口中,都存在of方法,可以创建一个不可变的集合。

这个集合不能添加,不能删除,不能修改。

方法名称说明
static List of(E…elements)创建一个具有指定元素的List集合对象
static Set of(E…elements)创建一个具有指定元素的Set集合对象
static <K , V> Map<K,V> of(E…elements)创建一个具有指定元素的Map集合对象
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值