1 Collection接口

Collection 接口是Set接口和List接口的父类

2 Set接口和List接口的区别

单纯的说这两个接口的话有以下几个区别

  1. List是有序的,可以允许重复对象和插入多个null 值,而Set不允许

  1. Set大部分是存取无序的,(TreeSet是存取有序的,这个需要特别注意)

3 Set接口的子类

Set接口平常用的比较多的是以下几个子类

3.1 HashSet

底层数据结构是哈希表(无序,并且唯一)

首先来验证一下无序

public class HasSet {public static void main(String[] args) {//创建一个Hashset集合Set<String> set = new HashSet<String>();//给集合赋值set.add("李四");set.add("张三");set.add("王五");set.add("张麻子");set.add("赵六");//输出结果在控制台System.out.println(set);}
}

在创建HashSet集合之后,在里面添加了5个字符串类型的值,输出打印结果如下

为啥输出的结果跟添加的顺序不一致呢,

如果你再多运行几次呢

多运行你会发现还是跟之前一样的输出结果,但与添加顺序不一致

原因

(1)HashSet存储数据在并非按照数组索引顺序添加,而是根据数组的哈希值存储添加元素时,通过元素的哈希值,在调用哈希算法计算元素在数组中应该存储的位置,也就是说你add方法存的顺序并不是你存入的顺序

(2)因为每个元素对应的哈希值唯一,所以存储位置也唯一确定.因此在某种意义上来说,HashSet也是有序的,因为输出的永远是经过哈希算法处理过的顺序

无序的问题处理完了,咱们再来看看唯一

为啥说HashSet集合是唯一的呢,先上代码

public class HasSet {public static void main(String[] args) {//创建一个Hashset集合Set<String> set = new HashSet<String>();//给集合赋值set.add("李四");set.add("张三");set.add("王五");set.add("张麻子");set.add("赵六");//再次添加一个同样的值set.add("赵六");//输出结果在控制台System.out.println(set);}
}

输出看一下控制台的结果

算上重复的值,我一共添加了6个,可控制台确是5个,为啥呢

咱们先看一下HashSet源码

HashSet的无参构造如下图所示

HashSet集合操作,是将数据值存入至HashMap的key中(HashMap下面会讲解),然而HashMap的key又是不能重复的,所以HashSet保持了唯一性

再来一个小问题:HashSet集合可不可以存null值呢

答案是可以的,话不多说,上代码

public class DemoTow {public static void main(String[] args) {//创建一个Hashset集合Set<String> set = new HashSet<String>();//给集合赋值set.add("李四");set.add("张三");set.add("王五");set.add("张麻子");set.add("赵六");set.add(null);//输出结果在控制台System.out.println(set);}
}

运行结果如下图所示

但只能存一个null值,因为HashSet的底层是HashMap,他支持null键,但key不能重复,所以只能有一个null值

3.2 LinkedHashSet

(1)LinkedHashSet是HashSet的子类

(2)它的底层结构是由链表和哈希表组成

由链表保证元素有序

由哈希表保证元素唯一

(3)同样也支持一个null值的存储

先来看看它是不是能够保持数据的插入顺序

public class Demo {public static void main(String[] args) {HashSet<String> linkedHashSet = new LinkedHashSet<>();linkedHashSet.add("张三");linkedHashSet.add("李四");linkedHashSet.add("王五");linkedHashSet.add("赵六");linkedHashSet.add("张麻子");System.out.println(linkedHashSet);}
}

再来看看运行结果

它很明显不同于HashSet,它能够保证数据的插入顺序

3.3 TreeSet

TreeSet集合特点

(1)无序不可重复,但是可以按照元素的大小顺序自动排序

(2)底层数据结构:红黑树

先来看看按照元素的大小顺序排序

public class Demo {public static void main(String[] args) {TreeSet<Integer> list = new TreeSet<Integer>();list.add(1);list.add(21);list.add(81);list.add(61);list.add(31);list.add(1011);list.add(101);System.out.println(list);}
}

如上图所示,创建了一个TreeSet集合,并且给上了一些没有大小顺序的值

运行结果如下图所示

运行结果把我给的没有顺序的值进行了排序,这怎样做到的呢?

平时往TreeSet中添加基本类型调用的是TreeSet的无参构造方法,因此是自然排序。

TreeSet底层就是一个TreeMap,它的有参构造还可以传一个比较器,除了基本类型,还可以传对象什么的,把传入的对象进行一个排序

4 List接口的子类

List常见的两个子类分别是ArrayList和LinkedList(其他还有,只不过不经常用,在这里我只总结这两个)

4.1 ArrayList和LinkdLsit的共同点和区别

共同点:都实现了List接口,同样都是线程不安全,都能保证元素存储有序

区别:ArrayList底层是动态数组,查询快增删慢,LinkedList底层是双向链表,查询慢增删快

4.2 ArrayList

  1. 能够存储重复元素,能存null值

  1. ArrayList可随着元素的增长而自动扩容,正常扩容的话,每次扩容到原来的1.5倍(初始容量为10)

public class Demo11 {public static void main(String[] args) {ArrayList<String> arrayList = new ArrayList<>();arrayList.add("张三");arrayList.add("李四");arrayList.add("王五");arrayList.add("赵六");arrayList.add("张麻子");arrayList.add("张麻子");arrayList.add("汤师爷");arrayList.add(null);System.out.println(arrayList);//运行结果:张三, 李四, 王五, 赵六, 张麻子, 张麻子, 汤师爷, null}
}

4.3LinkedList

LinkedList不用扩容,它是一个双向链表,没有初始化大小,也没有扩容的机制,就是一直在前面或者后面新增就好

public class Demo12 {public static void main(String[] args) {Collection<String> list = new LinkedList<String>();list.add("张麻子");list.add("黄四郎");list.add("汤师爷");list.add(null);list.add("张麻子");System.out.println(list);//运行结果:张麻子, 黄四郎, 汤师爷, null, 张麻子}
}

5 Map接口

5.1 HashMap

基于哈希表的 Map 接口的实现。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

1.HashMap集合底层是一个哈希表结构

2.HashMap是一个无序的集合:存储元素和取出元素顺序有可能不一致

public class DomeHashMap {public static void main(String[] args) {//创建一个HashMapHashMap<String, String> hashMap =new HashMap<String, String>();//map集合添加元素采用put方法hashMap.put("张麻子","40");hashMap.put("汤师爷","41");hashMap.put("黄四郎","42");hashMap.put("胡万","43");hashMap.put("张三","18");hashMap.put("李四","18");hashMap.put("王五","49");System.out.println(hashMap);//运行结果:{汤师爷=41, 李四=18, 张三=18, 王五=49, 黄四郎=42, 胡万=43, 张麻子=40}//添加一个重复的Key再运行试试结果hashMap.put("王五","49");System.out.println(hashMap);//运行结果:{汤师爷=41, 李四=18, 张三=18, 王五=49, 黄四郎=42, 胡万=43, 张麻子=40}//虽然添加了两次,但只打印了一次//map集合删除元素使用remove()方法hashMap.remove("李四");System.out.println(hashMap);//运行结果:{汤师爷=41, 张三=18, 王五=49, 黄四郎=42, 胡万=43, 张麻子=40}//根据Map集合中的Key删除了Value}
}

5.2 Map集合的遍历方式

  1. 键找值的方式

  • 使用Map集合中的方法keySet把Map集合中所有key取出来,存储到一个Set集合中

  • 再去遍历Set集合从Map中取出Key对应的Value

public class DomeHashMap {public static void main(String[] args) {//创建一个HashMapHashMap<String, String> hashMap = new HashMap<>();hashMap.put("张麻子","土匪");hashMap.put("汤师爷","县长");hashMap.put("黄四郎","恶霸");//使用KeySet方法拿到所有的keySet<String> strings = hashMap.keySet();//遍历这个Set集合for (String string : strings) {String s = hashMap.get(string);System.out.println(s);}//运行结果:县长 恶霸 土匪}
}
  1. 键值对流程

  • 先把Map集合转换成Set集合,Set集合中每个元素都是键值对实体类型了

  • 遍历Set集合,然后提取键以及提取值

public class DomeHashMap {public static void main(String[] args) {//创建一个HashMapHashMap<String, String> hashMap = new HashMap<>();hashMap.put("张麻子","土匪");hashMap.put("汤师爷","县长");hashMap.put("黄四郎","恶霸");//先把Map集合转换成Set集合Set<Map.Entry<String, String>> entries = hashMap.entrySet();//再遍历Set集合for (Map.Entry<String, String> entry : entries) {String key = entry.getKey();String value = entry.getValue();System.out.println("Key:" + key + " Value:" +value);}//运行结果:// Key:汤师爷 Value:县长//Key:黄四郎 Value:恶霸//Key:张麻子 Value:土匪}
}

5.3 HashMap集合的put方法和get方法原理

  1. put方法

  • NewHashMap的时候底层没有创建数组,当调用put方法的时候,才开始创建长度为16的数组(jdk8以后创建的是Node[]数组,而非Entry[]数组)

  • 调用put方法后,先把Key和Value存到Node节点中

  • 先调用key的hashCode方法得出哈希值,并通过哈希算法把数值转换成数组的下标

  • 下表标如果没有任何元素就把Node添加到这个元素上如果下标对应的位置上有链表,就会拿着Key和链表上的每个Key进行equals比较如果所有的equals方法得到的结果都是false,那么这个新的节点就会被添加到最末尾,如果有结果返回值是true那么这个节点的value就会被覆盖

  1. get方法

  • 先调用key的hashCode方法得出哈希值,并通过哈希算法把数值转换成数组的下标

  • 通过数组下标快速定位到某个位置,如果这个位置没有数据,则返回null

  • 如果该下标下是单向链表,就会拿着Key跟链表上的每一个节点进行比较,如果都返回false,那最终结果就是返回空,如果返回结果是true,那么就会把该数据返回去

5.4 哈希冲突和解决办法

  1. 什么是哈希冲突

哈希冲突(Hash Collision)是指在使用哈希表存储数据时,两个或多个不同的键(Key)通过hashCode方法计算出来的哈希值是一样的,被哈希函数映射到同一个位置的情况。这种情况会导致数据的存储和查找变得复杂,因此需要采取一些措施来解决哈希冲突。

  1. 如何解决哈希冲突

解决哈希冲突一般有四种方法:开放寻址法、链地址法(拉链法)、再哈希法、建立公共溢出区等方法

(1) 开放寻址法,该方法一共有三种方式

  • 线性探测法:是指当发生哈希冲突时,从当前位置开始,依次向后查找下一个空闲的位置,或查遍全表。

  • 二次探测法:是指当发生哈希冲突时,从当前位置开始,按照二次函数的规律查找下一个空闲的位置。

  • 双重哈希法:是指当发生哈希冲突时,使用另一个哈希函数计算出下一个空闲的位置

(2) 链地址法

  • 该方法是将所有散列到同一个地址的数据项存储在一个单链表中。这样,当查找某个数据项时,只需要在对应的链表中进行搜索即可。

(3)再哈希法

  • 在发生冲突的时候,再用另一个哈希函数算出哈希值,直到算出的哈希值不同为止。

(4) 建立公共溢出区

  • 在创建哈希表的同时,再额外创建一个公共溢出区,专门用来存放发生哈希冲突的元素。查找时,先从哈希表查,查不到再去公共溢出区查

5.5 LinkedHashMap

  • LinkedHashMap是HashMap的子类

  • 能根据存入顺序输出数据(元素存储有序)

  • 线程不安全

先来验证一下存取有序,代码如下

public class DemoLinkedHashMap {public static void main(String[] args) {//创建一个linkedHashMapLinkedHashMap map = new LinkedHashMap();//添加元素map.put("马匪","张麻子");map.put("县长","马邦德");map.put("乡绅","黄四郎");map.put("小弟","胡万");//输出结果System.out.println(map);//最终结果//{马匪=张麻子, 县长=马邦德, 乡绅=黄四郎, 小弟=胡万}}
}
  • 为什么LinkedHashMap能够保证元素的存取有序呢

因为LinkedHashMap对HashMap的newNode、afterNodeAccess、afterNodeInsertion方法都进行了重写,同时也对HashMap中的Node进行了重写增加了before和after字段。

(源码如下)

LinkedHashMap中有一个字段accessOrder

这个accesOrder其实就是控制节点在linkedHashmap中

  • 如果是true, 基于访问顺序

  • 如果是false,就按插入顺序排列 (就是我们的存入顺序)

5.6 关于Map的小拓展

以上是我们常用的Map集合,还有一些其他的,比如HashTable,TreeMap

  1. 关于HashTable

  • 线程安全

  • 不允许null值null键

  • 性能比HashMap差

  • Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。但二者都实现了Map接口

为什么HashTable是线程安全的呢,因为HashTable的每一个方法都加了Synchronize

  1. 关于TreeMap

  • treeMap根据其键的自然顺序排序,或者根据treeMap创建时提供的Comparator排序

  • 不是线程安全的

  • key 不可以存入null

  • 底层是基于红黑树实现的

6 迭代器

  1. Collection中定义了一个"遍历元素"的方法:iterator(),可以获取一个"迭代器"对象。

  1. Iterator是一个接口,常用方法:

  • boolean hasNext():判断是否有元素可以迭代

  • .next():获取当前的元素

  1. 注意:

  • Iterator迭代器是单向的,只能向下,不能回去

  • Iterator迭代器是一次性的,只能遍历一次。如果想再次遍历,就要重新获取一个新的迭代器对象

  • 通过迭代器遍历的时候,不能通过集合对象:添加、删除集合元素,可以修改

  • 如果是ArrayList,LinkedList,删除倒数第二个元素,不会抛出并发修改异常

  1. 如何解决使用迭代器操作集合长度:

  • 集合在遍历时,集合自身操作了集合长度后,即时跳出循环即可

  • 集合在遍历时,使用迭代器操作了集合长度后,就不会出错了

  • Collection的迭代器中只有remove方法,没有办法添加 如果是ArrayList的话,可以返回功能更强大的迭代器ListIterator,既可以添加又可以删除

代码示例

public class Demo {public static void main(String[] args) {//1.定义一个集合Collection<String> collection = new ArrayList<>();//2.添加元素collection.add("张三");collection.add("李四");collection.add("王五");collection.add("赵六");collection.add("张麻子");System.out.println(collection);//3.获取一个迭代器Iterator<String> iterator = collection.iterator();//使用迭代器 获取集合中每一个元+素while(iterator.hasNext()) {String thisCao = iterator.next();System.out.println(thisCao);}//如果想再次迭代集合,则需要重新获取一个该集合的迭代器对象}
}

7 集合的排序方法

Collections类中封装了一些静态的关于集合的方法

  1. 打乱集合顺序方法

使用Collections的shuffle方法,代码演示如下

public class DemoShuffle {public static void main(String[] args) {ArrayList<String> list = new ArrayList<String>();list.add("张麻子");list.add("黄四郎");list.add("汤师爷");list.add("胡万");list.add("花姐");System.out.println(list);Collections.shuffle(list);//第一次打乱顺序System.out.println(list);Collections.shuffle(list);//第二次打乱顺序System.out.println(list);//运行结果//[张麻子, 黄四郎, 汤师爷, 胡万, 花姐]--正常插入顺序//[汤师爷, 胡万, 花姐, 黄四郎, 张麻子]--第一次打顺序//[黄四郎, 胡万, 花姐, 汤师爷, 张麻子]--第二次打乱顺序}
}
  1. 普通类型排序方法

使用Collections的sort方法,代码演示如下

字符串类型排序时是通过把字符串类型的数据转化成Ascll码然后再进行排序

public class DemoSort {public static void main(String[] args) {//使用Collections的sort方法进行排序//创建一个存入整数的数据集合List<Integer> list = new ArrayList<>();list.add(11);list.add(1);list.add(2);list.add(23);list.add(443);list.add(543);list.add(987);list.add(4560);//排序之前System.out.println(list);//排序之后Collections.sort(list);System.out.println(list);//运行结果//[11, 1, 2, 23, 443, 543, 987, 4560]//[1, 2, 11, 23, 443, 543, 987, 4560]//对字符串进行排序ArrayList<String> list2 = new ArrayList<String>();list2.add("张麻子");list2.add("黄四郎");list2.add("汤师爷");list2.add("花姐");//排序之前System.out.println(list2);Collections.sort(list2);//排序之后System.out.println(list2);//运行结果// [张麻子, 黄四郎, 汤师爷, 花姐]//[张麻子, 汤师爷, 花姐, 黄四郎]}
}
  1. 比较器排序方法

一些普通的类型可以通过sort进行排序,但有些不可以,因为放入sort方法中的集合的泛型类型必须实现了Comparable接口,没有实现的就不能使用sort方法进行排序,针对于这种情况我们可以使用比较器进行排序

针对比较器排序有两种方式

  • 对于字定义的类,可以让它实现Comparable接口,然后实现接口中的compareTo方法,让字定义类具有自然比较规则,然后使用sort方法进行排序(代码演示如下)

/*** 让类具备自然比较性*/
public class Student implements Comparable<Student>{private String name;private int age;private double heigth;public Student() {}public Student(String name, int age, double heigth) {this.name = name;this.age = age;this.heigth = heigth;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public double getHeigth() {return heigth;}public void setHeigth(double heigth) {this.heigth = heigth;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", heigth=" + heigth +'}';}//需要重写compareTo方法,规定类型的比较规则//规则是随意的@Overridepublic int compareTo(Student o) {//按年龄比较大小//this//o
//        return this.age - o.age;return o.age - this.age;}
}
  • 对于已经实现Comparable接口,并重写了compareTo方法的普通类型,如Integer,String类型等,如果不想使用它已经有的排序规则,但又不能修改他现有的比较规则,就可以在排序的时候重新定义比较规则,这种方法不光适用于普通类型,也使适用于字定义实体类(演示代码如下)

public class Demo {public static void main(String[] args) {//1.定义一个存储整数的集合//排序:降序(从大到小)List<Integer> list = new ArrayList<>();Collections.addAll(list,34,6,7,8,2,345,7,8,0,65,4);//对集合元素排序Collections.sort(list, new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o2 - o1;}});System.out.println(list);//2.定义一个存储Student对象(没有实现Comparable接口)的集合List<Student> list3 = new ArrayList<>();list3.add(new Student("a张麻子",23,184.2));list3.add(new Student("c黄四郎",21,182.2));list3.add(new Student("d汤师爷",26,175.2));list3.add(new Student("b花姐",24,170.3));//对集合元素排序System.out.println(list3);//根据名称比较大小,从小到大排列Collections.sort(list3, new Comparator<Student>() {@Overridepublic int compare(Student o1, Student o2) {return o1.getName().compareTo(o2.getName()) ;}});System.out.println(list3);}
}

准备离职第1天:java集合复习整理相关推荐

  1. JSP Java 期末复习整理

    Java 期末复习 第一章 1.1.1 JSP 1.1.2 Jsp 优点 1.2.2 B/S技术工作原理 1.2.3 如何访问动态网页 1.2.4 -- Tomcat目录结构 1.2.6 -- Web ...

  2. java集合复习与巩固(一)

    HashMap这是一个老生常谈的问题,但是不经常看的话很容易忽略.特此记录,以增强记忆. 首先我们来说下数组,由于内存空间是连续的所以位置可以通过基本地址和偏移量计算出来,这也是为什么数组查询快的原因 ...

  3. java 考试复习整理——JAVA类和类成员的修饰符

    今天整理了一下以前学的JAVA的类和类成员的修饰符,考试的时候可能会考到. 一:访问修饰符: 1.省略访问修饰符: 具有默认的访问特性,即具有包访问特性,只能被同一个包中的类使用. 2.public访 ...

  4. Java多线程复习整理(二)

    目录 1. 线程的五种状态(操作系统层面)? 2.线程的状态(JAVA层面)? 3.. 线程相关的基本方法? 4. wait()和sleep()的区别? 5.sleep()方法和yield()方法区别 ...

  5. 基于《java2实用教程》的java知识点复习整理【第一章——java入门】

    第一章--java入门 一.知识结构框架 二.知识点详解 1.java特点:编写一次,随处运行 简单 Java要比C++简单,C++中许多容易混淆的概念,被Java弃之不用了,或者以一种更清楚更容易理 ...

  6. java复习系列[6] - Java集合

    Java集合 ArrayList的扩容 在add().addAll()方法中判断是否需要扩容 使用 grow() 函数以 1.5 倍的方式进行扩容 HashMap HashMap扩容流程(1.7 与 ...

  7. java基础复习-集合框架(1)

    java集合概述 Java 集合, 也叫作容器,主要是由两大接口派生而来:一个是 Collection接口,主要用于存放单一元素:另一个是 Map 接口,主要用于存放键值对.对于Collection ...

  8. java list有序还是无序_牛批!2w字的Java集合框架面试题精华集(2020最新版),赶紧收藏。...

    一个多月前,作者和一些小伙伴决定做一系列的 Java 知识点常见重要问题的小册,方便用来夯实基础!小册的标准就一个,那就是:取精华,取重点.每一本小册,我们都会充分关注我们所总结的知识点是否达到这个标 ...

  9. Java集合必会14问(精选面试题整理)

    前言:把这段时间复习的关于集合类的东西整理出来,特别是HashMap相关的一些东西,之前都没有很注意1.7 ->> 1.8的变化问题,但后来发现这其实变化挺大的,而且很多整理的面试资料都没 ...

最新文章

  1. 区别用户使用的终端设备代码 转 https://segmentfault.com/a/1190000012957023
  2. q7goodies事例_Java 8 Friday Goodies:轻松派本地缓存
  3. 鸿蒙开发者社区入口,鸿蒙OS 社区
  4. jQuery插件Label Effect制作个性化的文字特效
  5. WARNING:Result from SERVER not valid. Partial Result:
  6. 任务47:Identity MVC:ReturnUrl实现
  7. 一起谈.NET技术,在ASP.NET MVC3 中利用JSONP跨域登录WEB系统
  8. oracle 物料属性批次过期,物料批次特性值
  9. 关于webapp的一点思考
  10. 寻找固定的和----2013年2月26日
  11. opencv图片序列转换成视频
  12. 计算机应用和轨道交通哪个好,轻轨学校对重庆和男生有什么好处
  13. 通信加密原理(对称密钥、公钥、私钥)
  14. 贴片元器件焊接经验及总结
  15. USACO 2021 December Contest, Bronze
  16. eclipse官网下载安装教程
  17. 计算机应用软件用着总是闪退是什么原因
  18. matplotlib绘制双坐标轴(双纵轴)
  19. 一个屌丝程序员的青春(八六)
  20. “易语言.飞扬”十分钟入门教程

热门文章

  1. 关于JavaScript面向对象
  2. 全球7亿多电邮账号信息泄露,你数据还安全吗?
  3. luogu P3899 [湖南集训]谈笑风生 线段树合并
  4. 阿里真的开始衰落了吗?
  5. mysql 按首字母进行检索数据
  6. Android集成环信IM,实现为某一个好友设置消息免打扰
  7. 山水印|竹林野茶:它,被称为茶叶中瑰宝,不止抗癌那么简单
  8. 新《葫芦兄弟》被批毁童年,如果这样拍必然好看一百倍!
  9. 数学:定积分和数列和互相转化
  10. 理解COM的线程套件(转)