集合框架
接口:Collection接口、Map接口。Java集合框架图,如下:
Collections,提供了对集合进行排序、遍历等多种算法实现;
Arrays,提供了大量的静态方法,用来实现数组常见的操作;
自定义的集合类型,那么类需要有Comparable或者Comparator接口的支持;
Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法;
ListIterator 是 Collection API 中的接口, 它扩展了 Iterator 接口;
函数式接口
https://www.jianshu.com/p/8005f32caf3d
什么是函数式接口?
(1)函数式接口在java中是指:有且仅有一个抽象方法的接口。
- 函数式接口,即适用于函数式编程场景的接口。而java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。
(2)你可以通过Lambda表达式来创建该接口的对象。(若Lambda表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。
(3)我们可以在任意函数式接口上使用@FunctionalInterface注解,这样做可以检查它是否是一个函数式接口(一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。),同时javadoc也会包含一条声明,说明这个接口是一个函数式接口。需要注意的是,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。(该接口是一个标记接口)
备注:“语法糖”是指使用更加方便,但是原理不变的代码语法。例如在遍历集合时使用的for-each语法,其实底层的实现原理仍然是迭代器,这便是“语法糖”。从应用层面来讲,Java中的Lambda可以被当做是匿名内部类的“语法糖”,但是二者在原理上是不同的。
自定义函数
public abstract 可以不写,编译器自动加上
@FunctionalInterface
public interface MyNumber{
double getValue();
}
函数式接口中使用泛型
@FunctionalInterface
public interface MyFunc<T>{
T getValue(T t);
}
函数式编程
lambda的延迟执行
有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费。而Lambda表达式是延迟执行的,这正好可以作为解决方案,提升性能。
性能浪费的日志案例
- 一种典型的场景就是对参数进行有条件使用,
例如对日志消息进行拼接后,在满足条件的情况下进行打印输出:
public class Demo01Logger {
private static void log(int level, String msg) {
if (level == 1) {
System.out.println(msg);
}
}
public static void main(String[] args) {
String msgA = "Hello";
String msgB = "World";
String msgC = "Java";
log(1, msgA + msgB + msgC);//级别1 不一定能够满足 但是 字符串连接操作还是执行了 那么字符串的拼接操作就白做了,存在性能浪费
}
}
- SLF4J是应用非常广泛的日志框架,它在记录日志时为了解决这种性能浪费的问题,并不推荐首先进行字符串的拼接,而是将字符串的若干部分作为可变参数(包装为数组)传入方法中,
仅在日志级别满足要求的情况下才会进行字符串拼接。
LOGGER.debug("变量{}的取值为{}。", "os", "macOS")
其中的大括号 {} 为占位符。如果满足日志级别要求,则会将“os”和“macOS”两个字符串依次拼接到大括号的位置;否则不会进行字符串拼接。
Lambda可以做到更好。
@FunctionalInterface
public interface MessageBuilder {
String buildMessage();
}
public class Demo02LoggerLambda {
private static void log(int level, MessageBuilder builder) {
if (level == 1) {
System.out.println(builder.buildMessage());// 实际上利用内部类 延迟的原理,代码不相关 无需进入到启动代理执行
}
}
public static void main(String[] args) {
String msgA = "Hello";
String msgB = "World";
String msgC = "Java";
log(2,()->{
System.out.println("lambda 是否执行了");
return msgA + msgB + msgC;
});
}
}
使用Lambda作为参数和返回值
假设有一个 方法使用该函数式接口作为参数,那么就可以使用Lambda进行传参.如线程中的Runable接口
public class Runnable {
private static void startThread(Runnable task) { // 为了将Lambda表达式作为参数传递,接收Lambda表达式的参数类型必须是与该Lambda表达式兼容的函数式接口的类型。
new Thread(task).start();
}
public static void main(String[] args) {
startThread(() ‐> System.out.println("线程任务执行!"));
}
}
如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式(类始于new 内部类的实现方式 方法中可以实现构造内部类)
public class lambda_Comparator {
//下面给出 lambda 以及实际替代的内部类写法
private static Comparator<String> newComparator(){
return (a,b)->b.length()-a.length();
}
private static Comparator<String> newComparator1(){
return new Comparator<String>() {
@Override
public int compare(String a, String b) {
return b.length()-a.length();
}
};
}
public static void main(String[] args) {
String[] array={"abc","ab","abcd"};
System.out.println(Arrays.toString(array));
Arrays.sort(array, newComparator1()); // 方式一
Arrays.sort(array,(a,b)->b.length()-a.length());//更简单的方式
System.out.println(Arrays.toString(array));
}
}
Java内置四大核心函数式接口
要使用lambda表达式,就要创建一个函数式接口。
那每次用lambda表达式的时候岂不是很麻烦,这时候,java给我们内置了四大核心函数式接口。
包:java.util.functionSupplier接口(供应接口)
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
练习
public class Test_Supplier {
private static String test_Supplier(Supplier<String> suply) {
return suply.get(); //供应者接口
}
public static void main(String[] args) {
// 产生的数据作为 sout 作为输出
System.out.println(test_Supplier(()->"产生数据"));
System.out.println(String.valueOf(new Supplier<String>() {
@Override
public String get() {
return "产生数据";
}
}));
}
}
public class use_Supplier_Max_Value {
private static int getMax(Supplier<Integer> suply) {
return suply.get();
}
public static void main(String[] args) {
Integer [] data=new Integer[] {6,5,4,3,2,1};
int reslut=getMax(()->{
int max=0;
for (int i = 0; i < data.length; i++) {
max=Math.max(max, data[i]);
}
return max;
});
System.out.println(reslut);
}
}
Consumer接口
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code Consumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
void accept(T t),消费一个数据,其数据类型由泛型决定
默认方法:andThen,消费数据的时候,首先做一个操作,然后再做一个操作,实现组合
练习
public class Test_Comsumer {
public static void generateX(Consumer<String> consumer) {
consumer.accept("hello consumer");
}
public static void main(String[] args) {
generateX(s->System.out.println(s));
}
}
public class use_Consumer_FormattorName {
public static void formattorPersonMsg(Consumer<String[]> con1, Consumer<String[]> con2) {
// con1.accept(new String[]{ "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男" });
// con2.accept(new String[]{ "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男" });
// 一句代码搞定
con1.andThen(con2).accept(new String[] { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男" });
}
public static void main(String[] args) {
formattorPersonMsg((s1) -> {
for (int i = 0; i < s1.length; i++) {
System.out.print(s1[i].split("\\,")[0] + " ");
}
}, (s2) -> {
System.out.println();
for (int i = 0; i < s2.length; i++) {
System.out.print(s2[i].split("\\,")[1] + " ");
}
});
System.out.println();
printInfo(s->System.out.print(s.split("\\,")[0]),
s->System.out.println(","+s.split("\\,")[1]),datas);
}
// 自身自销 有意思
private static void printInfo(Consumer<String> one, Consumer<String> two, String[] array) {
for (String info : array) { // 这里每次产生 {迪丽热巴。性别:女 } String 数据 逻辑那边顺序处理就行
one.andThen(two).accept(info); // 姓名:迪丽热巴。性别:女。 } }
}
}
}
Predicate接口
@FunctionalInterface
public interface Predicate<T> {
boolean test(T var1);
default Predicate<T> and(Predicate<? super T> var1) {
Objects.requireNonNull(var1);
return (var2) -> {
return this.test(var2) && var1.test(var2);
};
}
default Predicate<T> negate() {
return (var1) -> {
return !this.test(var1);
};
}
default Predicate<T> or(Predicate<? super T> var1) {
Objects.requireNonNull(var1);
return (var2) -> {
return this.test(var2) || var1.test(var2);
};
}
static <T> Predicate<T> isEqual(Object var0) {
return null == var0 ? Objects::isNull : (var1) -> {
return var0.equals(var1);
};
}
// 源码中没有
static <T> Predicate<T> not(Predicate<? super T> target) {
Objects.requireNonNull(target);
return (Predicate<T>)target.negate();
}
}
boolean test(T t) 。用于条件判断的场景
默认方法:and(与) or(或) nagte (取反)
静态方法:not isEquals
练习
/**
* 1. 必须为女生;
* 2. 姓名为4个字。
*/
public static void main(String[] args) {
String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" };
getFemaleAndname((s) -> s.split("\\,")[0].length() == 4,
(s) -> s.split("\\,")[1].equals("女"), array);
}
private static void getFemaleAndname(Predicate<String> one,
Predicate<String> two, String[] arr) {
for (String string : arr) {
if (one.and(two).test(string)) {
System.out.println(string);
}
}
}
Function接口
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
/**
* Returns a composed function that first applies the {@code before}
* function to its input, and then applies this function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of input to the {@code before} function, and to the
* composed function
* @param before the function to apply before this function is applied
* @return a composed function that first applies the {@code before}
* function and then applies this function
* @throws NullPointerException if before is null
*
* @see #andThen(Function)
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
/**
* Returns a composed function that first applies this function to
* its input, and then applies the {@code after} function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of output of the {@code after} function, and of the
* composed function
* @param after the function to apply after this function is applied
* @return a composed function that first applies this function and then
* applies the {@code after} function
* @throws NullPointerException if after is null
*
* @see #compose(Function)
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
/**
* Returns a function that always returns its input argument.
*
* @param <T> the type of the input and output objects to the function
* @return a function that always returns its input argument
*/
static <T> Function<T, T> identity() {
return t -> t;
}
}
用来根据一个类型的数据得到另一个类型的数据
默认方法 andThen compose()
静态方法 identity:静态方法 identity
练习
public static void main(String[] args) {
String str = "赵丽颖,20";
solotion(s->s.split("\\,")[1],s->Integer.parseInt(s),s->s+=100,str);
}
private static void solotion(Function<String, String> o1, Function<String,
Integer> o2, Function<Integer, Integer> o3, String str) {
Integer apply = o1.andThen(o2).andThen(o3).apply(str);
System.out.println(apply);
}
扩展函数接口
标识接口
Cloneable接口
(1)实现了Cloneable接口,以指示Object的clone()方法可以合法地对该类实例进行按字段复制;
(2)如果在没有实现Cloneable接口的实例上调用Object的clone()方法,则会导致抛出CloneNotSupporteddException;
(3)按照惯例,实现此接口的类应该使用公共方法重写Object的clone()方法,Object的clone()方法是一个受保护的方法;
Object的clone()方法创建并返回此对象的一个副本。对于任何对象x,表达式:
(1)x.clone() != x为true;
(2)x.clone().getClass() == x.getClass()为true;
(3)x.clone().equals(x)一般情况下为true,但这并不是必须要满足的要求;
Serializable接口
为了能直接以 Java 对象的形式进行保存,然后再重新得到该 Java 对象,这就需要序列化能力。序列化其实可以看成是一种机制,按照一定的格式将 Java 对象的某状态转成介质可接受的形式,以方便存储或传输。
Serializable接口是用于实现Java类的序列化操作而提供的一个语义级别的接口。
(1)提供一种简单又可扩展的对象保存恢复机制。
(2)对于远程调用,能方便对对象进行编码和解码,就像实现对象直接传输。
(3)可以将对象持久化到介质中,就像实现对象直接存储。
(4)允许对象自定义外部存储的格式。
RandomAccess接口
Map
HashMap
LinkedHashMap
ConcurrentHashMap
Hashtable
TreeMap
Collection接口
Collection接口是所有集合类的根节点,Collection表示一种规则,所有实现了Collection接口的类遵循这种规则。
AbstractCollection抽象类,Collection接口的骨架实现类,最小化实现了Collection接口所需要实现的工作量。
基本所有集合的实现类,同时实现了Cloneable和Serializable接口。
List
List接口是一个元素有序(按照插入的顺序维护元素顺序)、可重复、可以为null的集合,中可以存放任意的数据。
public interface List<E> extends Collection<E> {}
Iterator接口实例化:Iterator<E> iterator();
AbstractList抽象类, List接口的骨架实现类,最小化实现了List接口所需要实现的工作量。
LinkedList
LinkedList 是基于链表实现的,所以它的插入和删除操作高效。但也是由于其为基于链表的,所以随机访问的效率差。
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable {}
AbstractSequentialList抽象类,继承自 AbstractList抽象类,是 List 接口 的简化版实现,只支持按次序访问。
Iterator接口实例化:
(1)public Iterator<E> iterator();
public abstract ListIterator<E> listIterator(int index);
Deque接口,定义了一个线性Collection,支持在两端插入和删除元素, Deque实际是“double ended queue(双端队列)”的简称,大多数Deque接口的实现都不会限制元素的数量,但是这个队列既支持有容量限制的实现,也支持没有容量限制的实现。比如,LinkedList就是有容量限制的实现,其最大的容量为Integer.MAX_VALUE
//长度
transient int size = 0;
//指向头结点
transient Node<E> first;
//指向尾结点
transient Node<E> last;
Iterator接口实例化:
(1)public Iterator<E> descendingIterator();//return new DescendingIterator();
private class DescendingIterator implements Iterator<E> {}
(2)public ListIterator<E> listIterator(int index);//return new ListItr(index);
private class ListItr implements ListIterator<E> {}
ArrayList
一个其容量能够动态增长的动态数组。可以随机访问元素,但在List中间插入和移除元素时较慢。同时,ArrayList的操作不是线程安全的,一般在单线程中才使用ArrayList。
public class ArrayList<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {}
AbstractList抽象类,其内部只有一个抽象方法 get(),子类必须实现它,一般是作为获取元素的用途。除此之外,如果子类要操作元素,还需要重写 add(), set(), remove() 方法,因为 AbstractList 虽然定义了这几个方法,但默认是不支持的,直接是抛出UnsupportedOperationException 异常。
Iterator接口实例化:
(1)private class Itr implements Iterator<E> {}
(2)private class ListItr extends Itr implements ListIterator<E> {}
RandomAccess接口是 List 实现所使用的标记接口,用来表明其支持快速(通常是固定时间)随机访问。此接口的主要目的是允许一般的算法更改其行为,从而在将其应用到随机或连续访问列表时能提供良好的性能。
// 内部实现为Object数组
transient Object[] elementData;
// 实际元素的数量
private int size;
Iterator接口实例化:
(1)public Iterator<E> iterator();//return new Itr();
(2)public ListIterator<E> listIterator();//return new ListItr(0);
指定位置迭代:public ListIterator<E> listIterator(int index);
Vector(放弃)
一个可增长的数组(和 ArrayList 类似),能够用索引直接找到元素,Vector 的容量可增可减。Vector 是线程安全的,它所有的方法都加上了 synchronized 关键字(放弃的原因)。由于进行同步等的操作,Vector的效率理论上会比其他几个List的操作低。
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {}
// Vector 的操作就是基于这个数组来实现的:
protected Object[] elementData;
// Vector 中的元素数量
protected int elementCount;
// Vector 的增量,用它来判断需要扩容多少
protected int capacityIncrement;
Iterator接口实例化:
(1)public synchronized Iterator<E> iterator();//return new Itr();
private class Itr implements Iterator<E> {}
(2)public synchronized ListIterator<E> listIterator();//return new ListItr(0);
指定位置迭代:final class ListItr extends Itr implements ListIterator<E>;
Stack(放弃)
一个标准的后进先出的栈。堆栈只定义了默认构造函数,用来创建一个空栈。 堆栈除了包括由Vector定义的所有方法,也定义了自己的一些方法。depue(双端队列)比起stack具有更好的完整性和一致性,应该被优先使用。
public class Stack<E> extends Vector<E>
Stack本身通过扩展Vector而来,而Vector本身是一个可增长的对象数组,那么这个数组的哪里作为Stack的栈顶,哪里作为Stack的栈底?
答案从源代码中寻找,jdk:addElement(item),the last item of the Vector object。
CopyOnWriteArrayList
ReentrantReadWriteLock 读写锁的思想,读读共享、写写互斥、读写互斥、写读互斥。
CopyOnWrite思想,就是对一块内存进行修改时,不直接在原有内存块中进行写操作,而是将内存拷贝一份,在新的内存中进行写操作,写完之后,再将原来指向的内存指针指到新的内存,原来的内存就可以被回收。
CopyOnWriteArrayList读取是完全不用加锁的,并且更厉害的是:写入也不会阻塞读取操作,只有写入和写入之间需要进行同步等待,读操作的性能得到大幅度提升。所有可变操作(add,set等等)都是通过创建底层数组的新副本来实现的。其中,写入操作 add() 方法在添加集合的时候加了锁,保证同步,避免多线程写的时候会 copy 出多个副本。
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {}
/** The lock protecting all mutators */
final transient ReentrantLock lock = new ReentrantLock();
/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;
Iterator接口实例化:
(1)public Iterator<E> iterator(); //return new COWIterator<E>(getArray(), 0);
static final class COWIterator<E> implements ListIterator<E>
(2)public ListIterator<E> listIterator(int index);
Set
public interface Set<E> extends Collection<E>
Set接口仅仅对Collection所有方法进行继承而已,而自己没有扩展任何方法,Set接口与Collection接口一样,都是15个方法。Set不允许包含相同的元素,这里说的相同元素指的是用 equals() 方法比价后返回 true.
HashSet
HashSet 实现类与 Set 接口在方法上的唯一区别就是 HashSet 多了一个clone()方法。
hashSet 有以下特点:
· 不能保证元素的排列顺序,顺序有可能发生变化
· 不是同步的
· 集合元素可以是 null,但只能放入一个 null
一般操作 HashSet 还是调用 Collection 的 add / remove 等方法进行操作
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
private transient HashMap<E,Object> map;
transient Set<K> keySet;
transient Collection<V> values;
Iterator接口实例化:
(1)public Iterator iterator();// return map.keySet().iterator();
HashSet底层通过包装HashMap来实现