Java数据结构 (2) :集合框架(Collection、Map)+ Stream流简介

Java集合框架提供了一套性能优良、使用方便的接口和类,位于java.util包中。

Stream是jdk8开始新增的一套API(java.util.stream.*),可以用于操作集合或者数组的数据。


0 数组:Array

0.1 初始化

int[] arr = new int[] {25, 35, 45, 55};
int[] arr = new int[10];
int[] arr = {1, 2, 3, 4, 5};
int[][] arr2d = {{1, 2}, {3, 4}};

0.2 遍历

/* 传统for循环 */
for (int i = 0; i < arr.length; i++) {		// 长度属性length
	System.out.println(arr[i]);
}

/* forEach循环 */
for (int x : arr) {
	System.out.println(x);
}

1 单列集合:Collection

  • List系列集合
    • ArrayList、LinkedList:有序、可重复、有索引
  • Set系列集合
    • HashSet:无序、不重复、无索引
    • LinkedHashSet:有先后顺序、不重复、无索引
    • TreeSet:按大小默认升序排序、不重复、无索引

1.1 Collection接口

单列集合的顶层接口Collection<E>

Collection<String> colle = new ArrayList<>();

1.1.1 通用方法

方法名说明
public boolean add(E e)把给定的对象添加到当前集合中
public boolean size()返回集合中元素的个数
public void clear()清空集合中所有元素
public boolean isEmpty()判断当前集合是否为空
public boolean remove(E e)把给定的对象在当前集合中删除
public boolean contains(Object obj)判断当前集合中是否包含给定的对象
public Object[] toArray()把集合中的元素存储到数组中

1.1.2 遍历

(1) 迭代器

迭代器是用来遍历集合的专用方式(数组没有迭代器),在Java中迭代器的代表是Iterator。

方法名说明
Iterator<E> iterator()返回集合中的迭代器对象,该迭代器对象默认指向当前集合的第一个元素

Iterator迭代器中的常用方法

方法名说明
boolean hasNext()判断当前位置是否有元素
E next()返回当前位置的元素,并同时将迭代器对象指向下一个元素

Collection本身无索引,故无法用正常的fori遍历,但可以使用增强型for遍历。

/* 迭代器遍历 */
Iterator<String> it = colle.iterator();
while (it.hasNext()) {
	System.out.println(it.next());
}

/* 增强版for遍历(实际底层使用的也是迭代器) */
for (String s : colle) {
	System.out.println(s);
}
(2) forEach()

调用forEach()方法,并使用Lambda表达式简化书写

colle.forEach(new Consumer<String>() {
	@Override
	public void accept(String s) {
		System.out.println(s);
	}
});
// 使用Lambda表达式简化
colle.forEach(s -> {
	System.out.println(s);
});
// 进一步简化
colle.forEach(System.out::println);

1.2 List系列集合

List系列集合添加的元素有序、可重复、有索引。因为有索引,故可用fori遍历。

List<String> list = new ArrayList<>();
List<String> linkedList = new LinkedList<>();
方法名说明
void add(int index, E element)在此集合中的指定位置插入指定元素
E remove(int index)删除指定索引处的元素并返回它
E set(int index, E element)修改指定索引处的元素并返回它
E get(int index)返回指定索引处的元素

1.2.1 ArrayList

基于数组实现,支持随机存取(顺序表),查找快、增删慢。

利用无参构造器创建的ArrayList,会在底层创建一个默认长度为0的数组。添加第一个元素时,底层会创建一个新的长度为10的数组。存满时,会扩容1.5倍。如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准。

/* 定义 */
ArrayList<Integer> list = new ArrayList<>();	// 元素必须为引用数据类型
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));	// 初始化时赋值
List<Integer> list = Arrays.stream(new int[] {1, 2, 3}.boxed().collect(Collectors.toList()));

/* 遍历 */
// 传统for循环
for (int i = 0; i < list.size(); i++) {		// 集合通用方法size
	System.out.println(list.get(i));
}
// forEach循环
for (int x : list) {
	System.out.println(x);
}
// 迭代器
for (Iterator<Integer> it = list.iterator(); it.hasNext();) {
	System.out.println(it.next());
}
// Stream流和Lambda表达式
list.stream().forEach(x -> System.out.println(x));

/* List转换为数组 */
// 使用toArray方法
Integer[] a = new Integer[v.size()];
list.toArray(a);
// 使用流
int[] a = list.stream().mapToInt(Integer::valueOf).toArray();	// mapToInt返回基本类型的流

/* 常用方法 */
// 添加元素
boolean add(E e)					// 在尾部追加元素
boolean add(int index, E element)	// 在指定下标处添加元素
// 添加另一个集合对象的元素
boolean addAll(Collection<? extends E> c)				// 在尾部追加元素
boolean addAll(int index, Collection<? extends E> c)	// 从指定下标开始添加元素
list.addAll(Arrays.asList(1, 2));
// 删除元素
boolean remove(Object o)	// 删除第一个与指定元素相同的元素,如果存在的话
E remove(int index)			// 删除指定下标处的元素
// 获取指定下标处的元素
E get(int index)
// 设置指定下标处的元素
E set(int index, E element)
// 返回指定下标区间的子序列,左闭右开
List<E> subList(int fromIndex, int toIndex)
// 判空
boolean isEmpty()
// 判断是否含有指定元素
boolean contains(Object o)
// 返回指定元素的索引,不存在则返回-1
int indexOf(Object o)
// 排序
default void sort(Comparator<? super E> c)
list.sort(Comparator.naturalOrder());	// 升序(非递减)排序
list.sort(Comparator.reverseOrder());	// 降序(非递增)排序
// 转换为数组
Object[] toArray();			// 返回数组
<T> T[] toArray(T[] a);		// 返回数组,并将所有元素存入数组a(若空间不足则直接重分配空间)

1.2.2 LinkedList

/* 定义 */
LinkedList<Integer> list = new LinkedList<>(Arrays.asList(1, 2, 3));

基于双链表实现(早期为循环双链表(参考C++STL中的list),新版本为提升时空性能已不再循环),查找慢、增删快,但对首尾元素的增删改查皆是极快的。因此新增了很多首位操作的特有方法:

方法名说明
public void addFirst(E e)头插指定的元素
public void addLast(E e)尾插指定的元素
public E getFirst()返回列表中的第一个元素
public E getLast()返回列表中的最后一个元素
public E removeFirst()删除并返回列表中的第一个元素
public E removeLast()删除并返回列表中的最后一个元素

1.3 Set系列集合

  • HashSet:无序、不重复、无索引
  • LinkedHashSet:有先后顺序、不重复、无索引
  • TreeSet:按大小默认升序排序、不重复、无索引
Set<Integer> set = new HashSet<>();
Set<Integer> linkedHashSet = new LinkedHashSet<>();
Set<Integer> treeSet = new TreeSet<>();

Java中每个对象都有一个哈希值,可调用public int hashCode()获取。
若希望Set集合认为2个内容一样的对象是重复的,则需重写对象的hashCode()equals()方法。(类似C++类重载关系运算符"==")

1.3.1 HashSet

基于哈希表实现,元素无序、不重复、无索引,增删改查都较好。

JDK8之前,哈希表=数组+链表,每条链表头插新元素。
JDK8开始,哈希表=数组+链表+红黑树,每条链表尾插新元素,且当链表长度>8、数组长度≥64时自动将链表转成红黑树

1.3.2 LinkedHashSet

与HashSet类似,基于哈希表实现,但每个元素额外追加了双链表的机制,记录了其前后元素的位置,因此元素有先后顺序、不重复、无索引。(参考C++STL中的unordered_set)

1.3.3 TreeSet

基于红黑树实现元素按大小默认升序排序、不重复、无索引。(参考C++STL中的set)

  • 对于数值类型(Integer、Double),默认按数值本身的大小进行升序排序。
  • 对于字符串类型,默认按字典序排序。

自定义排序规则

  1. 让自定义类型实现Comparable接口,重写compareTo(Object o)方法来指定比较规则。(参考C++类重载小于号,顺序相同)
public class Student implements Comparable<Student> {
	private int age;
	
	@Override
	public int compareTo(Student o) {
		// this > o  => 返回正整数
		// this < o  => 返回负整数
		// this == o => 返回0
		return this.getAge() - o.getAge();
	}
}
  1. 调用TreeSet集合有参构造器public TreeSet(Comparator<? super E> comparator)设置Comparator对象(比较器对象,制定比较规则)。(相减顺序关系同上)
Set<Student> students = new TreeSet<>(new Comparator<Student>() {
	@Override
	public int compare(Student o1, Student o2) {
		// 比较o1和o2成员,同上
		return o1.getAge() - o2.getAge();
	}
});
// 用Lambda表达式简化
Set<Student> students = new TreeSet<>((o1, o2) -> {
	return o1.getAge() - o2.getAge()
});

2 工具类Collections

提供了一系列静态方法用来操作集合

方法名称说明
public static <T> boolean addAll(Collection<? super T> c, T... elements)给集合批量添加元素
public static void shuffle(List<?> List)打断List集合中的元素顺序
public static <T> void sort(List<T> list)对List集合中的元素进行升序排序
public static <T> void sort(List<T> list, Comparator<? super T> c)对List集合中的元素按指定的规则进行排序

泛型的理解

  • <? super T>:表示?匹配的类型都是T的父类,包括T本身。
  • <? extends R>:表示?匹配的类型都是类型R的子类,包括R本身。

3 双列集合:Map

  • 根据键决定集合的特点
    • HashMap:无序、不重复、无索引
    • LinkedHashMap:有先后顺序、不重复、无索引
    • TreeMap:按大小默认升序排序、不重复、无索引

3.1 Map接口

Map系列集合Map<K, V>的每个元素key=value称为一个键值对/Entry对象,所有键不允许重复

Map<String, Double> map = new HashMap<>();
Map<String, Double> linkedHashMap = new LinkedHashMap<>();
Map<String, Double> treeMap = new TreeMap<>();

3.1.1 通用方法

方法名说明
public V put(K key, V value)添加元素
public int size()获取集合的大小
public void clear()清空集合
public boolean isEmpty()判断集合是否为空
public V get(Object key)根据键获取对应值
public V remove(Object key)根据键删除整个元素,并返回对应的值
public boolean containsKey(Object key)判断是否包含某个键
public boolean containsValue(Object value)判定是否包含某个值
public Set<K> keySet()获取全部键的集合
public Collection<V> values()获取Map集合的全部值

3.1.2 遍历

(1) 键找值

先获取Map集合全部的键,再通过遍历键来找值

方法名说明
public Set<K> keySet()获取全部键的集合
public V get(Object key)根据键获取对应值
Set<String> keys = map.keySet();
for (String key : keys) {
	double value = map.get(key);
	System.out.println(key + " -> " + value);
}
(2) 键值对

把键值对看成一个整体进行遍历

方法名说明
Set<Map.Entry<K, V>> entrySet()获取索引键值对集合

Map.Entry<K, V>提供的方法

方法名说明
K getKey()获取键
V getValue()获取值
Set<Map.Entry<String, Double>> entries = map.entrySet();
for (Map.Entry<String, Double> entry : entries) {
	String key = entry.getKey();
	double value = entry.getValue();
	System.out.println(key + " -> " + value);
}
(3) forEach()
map.forEach(new BiConsumer<String, Double>() {
	@Override
	public void accept(String k, Double v) {
		System.out.println(k + " -> " + v);
	}
});
// 使用Lambda表达式简写
map.forEach((k, v) -> {
	System.out.println(k + " -> " + v);
});

3.2 HashMap

与HashSet相同,基于哈希表实现(实际Set系列集合底层均为基于对应的Map实现,集合元素只有键没有值而已,下同),元素无序、不重复。

3.3 LinkedHashMap

与LinkedHashSet相同,基于哈希表实现,每个元素额外追加了双链表的机制,记录了其前后元素的位置,因此元素有先后顺序、不重复。(参考C++STL中的unordered_map)

3.4 TreeMap

与TreeSet相同,基于红黑树实现元素按大小默认升序排序、不重复,自定义排序方法也完全一致。(参考C++STL中的map)


4 Stream流

Stream是jdk8开始新增的一套API(java.util.stream.*),可以用于操作集合或者数组的数据。Stream流大量结合了Lambda的语法风格,支持链式编程,提供了一种更加强大,更加简单的方式操作集合或者数组中的数据,代码更简洁,可读性更好。
在这里插入图片描述

4.1 获取Stream流

4.1.1 获取集合的Stream流

Collection提供的方法说明
default Stream<E> stream()获取当前集合对象的stream流
/* 获取Collection系列集合的Stream流 */
List<String> names = new ArrayList<>();
Stream<String> stream = names.stream();
/* 获取Map系列集合的Stream流 */
Map<String, Double> map = new HashMap<>();
// 利用键的Set获取键流
Set<String> keys = map.keySet();
Stream<String> ks = keys.stream();
// 利用值的集合获取值流
Collection<Double> values = map.values();
Stream<Double> vs = values.stream();
// 利用键值对的Set获取键值对流
Set<Map.Entry<String, Double>> entries = map.entrySet();
Stream<Map.Entry<String, Double>> kvs = entries.stream();

4.1.2 获取数组的Stream流

Arrays类提供的方法说明
public static <T> Stream<T> stream(T[] array)获取当前数组的stream流
Stream类提供的方法说明
public static <T> Stream<T> of(T... values)获取当前接收数据的stream流
// 获取数组的Stream流
String[] names = {"set", "unordered_set", "multiset"};
String<String> stream = Arrays.stream(names);	// 用Arrays类提供的方法
String<String> stream2 = Stream.of(names);		// 用Stream类提供的方法

4.2 中间方法

中间方法:调用完成后会返回新的Stream流,可以继续使用(支持链式编程)

Stream流常用中间方法说明
Stream<T> filter(Predicate<? super T> predicate)用于对流中的数据进行过滤
Stream<T> sorted()对元素进行升序排序
Stream<T> sorted(Comparator<? super T> comparator按照指定规则排序
Stream<T> limit(long maxSize)获取前几个元素
Stream<T> skip(long n)跳过前几个元素
Stream<T> distinct()去除流中重复的元素(对于自定义对象需重写hashCode()equals()方法)
<R> Stream<R> map(Function<? super T, ? extends R> mapper)对元素进行加工(“将e1变成e2”),并返回对应的新流
static <T> Stream<T> concat(Stream a, Stream b)合并a和b两个流为一个流

4.2 终结方法

终结方法:调用完成后不会返回新Stream,无法继续使用流了

Stream流常用终结方法说明
void forEach(Consumer action)对此流运算后的元素执行遍历
long count()统计此流运算后的元素个数
Optional<T> max(Comparator<? super T> comparator)获取此流运算后的最大值元素
Optional<T> min(Comparator<? super T> comparator)获取此流运算后的最小值元素

收集Stream流:把Stream流操作后的结果转回到集合或者数组中去返回,Stream流只能收集一次

用于收集Stream流的终结方法说明
R collect(Collector collector)把流处理后的结果收集到一个指定的集合中去
Object[] toArray()把流处理后的结果收集到一个数组中去

综合运用!

// 例1:过滤出所有大于等于60的数据,去重后降序排序,跳过前3个输出
List<Double> scores = new ArrayList<>();
Collection.addAll(scores, 92.4, 52.5, 1.2, 61.1, 75.0, 88.8, 34.9, 69.3, 71.1);
scores.stream().filter(s -> s >= 60).distinct().sorted((o1, o2) -> o2 - o1)
	  .skip(3).forEach(System.out::println);

// 例2:过滤出年龄(值)大于等于15且小于等于20的学生(键),按年龄降序排序输出
Map<String, Double> map = new HashMap<>();
map.put("teresa", 17);
map.put("akira", 1234);
map.put("undefined", 20);
map.put("kina", 21);
map.put("yohane", 16);
map.entrySet().stream().filter(e -> 15 <= e.getValue() && e.getValue() <= 20)
			  .sorted((o1, o2) -> o2.getValue() - o1.getValue())
			  .forEach(e -> System.out.println(e.getKey() + "->" + e.getValue()));
// 例3:获取所有名字长度小于等于6的学生的年龄,返回其最大值
Double maxAge = map.entrySet().stream().filter(e -> e.getKey().length() <= 6).map(e -> e.getValue())
							  .max((o1, o2) -> Double.compare(o1, o2)).get();

// 例4:合并两个流,再遍历输出其前3个
Stream<String> st1 = Stream.of("kira", "owns");
Stream<String> st2 = Stream.of("kira2nd", "again", "again", "sassy");
Stream<String> allSt = Stream.concat(st1, st2);
allSt.limit(3).forEach(System.out::println);
// 例5:把流st1、st2、scores中的结果分别放到List、Map、数组中
List<String> list = st1.collect(Collectors.toList());
Map<String, Integer> map = st2.distinct().collect(Collectors.toMap(s -> s, s -> s.length()));	// 去重后分别设置键与值,此处为"字符串: 其长度"
Object[] arr = scores.toArray();	// 假如是转换成自定义类数组,则需在方法中设置转换后的元素(与toMap()设置键与值同理)

  • 11
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Akira37

💰unneeded

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值