Collection
和Map
两种体系:1.Collection
接口 : 储存单列数据;
2.Map
接口: 储存双列数据,保存键值对key-value类数据,有点像数学中的函数;
Collection
接口,我们就来看看其常用方法:boolean add(E e):添加一个元素
void clear():清空集合中所有的元素
boolean remove(E e):移除集合中指定的元素
boolean contains(E e):检查集合中是否包含指定的对象
boolean isEmpty():判断集合是否为空
void size():判断集合中元素的个数
Object[] toArray():把集合元素存储到数组中
Collection
接口含有两个子接口:List
接口 : 储存的元素都是有序且可以重复的,相当于“动态数组”;Set
接口: 储存的元素是无序且不能重复;List
接口中含有三个实现类:ArrayList
: List
的主要实现类,线程不安全,效率高,底层使用Object数组进行存储(没用泛型前);LinkedList
: 底层使用双向链表,频繁的插入删除操作时此优于ArrayList
。Vector
: List的古老实现类,线程安全,效率低,底层使用Object数组。ArrayList
的源码分析:jdk7
中:
ArrayList a = new ArrayList()
时,其实为底层创建了长度为10的Object数组,每次add相当于将添加的元素变成对象后添加进数组里,一旦数组不够用了,会扩容,默认扩容为扩容为原先的1.5倍,且将原先的数据再复制到新数组里。jdk8
中:
ArrayList a = new ArrayList()
时,没有真正的在底层new出数组,只是先声明,等到第一次add时再真正的创建数组,其他与上述相同;jdk7中ArrayList
对象的创建相当于单例模式中的饿汉式
,8中相当于单例模式中的懒汉式
,延迟数组创建节省内存。
LinkedList
的源码分析:Vector
的源码分析:Map
接口下方是两个实现类和一个子接口:HashMap
: 作为Map接口的主要实现类,线程不安全,效率高,可以存储为null的键值对key-value,jdk7
以前底层使用数组+链表
,jdk8
使用数组+链表+红黑树
存储;LinkedHashMap
:HashMap
;Hashtable
: 古老实现类,线程安全但效率低,不能存储为null的键值对key-value;SortedMap
: 其实现类为: TreeMap
HashMap
的底层实现原理简述:jdk7
时:
HashMap a = new HashMap();
实例化时底层创建了长度16的Entry数组。每次执行map.put(key1,value1)
操作时,会先调用key1的hashCode
方法计算哈希值,此哈希值经过某种算法可以得到此值在Entry数组中的具体存放位置:“七上八下”
的思想;equals
方法的判断,不相等则按照“七上八下”
的思想添加进去即可,相等则用新的value替换旧value。在不断的添加过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空)时,扩容,默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来;
jdk8
中:
jdk8
相较于jdk7
在底层实现方面的不同:
new HashMap()
:底层没有创建一个长度为16的数组jdk
8底层的数组是:Node[],而非Entry[]jdk7
底层结构只有:数组+链表
。jdk8
中底层结构:数组+链表+红黑树
红黑树
存储。七上八下
:LinkedHashMap
的底层实现原理简述:Map
接口中的常用方法:Set
接口中的实现类:HashSet
: Set
接口的主要实现类,线程不安全,可以存放null;LinkedHashSet
:HashSet
的子类,遍历数组可以按照添加的顺序遍历,频繁的遍历时优于HashSet
;TreeSet
:;可以按照添加的对象的某种属性对元素进行排序;Set
接口中没有定义新方法,其使用的都是Collection
的方法。
HashSet
底层源码分析:Set
中添加的数据,其所在类一定要重写hashCode
方法和equals
方法;hashCode
和equals
方法要保持一致性,相同的对象应该具有相同的散列码;LinkedHashSet
底层源码分析:TreeSet
底层源码分析:HashSet
需要注意:Collections
工具类的一些用于操作集合的静态方法:Stream
流我们也可以使用强大的Stream
来对集合进行操作,通过Stream
流对集合进行操作更加的容易。
Stream
的操作流程:Stream
我们有四种方式创建Stream
流:
List<Integer> list = new ArrayList<>();
//集合创建
Stream<Integer> stream = list.stream();
//创建一个顺序流
Stream<Integer> integerStream = list.parallelStream();
//创建一个并行流
int[] a = new int[]{
1,2,3};
IntStream stream1 = Arrays.stream(a);
Stream
的of方法
:Stream<Integer> integerStream1 = Stream.of(1, 2, 3);
无限流
:Stream
的中间操作我们先创建一个Employee
类用来演示,并填充一下集合创建Stream
:
List<Employee> list = new ArrayList<>();
//集合创建
Employee jack = new Employee("jack", 16, 2000);
Employee mike = new Employee("mike", 17, 4666);
Employee aili = new Employee("aili", 22, 3000);
Employee sherry = new Employee("sherry", 31, 10000);
list.add(jack);
list.add(mike);
list.add(aili);
list.add(sherry);
Stream<Employee> stream = list.stream();
//创建一个顺序流
1. 筛选与切片
接收流
(从流中排除某些元素):stream.filter(e -> e.salary>6000).forEach(System.out::println);
结果:
Employee{
name='sherry', age=31, salary=10000}
截断流
(使元素不超过指定数量):stream.limit(2).forEach(System.out::println);
结果:
Employee{
name='jack', age=16, salary=2000}
Employee{
name='mike', age=17, salary=4666}
跳过流
(跳过n个元素,返回去掉前n个元素后的流,若元素不足n个返回空流):stream.skip(2).forEach(System.out::println);
结果:
Employee{
name='aili', age=22, salary=3000}
Employee{
name='sherry', age=31, salary=10000}
筛选流
(通过流内元素的equals方法和hashCode方法去除重复元素):list.add(new Employee("sherry", 31, 10000));
Stream<Employee> stream = list.stream();
stream.distinct().forEach(System.out::println);
结果:
Employee{
name='jack', age=16, salary=2000}
Employee{
name='mike', age=17, salary=4666}
Employee{
name='aili', age=22, salary=3000}
Employee{
name='sherry', age=31, salary=10000}
2. 映射
map(Function f)
—接收一个函数作为参数,将元素转换为其他形式或提取信息,该函数会应用到每一个元素上,使得其映射为一个新元素。stream.map(e -> e.getName()).forEach(System.out::println);
结果:
jack
mike
aili
sherry
flatMap(Function f)
—接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。3. 排序
sorted()
----产生一个新流,按照自然排序
;
sorted(Comparator com)
产生一个新流,按照比较器顺序排序(定制排序
);
Stream
的终止操作1. 匹配与查找
// aLLMatch(Predicate p)——检查是否匹配所有元素。
// 练习:是否所有的员工的年龄都大于18
boolean allMatch = list.stream().allMatch(e -> e.getAge() > 18);System.out.println(allMatch);
// anyMatch(Predicate p)—检查是否至少匹配一个元素。
// 练习:是否存在员工的工资大于1eeee
boolean anyMatch = list.stream().anyMatch(e -> e.getSalary() >10000) ;System.out.println(anyMatch);
// noneMatch(Predicate p)—-检查是否没有匹配的元素。
// 练习:是否存在员工姓“she”
boolean noneMatch = list.stream().noneMatch(e -> e.getName( ).startsWith("she"));
// findFirst——返回第一个元素
Optional<Employee> employee = list.stream().findFirst();
System.out.println(employee);
// findAny—返回当前流中的任意元素
Optional<Employee> employee1 = list.stream().findAny();
System.out.println(employee1);
Stream
流。2. 归约
// reduce(T identity,BinaryOperator)---可以将流中元素反复结合起来,得到一个值,返回T
// 练习1:计算1-10的自然数的和
List<Integer> list1 = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list1.stream( ).reduce( 0 ,(a,b) -> Integer.sum(a,b));
System.out.println(sum);
// reduce(BinaryOperator)一可以将流中元素反复结合起来,得到一个值。返回Optional
// 练习2:计算公司所有员工工资的总和
// 这里的list是原先用来装Employee的集合
Stream<Integer> integerStream = list.stream().map(e -> e.getSalary());
Optional<Integer> sumMoney = integerStream.reduce((a, b) -> Integer.sum(a,b) );
System.out.println(sumMoney);
3. 收集
collect(Collector c)
例:
List<Employee> collect = list.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
即将原先的集合变成Stream流之后经过筛选后再返回原先一样类型的集合,重新收集
collect方法
只是将流转换一下形式,接着再使用Collectors
类中的静态方法对元素或元素的属性进行选择性收集。Collectors
实用类方法: