Java集合(Collection)-小记02

前言:最近复习了java的集合框架,大致的总结一些重点的内容包括基本概念和常用方法以及一些注意事项。

Collention集合的概念和分类

首先总结了Collention(单列集合)的内容,图1对Collection进行了分类,图片的来源是What is Collection Framework in Java? | Hierarchy & Interfaces of Java Collection Framework | Java Collections Methods & Interfaces - BTech Geeksicon-default.png?t=M4ADhttps://btechgeeks.com/what-is-collection-framework-in-java/

图1. Collection的分类

首先我们必须思考一个问题:为什么我们需要集合?

我认为集合是比数组更“灵活”的结构,数组可以放任任意类型的对象,但是它的长度是一开始就要初始化好的,一旦我们需要进行增删操作,需要重新new一个新的数组,大量的数据肯定会影响效率,集合的好处就是可以添加或者删除元素,长度也会改变。这对于Java这种面向对象语言来说就非常有用。

Collection有自己的父类:Iterable<E>接口,而Collection<E>自己也是一个接口,从图1中,继承Colletion<E>接口的List<E>,Queue<E>以及Set<E>这三个也是接口。所以以上所有的接口都是不能在代码中实例化的,但是想使用他们的方法,我们就需要使用以上三个接口的实现类。以下我重点说明List接口和Set接口的实现类。

Collection(单列集合)

List接口(有索引,可以重复,有序)

ArrayList:底层是数组, 查询快,增删慢

LinkedList:底层是链表,查询慢,增删快

Vector:与ArrayList类似,线程安全但是效率低

                ArrayList线程不安全,但是效率高

ArrayList底层是数组,根据数组的数据结构特点,我们只需要知道索引值就可以找到对应的元素,但是一旦删除/增加某个元素,后面所有的元素都需要向前/向后移动,因此查询快,增删慢。

LinkedList底层是链表,根据链表的特点,它是没有索引值的,它之和指向它的节点和它指向的节点有关系。如果需要查询某个元素,我们需要遍历整个链表,因此效率慢,但是要删除/增加某个元素,我们只需要改变节点的指针就可以实现,因此效率快。

Set接口(无索引,不重复,可排序)

HashSet(无序):底层是哈希表

LinkedHashSet(有序):底层是链表+哈希表

TreeSet(可有序):与HashSet类似,线程安全但是效率低

                                HashSet线程不安全但是效率高

Collection集合的常用方法

根据他们的继承关系和实现关系,我从Cllection开始向下,总结从共有的方法到独有的方法。

Collection:方法返回值  方法名(参数);

boolean add(E e);         增加一个元素,布尔值表示添加成功或者失败

boolean remove(Object obj);         去除某个元素,布尔值表示删除成功或者失败

boolean contains(Object obj);         是否包含某个元素

boolean isEmpty();         集合是否为空

int size();         返回一个集合的长度

void clear();         清空集合的元素

Object[] toArray[];         将集合转成一个数组,数字的元素就是集合的元素

Iterator<E> iterator();         生成集合的构造器,用于遍历集合

**************************************************************************************************************

List:

void add(index, E e); //        添加一个元素

E remove(int index); //        删除一个元素

E set(int index, E e); //        改变一个元素

E get(int index); //        获得指定元素

我们可以看到,Collection和List都有add方法,但参数不同,说明这是两种不同的抽象函数,因此他们的实现类比如ArrayList可以使用两种添加方法。对于ArrayList,只要使用List接口和Collection接口的抽象方法就可以解决大部分的java基础问题,同时ArrayList在工作中也是用的很多的数据结构。但在工作中,我们需要多思考是否可以使用LinkedList去解决,毕竟LinkedList的增删操作要比ArrayList效率高。

LinkedArrayList:

void addFirst(E e);         在头部添加一个元素                     

void addLast(E e);         在尾部添加一个元素 

E getFirst();       获得头部元素   

E getLast();       获得尾部元素 

E removeFirst();         移除头部元素

E removeLast();         移除尾部元素

**************************************************************************************************************

Set类:

对比于List集合,Set集合的最大的特点是无重复的。它的实现类使用的方法一般都会使用Collection的方法。

Set为什么元素是不能重复,它的底层原理是什么?

以下是JDK1.8版本的原码,图2是Set的add方法,原码中可以看到set的底层添加方法实质就是调用了map的方法,只是将map的value键统一指向了一个叫PRESENT的空对象。之后我们点开map.put的原码,它返回了一个叫putVal的方法,里面有很多参数。

图2. Set的add方法原码

我们打开这个方法的原码,里面的参数我们只需要重点关注两个,hash和key。整个代码很长,但是大致的内容就是,set拿到元素时,会先计算他们的哈希值hash,之后用hash去%一个数组的长度,算出的值就是就是在set中的位置,如果整个位置为null,就将这个元素加入进去,如果这个位置有元素说明他们哈希值相等,然后就会去用==和equals方法比较他们,由代码可以看出,只要==的结果是true,那么后面就会短路,不再判断直接返回一个true,对于两个元素,哈希值相同,地址值相同就会认为他们是同一个元素而不添加,因此如果想去重,我们需要重写hashcode和equals方法,只要他们内容一致,我们就会认为他们是同一个元素从而达到去重。

另外,如果两个元素,他们内容不同,但是哈希值也是有可能相同的,这就是哈希冲突。因此如果不重写==和equals方法,第二个元素就会把第一个元素顶替掉。

图3. putvAL方法

图4. Objetc类的equals方法

equals方法属于Object类中的方法,所以我们在自定义类中可以去直接重写该方法。它在Object中本质上就是用==。

图5. Objetc类的==方法

hashCode方法也属于Object类,其中native关键字指的是方法是有方法体的,这里不显示是因为方法是由其它语言比如C去实现的,在执行过程中JVM会自己执行我们看不到的方法体。

遍历集合

遍历集合的方式有三种:

1>普通for循环去遍历。这是是最容易理解的如图2,但是前提是这个集合是有索引值的。

 图6. for循环遍历

2>构造Iterator去遍历。需要注意的是,在遍历集合时,Iterator是不能对数据进行增删操作的。但我们可以改变它的值如图3,我们想移除遍历的元素会直接报编译错误。

图7. Iterator遍历集合

3>for each方法遍历。这种方法也叫增强for循环,虽然听着和for循环很相似,但是在java的底层实现是完全不同的,for each的实现在java底层其实就是构造器Iterator,只是简化了一些步骤。我个人也比较喜欢这个,简单易懂。

图8. for each遍历集合 

Map集合

首先,Map是由两个部分组成:key键和value键,他们可以是任意数据类型,但是key不能是重复的,因为key的底层就是Set集合,一个key对于一个唯一的value。value可以重复

Map的常见分类:

如图9所示是Map的分类。图片来源Java Collections Framework Video Tutorial (marcus-biel.com)icon-default.png?t=M4ADhttps://marcus-biel.com/java-collections-framework/

图9. Map集合 

HashMap:        底层是哈希表,无序。是Map接口的实现类

                        线性不安全但是效率高

                        注意:HashMap的初始容量是16,一旦容量超过总容量*0.75这个值,他就会扩容,扩容量是之前容量的2倍。

LinkedHashMap:        底层是哈希表+链表,有序。是HashMap的子类

HashTable:         与HashMap相同,线性安全但是效率低。

Map集合的常用方法

因为Key键值具有唯一性,所以Map的方法是有点特别的。

V    put(K k, V v);          添加一个元素,如果K存在,顶掉V并且返回被替换的V

                                如果K不存在,则返回null

 V   remove(Object K);        根据K删除整个键值包括K和V

                                                       如果K存在,返回V,如果K不存在,返回null

V get(Object k); 根据K去获取对应的值,存在返回,不存在返回null,这个

                                        和增删不同,增删是已经做完了操作,get方法去查询的整个键在Map中

boolean containsKey(Object K); 检查是否由唯一的K,由返回ture,没有false

 小结:Map的方法常用的就这些,可以看出,K的唯一性是Map的特点,这也和Map的底层部分有Set有关。     

Map集合遍历的三种方法

1>将双列集合转成单列集合

Set<Entry<K,V>>      entrySet<K,V>();

转成单列集合后,我们就可以根据Collection的遍历方法去遍历Set,获取每一个Entry,然后根据Entry的getKey()和getValue()去获取对应的值。

2>通过K遍历所有的元素(K 的唯一性)

Set<K>    keySet();

我们可以获取K的set集合,泛型是K键值的数据类型,之后还是使用Collection的遍历方法,如果我们还想获取Value,可以使用Map的get()方法,找到K对应的值。

3> 只获取Value

Collection<V>     values();

获取values的Collention的集合,泛型是V键值对应的数据类型,因为V是可以重复的,我们不能通过遍历这个Collection再去获得K。

小结:如果我们有自定义类型的对象作为参数组成一个Map,比如我new一个HashMap,泛型K键值是student,V键值是Integer,为了保证K的唯一性,我们最好在student类中重写hashcode()和equals()方法。

总结

以上就是Java集合的基本内容,集合是Java的重要部分,许多现实中的事物我们都可以抽象成集合然后去处理它们。

  • 11
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值