Java集合

来源:互联网 发布:树上10只鸟 程序员 编辑:程序博客网 时间:2024/06/09 13:55

Java集合就像是一种容器,可以将多个对象(其实是对象的引用)丢入其中。Java集合分为Set、List、Queue和Map四大类,集合类又称容器类。

  1. Set代表无序不重复的集合
  2. List代表有序可重复的集合
  3. Queue代表队列集合
  4. Map代表映射关系集合

这里写图片描述

有图可知,如果访问List集合,我们可以通过元素的索引来访问,如果访问Map集合中的元素,可以根据每项的key来访问其value,但是如果访问set集合中的元素,则只能根据元素本身来访问(因而不能重复)。

Java5之前,Java集合会丢失放入其中的对象的数据类型,把所有对象都当成Object类型来处理。Java5 增加泛型之后,Java集合可以记住容易中对象的数据类型,从而可以编写出更简洁、更健壮的代码。

1、集合概述

加入我们需要存放多个数据,可以考虑放在数组中,但是放在数组中的问题是,数组长度不可变,而且数组不能保存映射关系的数据。

为了保存不确定长度的数据和保存具有映射关系的数据(也被称为关联数组),Java提供了集合类。所有集合类都位于java.util包下,后来为了处理多线程环境下的并发安全问题,java 5在java.util.concurrent包下提供了一些多线程支持的集合类。

集合类和数组不一样,数组既可以保存基本类型数据,又可以保存引用类型的数据(其实保存的就是引用)。但是集合类中只能保存引用类型的数据。

Java集合类主要由Collection和Map两个接口派生出来的,这两个是根接口,其下包含了很多子接口和实现类。

Collection接口

Map接口

其中颜色重的表示最常用的实现类。

2、Collection和Iterator接口

Collection接口是Set、List和Queue的父接口,因而Collection接口中定义的方法,是三个子接口的公共方法。

  1. 添加对象,add(o)、addAll(c)、
  2. 查找对象,contains(o)、containsAll(c)
  3. 删除对象,remove(o)、removeAll(c)、retainAll(c)、clear
  4. 容器大小,isEmpty()、size()
  5. 其他,iterator()、toArray()

2.1 使用Lambda表达式来遍历集合

Java8中提供了新的函数式接口包以及4个函数式接口。
java.util.function这个包,提供了4个核心接口,

  1. 功能型接口,Function

    public Interface Function<T,R>{    public R apply(T t);}

    此接口需要接收一个参数,并且返回一个处理结果。

  2. 消费型接口,Consumer

    public Interface Consumer<T>{    public void accept(T t);}

    此接口只负责接收数据,并不返回处理结果。

  3. 供给接口,Supplier
public interface Supplier<T>{    public T get();}

此接口不接受参数,但可以返回结果
4. 断言型接口,Predicate

public interface Predicate<T>{
public boolean test(T t);
}

进行判断操作使用

Jdk1.8 中有以上的四个功能型接口,所以一般很少会由用户定义新的函数式接口。

观察函数式接口,特征是接收参数,并且返回处理结果
String有个public boolean startsWith(String str)

Function<String, Boolean> fun = "##hello" :: startsWith;Syso(fun.apply("##"));  //返回true 

消费型接口

class MyDemo{    public void print(String s){  //此方法有参数无返回值        syso(s);    }}//mainConsumer<String> cons = new MyDemo() :: print;cons.accept("Hello Java");

供给型接口,
例如String类的toUpperCase()方法,public String toUpperCase();

Supplier sup = "hello" :: toUpperCase();syso(sup.get());

断言型接口
String

Predicate<String> pre = "hello" :: equalsIgnoreCase;syso(pre.test("Hello"));  //true

这几个接口包含了所有可能出现的方法引用,也是函数式接口的代表,有许多的接口与他们类似,例如Function中有个DoubleToIntFunction,其中的apply方法int applyAsInt(double value); 只能接收double转换为int。

3、Set集合

Set集合和Collection基本相同,没有添加额外的方法,实际上Set集合就是Collection,只是行为略有不同,不能添加重复的元素。

如果把两个相同的元素add()到同一个集合,会返回false。

Set集合有三种常见的实现类,HashSet、TreeSet、EnumSet。

3.1 HashSet

HashSet是Set接口的典型实现类,我们使用的Set大多数都是这个Set。HashSet是按照Hash算法来存储集合中的元素,因此具有很好的存取和查找性能。

HashSet具有以下特点:

  1. 不能保证元素的排列顺序,顺序可能与添加元素的顺序不一致;
  2. HashSet不是同步的,加入有多个线程来操作同一个HashSet,必须通过代码来同步;
  3. 元素值可以为null。

当向HashSet中存入一个值时,HashSet会调用这个对象的hashCode()方法,获得该对象的hash值,然后根据这个值决定其在HashSet中存储的位置。如果两个对象通过equals方法得到的值相同,但是他们的Hash值不同,他们仍将被存储在不同的位置。

HashSet判断两个元素对象相等的标准是equals()方法比较相等,并且两个对象的hashCode()值也相等。

要注意的一点是,当把一个对象放入到HashSet中时,如果重写了这个对象对应类的equals方法,则应该也重写该类的hashCode()方法。规则是,如果两个对象,通过equals方法返回true,则两个对象的hashCode值也应该相同。如果两个hashCode不同,则会被Set都存储,则Set中就会存在两个值一样的对象,这是不符合Set规范的。如果两个对象的hashCode值相同,但是equals比较的值不同,这个也是不对的。如果两个对象的hashCode相同,HashSet会试图将他们保存在同一个位置,结果发现已经有数据,则会在这个位置使用链式结构来保存多个对象。HashSet访问集合元素时会根据元素的HashCode值来快速定位,同一个位置多个值会导致性能下降。

hash算法的价值在于速度,HashSet类似于数组,数组也包含多个元素,每个元素都有索引,如果需要访问元素,只需要提供索引,然后根据索引计算元素在内存中的存储位置。为什么不直接用数组,而用HashSet呢,因为数组是连续不可变长度的存储。HashSet使用hashCode做索引,不要求索引是连续的,长度也是可变,可以根据索引快速定位到内存地址。

当向HashSet中添加可变的对象时,必须十分小心,如果修改集合中的对象,有可能导致该对象与集合中的其他对象相等,从而导致HashSet无法正确的访问到该对象。

3.2 LinkedHashSet类

HashSet还有个子类,LinkedHashSet类,它也是使用对象的hashCode来决定对象的存储位置,但它同时使用链表来维护元素的次序,这样使得元素看起来是以插入的顺序来保存的。也就是说,遍历LinkedHashSet时,会以其插入顺序来放访问元素。但是其仍然是HashSet,仍然不会允许元素重复的。

LinkedHashSet需要维护元素的插入顺序,因而性能比HashSet要略低,但是在迭代Set中全部元素时具有很好的性能,因为其是你链表来维护内部顺序的。

3.3 TreeSet类

TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。

相比与HashSet,TreeSet额外提供了几个跟有序相关的方法,比如访问第一个、前一个、访问最后一,后一个,截取子TreeSet等方法。

与HashSet使用hash算法来决定元素的位置不同,TreeSet使用红黑树的数据结构来存储集合元素。Tree

0 0
原创粉丝点击