轩辕李的博客 轩辕李的博客
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

轩辕李

勇猛精进,星辰大海
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Java

    • 核心

      • Java8--Lambda 表达式、Stream 和时间 API
      • Java集合
        • JDK内部集合
          • 1、数组
          • 2、List
          • 3、Set
          • 4、Map
          • 5、其他
        • Apache Commons Collection集合
          • 1、工具类
          • 2、容器类
          • 3、辅助类
        • Guava集合
          • 1、不可变系列对象
          • 2、新的集合类型
          • 3、实用工具类
        • 并发集合
        • Redission分布式集合
        • 总结
      • Java IO
      • Java 文件操作
      • Java 网络编程
      • Java运行期动态能力
      • Java可插入注解处理器
      • Java基准测试(JMH)
      • Java性能分析(Profiler)
      • Java调试(JDI与JDWP)
      • Java管理与监控(JMX)
      • Java加密体系(JCA)
      • Java服务发现(SPI)
      • Java随机数生成研究
      • Java数据库连接(JDBC)
      • Java历代版本新特性
      • 写好Java Doc
      • 聊聊classpath及其资源获取
    • 并发

    • 经验

    • JVM

    • 企业应用

  • Spring

  • 其他语言

  • 工具

  • 后端
  • Java
  • 核心
轩辕李
2021-08-06
目录

Java集合

本文主要讲解JDK内部集合类、Apache Commons Collection中的集合类、Guava中的集合类、并发集合类、Redission分布式集合类。

# JDK内部集合

# 1、数组

两种初始化方法:

// 方式1
String[] arr = new String[2];

// 方式1
String[] arr = new String[]{"", ""};

多维数组:

String[][] arr = new String[2][3];

用法:

// 访问元素
arr[0]

// 循环-方式1
for (String s : arr) {
    ...
}

// 循环-方式2
for (int i = 0; i < arr.length; i++) {
    String s = arr[i];
    ...
}
// 循环-方式3
Arrays.stream(arr).forEach(s -> {});

// 循环-倒序
for (int i = arr.length - 1; i >= 0; i--) {
    String s = arr[i];
    ...
}

Arrays工具类中的方法:

  • stream:数组转为Stream流
  • copyOfRange:将指定数组的指定范围复制到新数组中
  • copyOf:复制指定的数组,截断或填充空值(如有必要),使副本具有指定的长度
  • binarySearch:二分搜索
  • fill:将指定的值分配给数组的每个元素
  • setAll:和fill类似,不过指定值为自定义Function
  • sort:升序排序
  • spliterator:拆分器。大多数情况下,你都不需要使用到这个方法,他主要是跟StreamSupport配合使用。参考java8 Stream之Spliterator (opens new window)
  • parallelPrefix:使用提供的函数并行累积给定数组的每个元素。相当于并行的reduce操作
  • parallelSetAll:setAll的并行版本
  • parallelSort:sort的并行版本
  • toString、equals、hashCode:共用基础方法,不多说。此外这三个方法还有对应的deep系列,分别是deepToString、deepEquals、deepHashCode

这其中要重点说一下数组拷贝,其实Java中数组拷贝最终都是调用System.arraycopy()方法,这个方法的性能是非常高的,因为它是直接对内存进行复制。所以关于数组复制尽量调用此方法。
需要注意的是System.arraycopy()属于浅复制,也就是复制对象和二维数组的时候复制的是引用,修改复制后的对象会影响到原始对象。

数组特点:

  • 访问速度快。插入和查询时间复杂度是O(1)
  • 初始化之后长度不可变,不支持动态扩容

# 2、List

为了解决数组动态扩容的问题,产生了List集合。
主要有两个实现类:

  • ArrayList:数组实现。遍历和随机访问效率高
  • LinkedList:链表实现。插入和删除效率高

初始化:

List<Integer> list = new ArrayList<>();
list.add(2);
list.add(3);

// java9之后。of方法返回的是不可变List
List<Integer> list = List.of(2,3)

List继承自Collection接口,Collection继承自Iterable接口。
Iterable中的方法:

  • iterator:迭代器。有了他我们就可以使用foreach循环了
  • forEach:java8之后新增方法,lambda式的forEach
  • spliterator:拆分器

Collection中的方法:

  • size:获得集合中的元素数
  • isEmpty:此集合是否不包含任何元素
  • contains:是否包含指定元素
  • toArray:转换为array数组。toArray默认返回时Object数组,但他有两个重载方法,可以返回指定类型的数组
String[] arr = list.toArray(new String[]{});
String[] arr2 = list.toArray(i->new String[]{});
  • add:添加元素
  • remove:删除元素
  • containsAll:是否包含指定的所有元素
  • addAll:添加指定的所有元素
  • removeAll:删除指定的所有元素
  • removeIf:删除满足条件的元素
  • retainAll:保留指定的所有元素,其他元素则删除
  • clear:清空集合
  • stream:获得stream流
  • parallelStream:获得并行stream流

List中的方法:

  • addAll(i,col):指定位置添加元素
  • replaceAll:按照指定规则替换所有元素
  • sort:根据Comparator进行排序。在java8中使用lambda表达式特别方便
// 对于基本类型
list.sort(String::compareTo);
//对于Comparator<String> compare = String::compareTo;你可能会比较迷惑,其实他的转换过程是这样的
BiFunction<String, String, Integer> bf = String::compareTo;
Comparator<String> compare = (Comparator<String>) bf;  // 可以看到Comparator函数接口的结果和BiFunction是相似的,java做了简化处理


// 对于Bean
list.sort(Comparator.comparing(TUser::getId))
  • get:获得指定位置元素
  • set:设置指定位置元素
  • add(i,e):添加指定位置元素。会导致原先此位置的元素后移
  • remove(i):删除指定位置元素。如果List中泛型为Integer,这里会和Collection中的remove有一个冲突,具体这么做
// 调用的是Collection.remove
intList.remove(Integer.valueOf(2));

// 调用的是List.remove
intList.remove(5);
  • indexOf:指定元素第一次出现时的索引
  • lastIndexOf:指定元素最后一次出现时的索引
  • listIterator:获得ListIterator,支持在迭代期间修改列表
  • subList:返回指定位置范围的视图

List还有两个静态方法:

  • of:java9之后支持。获得不可修改的列表
  • copyOf:java11之后支持。拷贝一个不可修改的列表

# 3、Set

Set是不包含重复元素的集合。继承自Collection接口,主要实现类:

  • HashSet:不保证迭代顺序和插入顺序一致。内部借助HashMap来实现
  • LinkedHashSet:能保证迭代顺序和插入顺序一致。内部借助LinkedHashMap来实现
  • TreeSet:元素按照自然顺序或者提供的Comparator来排序

初始化:

// 方式1
Set<String> set = new HashSet<>();
set.add("a");
set.add("b");

// 方式2。返回不可变Set
Set<Integer> integers = Set.of(1, 2);

HashSet和LinkedHashSet没有自己独立的方法,参考Collection接口即可。
Set接口有of和copyOf方法,功能同List。
TreeSet继承自NavigableSet,NavigableSet继承自SortedSet。我们来看看他们其中的方法。

SortedSet提供了对元素排序的功能,其中的方法有:

  • subSet:返回此集合部分的视图,其元素范围从fromElement (包括)到toElement (不包括)
  • headSet:返回此集合中元素严格小于toElement的部分的视图
  • tailSet:返回此集合中元素大于或等于fromElement的部分的视图
  • first:第一个元素
  • last:最后一个元素

NavigableSet是对SortedSet的导航扩展,报告给定搜索目标的最接近匹配。参考NavigableSet Api (opens new window)

Set和List都继承自Collection接口,JDK提供了Collections工具类,其中有一些常用的方法:

  • sort:自然排序
  • binarySearch:二分搜索
  • reverse:反转集合顺序
  • shuffle:打乱顺序(洗牌)
  • swap:根据下标交换指定元素的位置
  • copy:列表拷贝
  • min:根据排序规则,取得最小值
  • max:根据排序规则,取得最大值
  • rotate:旋转,元素的位置将要被重新调整为(i - distance) mod list.size()。用于快速元素移动很有效
List<Integer> list = Lists.newArrayList(1, 2, 3, 4);
// 将元素整体后移一位
Collections.rotate(list, 1);     // [4, 1, 2, 3]
// 将元素整体前移一位
Collections.rotate(list, -1);     // [2, 3, 4, 1]
// 仅移动部分区间数据
Collections.rotate(list.subList(1,3),1);    // [1, 3, 2, 4]
  • replaceAll:替换集合中旧值为新值
  • indexOfSubList:列表级别的indexOf
  • unmodifiableCollection:变为不可修改集合。还有系列方法:unmodifiableSet、unmodifiableSortedSet、unmodifiableNavigableSet、unmodifiableMap等
  • synchronizedCollection:变为现场安全的集合。系统方法同上。Java1.5之后新增了同步容器ConcurrentMap等,所以不再推荐使用此系列方法
  • checkedCollection:返回指定集合的动态类型安全视图,只允许插入指定的类型。系列方法同上
  • emptyIterator:返回一个没有元素的迭代器。还有系列方法:emptySet、emptyList、emptyMap等
  • singleton:返回一个只包含指定对象的不可变集合。还有系列方法:singletonList、singletonMap
  • nCopies:n个对象副本组成的不可变列表
  • reverseOrder:排序辅助方法,返回逆序的Comparator
  • enumeration:返回集合的Enumeration对象
  • list:Enumeration对象转换为List对象
  • frequency:返回指定集合中等于指定对象的元素数,其实可以叫getCount
  • disjoint:两集合没有共同的元素,则返回true

# 4、Map

Map是键值对,每个键最多映射到一个值上。主要实现类有:

  • HashMap:基于哈希表的实现,允许null键和null值。内部使用数组+链表的数据结构,为了保证性能,Java8之后链表元素数量大于阈值会变为红黑树
  • LinkedHashMap:保证了迭代顺序和插入顺序一致,允许null键和null值。内部使用哈希表+双向链表实现
  • TreeMap:键值按照自然顺序或者提供的Comparator来排序,不允许null键,允许null值。内部使用红黑树来实现
  • HashTable:线程安全的哈希表,因为性能太差,不推荐使用。讲到并发集合的时候,用ConcurrentMap替代它

Map接口有of和copyOf方法,功能同List。此外还有两个静态方法:entry和ofEntries。
初始化:

// 方式1
Map<String, Integer> map = new HashMap<>();
map.put("a",1);
map.put("b",2);
        
// 方式2。返回不可变Map
Map<Integer, Integer> integerMap = Map.of(1, 2);

// 方式3。返回不可变Map
Map<String, Integer> stringIntegerMap = Map.ofEntries(Map.entry("a", 1), Map.entry("b", 2));

Map接口的方法:

  • size:键值数量
  • isEmpty:是否为空字典
  • containsKey:是否包含指定键
  • containsValue:是否包含指定值
  • get:根据键获得值
  • put:添加键值
  • remove:删除键值
  • putAll:批量添加
  • clear:清空键值字典
  • keySet:获得键Set
  • values:获得值集合
  • entrySet:获得键值对Set,泛型为Map.Entry
  • getOrDefault:安全的get,没有值则返回默认值
  • forEach:foreach的lambda版本
  • replaceAll:替换匹配的所有元素
  • putIfAbsent:仅当键不存在时,才会执行给定的函数来计算新的值,并用这个新值插入到 Map 中
  • replace:替换
  • computeIfAbsent:putIfAbsent的Function版本
  • computeIfPresent:仅当键存在时,才会执行给定的函数来计算新的值,并用这个新值替换原来的值。
  • compute:无论键是否存在,都会执行给定的函数来计算新的值,并用这个新值替换原来的值。
  • merge:如果键存在,则使用提供的 BiFunction 函数来合并旧值和新值;如果键不存在,则直接将新值插入到 Map 中。

后四个方法为Java 8中新增的,有必要演示一下:

// 基础数据
Map<String, Object> map = new HashMap<>();
map.put("name", "Jack");
map.put("age", 25);

// computeIfAbsent:name不会被插入成功。因为已经存在了name键
System.out.println(map.computeIfAbsent("name", k -> k + " random")); // 返回值为"Jack"
System.out.println(map); // map:{name=Jack, age=25}

// 插入一个新的键值对
System.out.println(map.computeIfAbsent("score", k -> 90)); // 返回值为90
System.out.println(map); // map:{name=Jack, age=25, score=90}

// computeIfPresent:name会被插入成功,会替换掉旧值
System.out.println(map.computeIfPresent("name", (k, v) -> k + " random")); // 返回值为"name random"
System.out.println(map); // map:{name=name random, age=25}

// 按照你想要的规则进行替换
System.out.println(map.computeIfPresent("name", (k, v) -> v + " random")); // {name=Jack random, age=25}
System.out.println(map);
System.out.println(map.computeIfPresent("name", (k, v) -> k + v)); // {name=name Jack random, age=25}
System.out.println(map);

// 替换新值为null的话键值会被删除
System.out.println(map.computeIfPresent("name", (k, v) -> null)); // 返回值为null
System.out.println(map); // map:{age=25}

// 插入一个不存在的键值会失败
System.out.println(map.computeIfPresent("score", (k, v) -> 90)); // 返回值为null
System.out.println(map); // map:{age=25, score=90}

// compute:可定制的计算,computeIfPresent的增强版,可以获得现有的k,v对齐进行操作
System.out.println(map.compute("age", (k, v) -> (Integer) v + 1)); // 返回值为26
System.out.println(map); // map:{age=26, score=90}

// merge:合并。如果存在旧值,则使用BiFunction进行替换;否则赋予新值
System.out.println(map.merge("score", 80, (oldValue, newValue) -> (Integer) oldValue + newValue)); // 返回值为170
System.out.println(map); // map:{age=26, score=170}

// 合并一个不存在的键值对
System.out.println(map.merge("height", 180, (oldValue, newValue) -> (Integer) oldValue + newValue)); // 返回值为180
System.out.println(map); // map:{age=26, score=170, height=180}

HashMap和LinkedHashMap没有自己独有的方法,只是实现了Map接口。
TreeMap还实现了NavigableMap->SortedMap,参考NavigableMap Api (opens new window)

# 5、其他

Queue接口定义了队列,有offer、poll、peek等队列专有方法。主要实现类有PriorityQueue(优先级队列)。
Deque继承自Queue,定义了双端队列,有offer/poll/peek-First/Last等系列方法,主要实现类是ArrayDeque、LinkedList。

Stack是Java中栈的实现类,有push、pop、peek等专有方法。

# Apache Commons Collection集合

Apache Commons系列工具包是Java开发中常用的工具包,其中关于集合方面的扩展要使用到commons-collections4:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.4</version>
</dependency>

此工具工具包主要提供了三方面的扩展

# 1、工具类

主要是SetUtils、ListUtils、CollectionUtils、MapUtils。这些工具类有一些方法在Java8之后有点过时了,比如:

List<Integer> list = List.of(1, 2, 3, 4);
// select方法
List<Integer> select = ListUtils.select(list, e -> e > 2);
// java8之后这么做
select = list.stream().filter(e->e>2).collect(Collectors.toList());

其他诸如filter、transform、getCardinalityMap等方法,用Stream操作会更简单一些。

下面列出一些提高效率的便利方法:

  • subtract:取得差集
  • disjunction:获得彼此差集之后的并集
  • intersection:取得交集
  • union:取得并集
  • containsAny:包含任何
  • permutations:返回列表的所有排列组合,大小是列表大小的阶乘级别,注意性能和内存问题

# 2、容器类

Bag 会记录对象在集合中出现的次数,用法:

Bag bag = new HashBag();
bag.add("ONE", 6);  // 添加6次
bag.remove("ONE", 2);  // 删除2次
bag.getCount("ONE");  // 返回4

主要实现类有HashBag和TreeBag(自然排序)


List的扩展,主要有如下类:

  • CursorableLinkedList:提供了可修改的迭代器。在JDK的List.listIterator中提供了类似功能
  • FixedSizeList:固定大小的List,add、remove、clear和retain操作是不被支持的,set方法是允许的但是不会影响列表大小
  • GrowthList:可以使其在因set或add操作造成索引超出异常时无缝的增加列表长度,可以避免大多数的IndexOutOfBoundsException
  • LazyList:可以自动增长,但只发生在get时--允许指定一个Factory获得超出索引位置的值
  • PredicatedList:添加元素时进行校验
  • SetUniqueList:不允许重复元素的列表
  • TransformedList:在set和add时对元素进行转换后再添加
  • TreeList:实现了一个快速插入、删除,同时查询性能也可观的List

看完之后,怎么说呢?这些类用的频率都不太高,但又弃之可惜。


Set的扩展有如下类:

  • CompositeSet:多个Set的组合视图
  • 剩余的Predicated、Transformed、Unmodifiable和List一样,不赘言

Map的扩展有如下类:

  • CaseInsensitiveMap:大小写不敏感的Map,会把键都转换为小写
  • CompositeMap:多个Map的组合视图
  • DefaultedMap:键不存在,返回默认对象
  • FixedSizeMap:固定大小的Map
  • IdentityMap:匹配键值不是通过==,而是equals
  • MultiKeyMap:允许多个键关联到一个值上
  • MultiValueMap:允许多个值关联到一个键上
  • BidiMap:双向Map,可以通过值查找键。主要实现类是DualHashBidiMap、TreeBidiMap
  • LRUMap:固定大小Map,容器满了之后删除最近最少使用(Least Recently Used)的元素
  • LazyMap/PredicatedMap/TransformedMap/TypedMap/UnmodifiableMap,逻辑同List扩展一样,不赘言

# 3、辅助类

主要是Comparator、Predicate、Transformer等辅助类,这些辅助类在Java8 Stream面前都显得有些过时了,不赘言

# Guava集合

如果说commons-collections4是质朴村花的话,那么Guava就是靓丽女神了。
Guava中最受欢迎的就是他的集合类。

# 1、不可变系列对象

比如ImmutableList、ImmutableSet,比如JDK内部的Collections.unmodifiable系列方法来说,具有如下优点:

  • 线程安全
  • 更加节省空间

需要注意的是,所有Immutable都拒绝空值。

使用:

ImmutableSet.of("red", "orange", "yellow")

Set<Bar> bars = ...
ImmutableSet.copyOf(bars)

ImmutableSet.<Color>builder().addAll(WEBSAFE_COLORS).add(new Color(0, 191, 255)).build();

# 2、新的集合类型

Multiset,定义一个集合,该集合计算对象在集合中出现的次数。
主要方法有add、remove、getCount,使用起来比较简单:

MultiSet<Object> multiSet = new HashMultiSet<>();
multiSet.add("name");
multiSet.add("name");
multiSet.add("name", 3);
multiSet.getCount("name");  // 5

Multimap,一个键可以关联多个值。
初始化:

ListMultimap<String, Integer> multimap1 = MultimapBuilder.hashKeys().arrayListValues().build();
SetMultimap<String, Integer> multimap2 = MultimapBuilder.treeKeys().hashSetValues().build();

使用:

multimap.put("type",1);
multimap.putAll("type", Lists.newArrayList(2,3));
List<Integer> types = multimap.get("type");
Map<String, Collection<Integer>> map = multimap.asMap();

BiMap,双向Map。在commons-collections4有类似实现

BiMap<String, Integer> userId = HashBiMap.create();
...

String userForId = userId.inverse().get(id);

Table,一个索引映射多个值,类似Map<FirstName, Map<LastName, Person>>这样的结果
用法:

Table<String, String, Double> table = HashBasedTable.create();
table.put("cny", "usd", 0.235);
table.put("cny", "jpy", 8.523);
table.put("hkd", "jpy", 2.053);

table.get("cny", "usd");    // 0.235
table.row("cny");   //{usd=0.235, jpy=8.523}
table.column("jpy");    //{cny=8.523, hkd=2.053}
table.rowKeySet();  //cny, hkd

RangeSet,包含范围的集合。
用法:

RangeSet<Integer> rangeSet = TreeRangeSet.create();
rangeSet.add(Range.closed(1, 10)); // {[1, 10]}
rangeSet.add(Range.closedOpen(11, 15)); // disconnected range: {[1, 10], [11, 15)}
rangeSet.add(Range.closedOpen(15, 20)); // connected range; {[1, 10], [11, 20)}
rangeSet.add(Range.openClosed(0, 0)); // empty range; {[1, 10], [11, 20)}
rangeSet.remove(Range.open(5, 10)); // splits [1, 10]; {[1, 5], [10, 10], [11, 20)}

Set<Range<Integer>> ranges = rangeSet.asRanges();

RangeMap,包含范围的Map。与RangeSet不同的是它不合并相邻映射。
用法:

RangeMap<Integer, String> rangeMap = TreeRangeMap.create();
rangeMap.put(Range.closed(1, 10), "foo"); // {[1, 10] => "foo"}
rangeMap.put(Range.open(3, 6), "bar"); // {[1, 3] => "foo", (3, 6) => "bar", [6, 10] => "foo"}
rangeMap.put(Range.open(10, 20), "foo"); // {[1, 3] => "foo", (3, 6) => "bar", [6, 10] => "foo", (10, 20) => "foo"}
rangeMap.remove(Range.closed(5, 11)); // {[1, 3] => "foo", (3, 5) => "bar", (11, 20) => "foo"}

# 3、实用工具类

比如Lists、Sets等,你可能想象不到,Guava中使用频率最高的是如下代码:

List<Integer> integers = Lists.newArrayList(2, 3, 4);

不由得令人感慨,偷懒是最高生产力。

这些工具类中方法众多,具体参考:Guava 集合工具扩展 (opens new window)

这里列出一些使用频率较高,令人眼前一亮的工具扩展:

  • Lists.partition:分割列表。返回连续子列表,每个子列表大小系统(最后一个可能更小)
  • Sets.subSet:提取范围内的子视图
  • Maps.subMap:提取范围内的子视图

# 并发集合

前面说道不推荐使用Collections.synchronized系列方法,原因是它内部只是提供了一个简单的互斥操作,在高并发情况下,并不十分高效。
Java 5之后利用CAS指令(乐观锁)提供了更高效的并发容器,它们分别是:

  • ConcurrentLinkedQueue:高并发环境下性能最好的队列
  • CopyOnWriteArrayList:只在写时加锁的ArrayList,采用写时复制的思想,读性能大幅提升
  • BlockingQueue:一个阻塞式的数据共享通道,参考:阻塞队列BlockingQueue。他有三个主要实现:
    • ArrayBlockingQueue:数组支持的有界队列
    • LinkedBlockingQueue:链表支持的可选有界队列
    • LinkedBlockingDeque:链表支持的双向可选有界队列
  • ConcurrentMap:线程安全的map,主要实现有:
    • ConcurrentHashMap:高并发哈希表
    • ConcurrentSkipListMap:跳表,类似LinkedHashMap,提供有序的遍历。类似于平衡树,特点是插入和删除只需要对局部数据进行操作即可。平衡树的话需要全局锁,而跳表只需要部分锁。

# Redission分布式集合

缓存作为提升性能和体验的必要手段,使用非常广泛。
在同一个JVM内部,使用ConcurrentMap作为缓存是常见手段。
在分布式环境中,可能需要集中式缓存服务,目前比较流行的是Redis,它提供了多种数据结果供我们使用。
Redission作为Redis客户端库,更进一步的把原生Redis数据结构封装成了Java大家熟悉的集合容器。
以Map举例:

RMap<String, SomeObject> map = redisson.getMap("anyMap");
SomeObject prevObject = map.put("123", new SomeObject());
SomeObject currentObject = map.putIfAbsent("323", new SomeObject());
SomeObject obj = map.remove("123");

RFuture<SomeObject> putAsyncFuture = map.putAsync("321");

它提供了同步和异步的添加和删除方法。

Redission还对分布式环境下常用操作做了一些封装,这已经超出了本章要讨论的内容,参考:Redisson项目介绍 (opens new window)

# 总结

本文对Java中的集合进行了讨论,主要讲到JDK内部集合类、Apache Commons Collection中的集合类、Guava中的集合类、并发集合类、Redission分布式集合类的使用。
就我个人经验来说,学习一门语言不难,难的是对语言中的各种工具库、编程范式做到信手拈来、熟练使用,达到这一步,工作效率会有一个质的提升。
当然,实际中也不提倡死记硬背,只需要在脑海中有一副地图,这样遇到问题的时候能快速的索引到对应的位置,多使用多历练,慢慢就成为一个高手。

祝你变得更强!

编辑 (opens new window)
#Java集合
上次更新: 2024/12/26
Java8--Lambda 表达式、Stream 和时间 API
Java IO

← Java8--Lambda 表达式、Stream 和时间 API Java IO→

最近更新
01
Spring Boot版本新特性
09-15
02
Spring框架版本新特性
09-01
03
Spring Boot开发初体验
08-15
更多文章>
Theme by Vdoing | Copyright © 2018-2025 京ICP备2021021832号-2 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式