Java 8新特性
1. HashMap
JDK 1.8之前,HashMap采用的是数组+链表结构。1.8之后采用数组+链表+红黑树(哈希表)。
JDK1.8之前向Map中放元素
- 首先创建一个hash表,hash表是数组结构,存在索引下标。存放的值是entry(key-value)。(数组大小默认是16,扩容因子默认0.75,当数组空间占用达到0.75时,自动进行扩容。)
- 放入对象前,根据对象的hashCode方法计算该对象的hash值。从数组中查找该hash值是否存在,不存在,直接放入数组。
- 若hash值在数组中存在,形成链表结构。后进入的放前面。
这种数据结构的缺点:存入对象过多时,链表长度也会随之加大,再次放入对象时,需要和链表中的每个元素进行equals运算。效率低。

JDK1.8之后向Map中放元素
- 但链表长度大于等于7,且数组长度大于64。
- 红黑树结构除了对集合中的元素进行添加比链表速度慢意外。其他的(删,查)都比链表速度快。
新建一个HashMap
- 根据数组默认大小和扩容因子,创建一个HashMap
/**
* Constructs an empty <tt>HashMap</tt> with the default initial capacity
* (16) and the default load factor (0.75).
*/
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
向HashMap中放入值
- 调用put方法
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//如果是初始化map,n=0(数组索引值)
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//如果该hash值在数组中不存在,直接放入该值。
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
//否则,将该值放在已存在值得后面,形成链表结构
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
//如果链表长度大于等于7(8-1),且数组长度大于64形成红黑树结构
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
/**
* The smallest table capacity for which bins may be treeified.
* (Otherwise the table is resized if too many nodes in a bin.)
* Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
* between resizing and treeification thresholds.
*/
static final int MIN_TREEIFY_CAPACITY = 64;
//数组长度大于64才变长红黑树结构
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();
else if ((e = tab[index = (n - 1) & hash]) != null) {
TreeNode<K,V> hd = null, tl = null;
do {
TreeNode<K,V> p = replacementTreeNode(e, null);
if (tl == null)
hd = p;
else {
p.prev = tl;
tl.next = p;
}
tl = p;
} while ((e = e.next) != null);
if ((tab[index] = hd) != null)
hd.treeify(tab);
}
}
2. 内存结构升级
JDK 1.8之前,方法区属于堆内存中永久区的一部分,另一部分是垃圾回收区。
- 方法区内存是JVM分配的虚拟内存,当方法区要满的时候,垃圾回收机制会对其进行回收。
JDK1.8之后,方法区从堆永久区中剥离,使用物理内存(直接使用内存条内存。不是分配的虚拟内存),称为元空间
- 垃圾回收机制优化。物理内存较大,类存放在元空间(方法区),不易被垃圾回收机制所回收。
3. Lambda表达式
4. Stream API
Stream API介绍:
Stream是 Java8中处理集合,数组的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似于使用SQL执仃的数据库查询。也可以使用Stream API来并行执行操作。简而言之,Stream API提供了一种高效且易于使用的处理数据的方式。 数据操作结束,产生一个新流,数据源不变。
-
流(stream)是数据渠道,用于操作数据源(集合,数组等)所生成的元素序列。
-
集合讲的是
数据,流讲的是计算。 -
stream自己不会存储元素。
-
stream不会改变数据源对象,会返回一个持有结果的新的stream。
-
stream操作是延迟执行的,意味着会等到需要结果的时候才执行。
4.1 创建stream。
- 通过一个数据源(集合,数组),获取一个流。
ArrayList<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
String[] array1 = new String[10];
Stream<String> stream2 = Arrays.stream(array1);
Stream<String> stream3 = Stream.of("1", "2", "3");
//创建一个无限流(迭代)
Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
//终止流
Stream.iterate(0, (x) -> x + 2)
.limit(10)
.forEach(System.out::println);
//0 2 4 6 8 10 12 14 16 18
//创建无限流的第二种方式(生成)
Stream.generate(()->Math.random())
.limit(5)
.forEach(System.out::println);
4.2 中间操作
- 中间操作链,对数据源的一系列处理。
4.2.1筛选与切片
- filter——接收Lambda ,从流中排除某些元素。
- limit——截断流,使其元素不超过给定数量。
- skip(n)—跳过元素,返回一个扔掉了前n 个元素的流。若流中元素不足n 个,则返回一个空流。与limit(n)互补
- distinctl—筛选,通过流所生成元素的hashCode()和equals()去除重复元素
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理而在终止操作时一次性全部处理,称为“惰性求值”。
filter
Person person1 = new Person("张三", 18, "99");
Person person2 = new Person("李四", 22, "85");
Person person3 = new Person("王五", 24, "78");
Person person4 = new Person("赵六", 17, "60");
ArrayList<Person> list = new ArrayList<>();
list.add(person1);
list.add(person2);
list.add(person3);
list.add(person4);
//中间操作,不会执行任何操作
Stream<Person> stream = list.stream().filter(
(e) ->{
System.out.println("中间操作");
return e.getAge()>18;
}
);
//终止操作,一次性执行全部内容
stream.forEach(System.out::println);
//内部迭代,迭代操作由Stream API自动完成。
/*
中间操作
中间操作
Person{name='李四', age=22, score='85'}
中间操作
Person{name='王五', age=24, score='78'}
中间操作
*/
limit
list.stream().filter(
(p)->{
System.out.println("短路!");
return p.getAge()>=18;
})
.limit(2)
.forEach(System.out::println);
/* 过滤后有3个结果,截取前两个
//找到满足条件的两条结果后,就不再继续迭代,称之为短路。提高效率
短路!
Person{name='张三', age=18, score='99'}
短路!
Person{name='李四', age=22, score='85'}
*/
skip
- limit是取前几个,skip是跳过前几个。
list.stream().filter((p)->p.getAge()>=18)
.skip(1)
.forEach(System.out::println);
/*
Person{name='李四', age=22, score='85'}
Person{name='王五', age=24, score='78'}
*/
distinct
- 去重需要重写hashcode和equals方法
//集合中新增值
Person person5 = new Person("赵六", 25, "60");
Person person6 = new Person("赵六", 25, "60");
list.stream().filter((p)->p.getAge()>=18)
.skip(1)
.distinct()
.forEach(System.out::println);
//重写Person类hashcode和equals方法
/*
Person{name='李四', age=22, score='85'}
Person{name='赵六', age=25, score='60'}
*/
4.2.2 映射
map———接收Lambda ,将元素转换成其他形式或提取信息,接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap———接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
map
List<String> list = Arrays.asList("aaa", "bbb", "ccc");
list.stream().map((s)->s.toUpperCase())
.forEach(System.out::println);
//去重获取前面集合中的所有名字
list.stream().map(Person::getName)
.distinct()
.forEach(System.out::println);
/*
AAA BBB CCC
*/
flatmap
- 如果使用map,流类似于{{a,a,a},{b,b,b},{c,c,c}}
- 使用flatmap。流类似于{a,a,a,b,b,b,c,c,c}。将多个流整合为一个流。
- 类似于集合中的add()和addAll()方法。
//将字符串转换为字符集合。
public static Stream<Character> strToChar(String str){
ArrayList<Character> list = new ArrayList<>();
for (char c : str.toCharArray()) {
list.add(c);
}
return list.stream();
}
list.stream().flatMap(MyTests::strToChar)
.forEach(System.out::println);
/*
a a a b b b c c c
*/
4.2.3 排序
- sorted (自然排序)————Comparable
- sorted (Comparator com)————定制排序
sorted
- String对象有自然排序
List<String> list = Arrays.asList("ccc", "bbb", "aaa");
list.stream().sorted().forEach(System.out::println);
//String类中的compareTo方法
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
定制排序
- 给自定义对象排序
list.stream().sorted(
(p1,p2)->{
//年龄一样按姓名排,年龄不同按年龄排
if (p1.getAge()==p2.getAge()){
//String类型排序
return p1.getName().compareTo(p2.getName());
}else {
//年龄从小到大排
//return p1.getAge()-p2.getAge();
//年龄从大到小排(int类型)
return p2.getAge()-p1.getAge();
}
}
).forEach(System.out::println);
4.3 终止操作
- 执行中间操作链,获取结果。
4.3.1 查找与匹配
- a11Match——检查是否匹配所有元素
- anyMatch—检查是否至少匹配一个元素
- noneMatch—--检查是否没有匹配所有元素
- findFirst-—返回第一个元素
- findAny——返回当前流中的任意元素
- count——返回流中元素的总个数
- max——返回流中最大值
- min——返回流中最小值
allMatch
List<Person> list = Arrays.asList(new Person("张三", 18, "99", Person.Status.BUSY),
new Person("李四", 22, "85", Person.Status.FREE),
new Person("王五", 17, "78", Person.Status.VOLICATION),
new Person("赵六", 25, "60", Person.Status.BUSY),
new Person("c陈六", 25, "60", Person.Status.FREE)
);
//查看集合中的对象是否都匹配(满足)枚举状态是BUSY的。
boolean b = list.stream().allMatch((person) ->
person.getStatus().equals(Person.Status.BUSY)
);
//false
System.out.println(b);
anyMatch
boolean b1 = list.stream().anyMatch((person) ->
person.getStatus().equals(Person.Status.BUSY));
//true
System.out.println(b1);
noneMatch
boolean b2 = list.stream().noneMatch((person) ->
person.getStatus().equals(Person.Status.BUSY));
//false ,至少有一个是匹配的就返回false
System.out.println(b2);
findFirst
- java 8为了避免空指针异常,创建了Optional。将对象封装到Optional中。如果对象为空,可以使用orElse()方法创建一个默认对象代替。
Optional<Person> person = list.stream().sorted(
(p1, p2) -> {
if (p1.getAge() == p2.getAge()) {
return p1.getName().compareTo(p2.getName());
} else {
return p2.getAge() - p1.getAge();
}
}
).findFirst();
//Optional[Person{name='c陈六', age=30, score='60', Status=FREE}]
System.out.println(person);
findAny
Optional<Person> any = list.stream().filter(p -> p.getStatus().equals(Person.Status.FREE)).findAny();
System.out.println(any);
count
long count = list.stream().count();
System.out.println(count);
max
Optional<Person> max = list.stream().max(Comparator.comparingInt(Person::getAge));
//返回年龄最大的对象
System.out.println(max);
4.3.2 收集
collect—将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
List<Integer> list1 = list.stream().map(Person::getAge).collect(Collectors.toList());
//将Person集合中的对象年龄映射为一个集合 [18, 22, 17, 25, 30]
System.out.println(list1);
Set<Integer> collect = list.stream().map(Person::getAge).collect(Collectors.toSet());
//映射到set集合,去重复元素
System.out.println(collect);
LinkedHashSet<Integer> collect1 = list.stream().map(Person::getAge).collect(Collectors.toCollection(LinkedHashSet::new));
//指令映射集合类型
System.out.println(collect1);

浙公网安备 33010602011771号