在Java中集合分为单列集合(Collection)与双列集合(Map),本文主要对两种集合的常用实现类作一介绍,如果有不足,恳请指教更正。

1.前言

说到集合肯定要介绍下集合的概念与特点:集合是一个容器,能够用来存储引用数据类型,长度是可变的。说到存储数据的容器大家脑海里想到的肯定还有数组,数组也是一个容器,可以用来存储任意类型的数据,但是长度是不可变的。因此数组有很大的局限性,在开发中存储数据我们一般都使用集合。

                                (图是拷贝的)

2.Collection单列集合

Collection是单列集合的顶层父接口,其下分为List集合与Set集合两大类。List集合与Set集合各有其特点:

List集合:1.元素存取有序   2.有索引  3.元素可以重复

Set集合:1.元素存取无序   2.无索引  3.元素不能重复

(元素存取有序是指按什么顺序存的就按什么顺序取出)

2.1List集合

List集合下常用的实现类有ArrayList、LinkedList、Vector。

2.1.1ArrayList

ArrayList集合在我们平时开发中使用的频率最高,其底层其实是Object数组,所以查询快,增删慢。为什么这么说呢?因为在数组结构中数组在内存中是连续的,数组的地址值其实是连续空间的第一个空间的地址,由于连续性,数组就可以通过直接计算出对应空间的地址值从而实现快速查询,但是数组长度不可变,如果我们要在数组中进行增删操作,需要创建一个新的数组,还要把原数组中的元素复制过去,这就导致了增删慢。

2.1.2LinkedList

LinkedList集合的底层是一个双向链表,所以查询慢,增删快。在这里要说明一下链表结构,链表结构其实是有多个节点连接起来,节点最少由数据部分、地址部分2部分组成。数据部分:存数据。地址部分:存下一个节点的地址。多个节点之间通过地址进行连接。例如:多个人手拉手,每个人使用自己的右手拉住下一个人的左手,一次类推,这样多个人就在一起了。这种数据接口查找慢的原因是:链表中的数据是离散的,没有任何规律,必须从第一个节点开始一个一个向后查询,而增删快的原因是:节点它可以记录下一个节点的地址值,链表中增删数据时,不用修改原本数据的内存地址值,增加元素只需要修改连接下个元素的地址即可,同理,删除元素也只需要修改下个元素的地址即可。

2.1.3Vector

Vector集合底层是扩展的对象数组,功能与ArrayList集合相似,但是Vector是线程安全的,而ArrayList是线程不安全的,线程安全带来的弊端就是效率比较低,所以Vector集合一般很少使用。

2.2单列集合的遍历

集合数据的添加可以通过集合中的add方法实现,那么要怎么获取集合中的元素呢?

集合中元素的获取可以通过一般for循环、增强(高级)for循环、迭代器实现,这里主要说一下高级for循环和迭代器。

高级for循环也叫foreach循环,是从JDK1.5之后出现的,专门用来遍历数组和集合。其使用格式为:for(数据类型 变量名 :容器){ 循环体  }。注意:数组的增强for循环底层是普通for循环;集合的增强for循环底层原理是迭代器。

 ArrayList<String> list = new ArrayList<>();
list.add("小明");
list.add("小红");
list.add("小黑");
list.add("小白");
for ( String s : list ) {
System.out.println(s);
}
控制台打印:

小明
小红
小黑
小白

 

迭代器(Iterator)可以简单的看作是一个具有光标(指针)的遍历集合的工具。是Collection集合元素的通用获取方式。一开始指向集合元素的第一个位置的值,在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来,一直把集合中的所有元素全部取出,这种取出方式专业术语称为迭代。有人可能会问有了这么简单的foreach循环为什么还要使用迭代器?

foreach循环遍历与迭代器遍历比较:

如果对集合中的元素随机访问,foreach循环中的get()方法采用的就是随机访问的形式,因此在ArrayList集合中,foreach遍历较快。

如果对集合中的元素顺序访问,迭代器中的next()方法,采用的就是顺序访问的形式,因此在LinkedList集合中,使用迭代器较快。

从数据结构角度分析,for循环适合访问顺序结构,可以根据下标快速获取指定元素.而Iterator 适合访问链式结构,因为迭代器是通过next()和Pre()来定位的.可以访问没有顺序的集合.

foreach只是让代码更加简洁了,但是他有一些缺点,就是遍历过程中不能操作数据集合(删除等),所以有些场合不使用,而迭代器可以在遍历过程中操作集合,但值得注意的是必须用迭代器对象操作集合,而不是集合对象操作集合!比如你要遍历一个集合,当满足某条件时候,删除一个元素,  如果用的是foreach循环,就用集合自带的remove(),而这样就改变了集合的Size()循环的时候会出错但如果把集合放入迭代器,既iterator迭代可以遍历并选择集合中的每个对象而不改变集合的结构,而把集合放入迭代器,用迭代器的remove()就不会出现问题。

迭代器遍历过程:

1.有一个单列集合对象

2.调用集合对象的iterator获取迭代器

3.调用迭代器的hasNext方法判断是否还有数据

4.如果有调用迭代器的next方法来获取

ArrayList<String> list = new ArrayList<>();
list.add("小明");
list.add("小红");
list.add("小黑");
list.add("小白");
Iterator<String> it = list.iterator();//获取迭代器对象
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
} LinkedList<Integer> linkedList = new LinkedList<>();
linkedList.add(1);
linkedList.add(2);
linkedList.add(3);
linkedList.add(4);
Iterator<Integer> iterator = linkedList.iterator();
while (iterator.hasNext()) {
Integer next = iterator.next();
System.out.println(next);
}
控制台输出:

小明
小红
小黑
小白
1
2
3
4

2.2 Set集合

Set集合下常用的实现类有HashSet、LinkedHashSet、TreeSet。

2.2.1 HashSet

HashSet集合的底层是哈希表,哈希表在JDK1.8之后新增了红黑树,即哈希表=数组+链表+红黑树。当向哈希表中存储数据时,先判断哈希值(hashcode),再判断内容(equals),这也是哈希表的去重方式。哈希表的存储方式是数组存放的是哈希值不同的元素,链表是用来存放哈希值相同但内容不同的元素,当链表的长度超过8时会自动变成红黑树,提高效率。  在向HashSet集合中存储自定义对象时,一定要重写hashcode()和equals()方法,以实现对自定义对象的去重。

(哈希值:是一个十进制的int值,它表示一个对象的特征码(可能相同),重写后是以对象的属性值按照一定的计算方式获取的,所以如果属性内容相同,2个不同地址值的对象的哈希值有可能一样。)

扩展:在哈希表中地址不同的2个对象,内容相同,哈希值是可能一样的;地址不同,内容也不同,哈希值也有可能一样。

public class Person {
private String name;
private int age; @Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
  
  //重写equals方法:对对象的内容进行比较判断
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}

  //重写hashCode方法:对对象的属性值进行运算,
//底层算法:result = 31 * result + (element == null ? 0 : element.hashCode()); @Override
public int hashCode() { return Objects.hash(name, age); } 

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 Person(String name, int age) { this.name = name; this.age = age; } 

public Person() { } }

2.2.2 LinkedHashSet

LinkedHashSet集合的底层是双向链表+哈希表,而链表是用来保存顺序的,所以LinkedHashSet就实现了存取有序了,是Set集合中唯一一个能保证怎么存怎么取的集合。LinkedHashSet集合同样有去重的作用,不能存储重复数据,其功能除了有序外,与HashSet集合差不多。

2.2.3 TreeSet

TreeSet底层是二叉树结构, 实现了SortedSet接口 ,能够对集合中对象进行排序(升序),同样具有去重,不重复的特点。

针对自定义对象的排序可以分为两种方式:

方式1:实现Comparable接口,实现compareTo方法,这种方式也称为元素的自然排序。口诀:我减它>0是升序

public class Student implements Comparable<Student> {
private String name;
private int age;
private int score; @Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
} 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 int getScore() {
return score;
} public void setScore(int score) {
this.score = score;
} public Student() { } public Student(String name, int age, int score) { this.name = name;
this.age = age;
this.score = score;
} @Override
public int compareTo(Student o) {
//分数做降序
if (this.score > o.score) {
return -1;
} else if (this.score < o.score) {
return 1;
} else {
//年龄升序
if (this.age > o.age) {
return 1;
} else if (this.age < o.age) {
return -1;
} else {
return 0;
}
} }
}

方式2:Comparator比较器,这种方式常用于匿名内部类,不用修改源码,所以比较常用。口诀:我减它>0是升序

(使用比较器就不用实现Comparable接口了)

//分别用Comparable和Comparator两个接口对下列四位同学的成绩做降序排序,
// 如果成绩一样,那在成绩排序的基础上按照年龄由小到大排序
public class Test13 {
public static void main(String[] args) {
Student liusan = new Student("liusan", 20, 90);
Student lisi = new Student("lisi", 22, 90);
Student wangwu = new Student("wangwu", 20, 99);
Student sunliu = new Student("sunliu", 22, 100); comparable(liusan, lisi, wangwu, sunliu);
comparator(liusan, lisi, wangwu, sunliu);
} public static void comparator(Student liusan, Student lisi, Student wangwu, Student sunliu) {
ArrayList<Student> list = new ArrayList<>();
list.add(liusan);
list.add(lisi);
list.add(wangwu);
list.add(sunliu);
//比较器
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
if (o1.getScore() > o2.getScore()) {
return -1;
} else if (o1.getScore() < o2.getScore()) {
return 1;
} else {
if (o1.getAge() > o2.getAge()) {
return 1;
} else if (o1.getAge() < o2.getAge()) {
return -1;
} else {
return 0;
}
}
}
});
for ( Student student : list ) {
System.out.println("student = " + student);
}
} public static void comparable(Student liusan, Student lisi, Student wangwu, Student sunliu) {
ArrayList<Student> list = new ArrayList<>();
list.add(liusan);
list.add(lisi);
list.add(wangwu);
list.add(sunliu); //Student类中实现了comparable接口并且重写了compareTo方法,Collections.sort()方法会调用重写的compareTo方法
Collections.sort(list);
for ( Student student : list ) {
System.out.println("student = " + student);
}
}
}

3 Map集合

Map 集合是双列集合的顶层父接口,该集合中每个元素都是键值对<key,value>,成对出现。双列集合中,一个键一定只找到对应的一个值,键不能重复,但是值可以重复。

Map集合常用的实现类有HashMap、LinkedHashMap、HashTable,TreeMap。

3.1 HashMap

HashMap的底层是哈希表,与HashSet相似,只是数据的存储形式不同,HashMap可以使用null作为key或value,是线程不安全的,但是效率相对较高。当给HashMap中存放自定义对象时,如果自定义对象作为key存在,这时要保证对象唯一,必须复写对象的hashCode和equals方法。

3.2 HashTable

HashTable与HashMap之间的关系完全类似于Vector和Arraylist的关系。HashTable是线程安全的,但是效率相对较低,Hashtable不允许使用null作为key和value。

3.3 LinkedHashMap

LinkedHashMap是HashMap的子类,其底层是链表+哈希表结构,其关系与HashSet和LinkedHashSet类似,使用链表来维护key-value的次序,可以记住键值对的插入顺序。

3.4 TreeMap

TreeMap存储key-value键值对时,需要根据key对节点进行排序。TreeMap可以保证所有的key-value对处于有序状态。也有两种排序方式:

1)  自然排序:TreeMap的所有key必须实现Comparable接口,而且所有的key应该是同一个类的对象,否则抛出ClassCastException异常。

2)  定制排序:创建TreeMap时,传入一个Comparator对象,该对象负责对TreeMap中的所有key进行排序。不需要Map的key实现Comparable接口。

Map集合的遍历:

map集合采用put(key,value)的形式保存键值对数据,对于双列集合Map中键值对形式的数据如何遍历呢?

Map接口是没有Iterator方法,所以不能直接获取迭代器进行遍历;双列集合没有实现Iterable接口,因此也不能直接通过增强for(foreach)循环遍历。

因此我们只能间接的对Map集合进行遍历,方式有2种。

第一种:键找值方式

   1.调用keySet()方法获取到双列集合的所有key,存放到set单列集合中

   2.迭代器遍历或者增强for循环遍历单列set集合中的key,通过双列集合的get(key)方法获取value

/*
* 有2个数组,第一个数组内容为:[黑龙江省,浙江省,江西省,广东省,福建省],第二个数组为:[哈尔滨,杭州,南昌,广州,福州],
* 将第一个数组元素作为key,第二个数组元素作为value存储到Map集合中。如{黑龙江省=哈尔滨, 浙江省=杭州, …}
* */
public class Test07 {
public static void main(String[] args) {
String[] str1 = {"黑龙江省", "浙江省", "江西省", "广东省", "福建省"};
String[] str2 = {"哈尔滨", "杭州", "南昌", "广州", "福州"};
Map<String, String> map = new HashMap<>();
for ( int i = 0; i < str1.length; i++ ) {
map.put(str1[i], str2[i]);
} Set<String> keySet = map.keySet();
for ( String key : keySet ) {
String value = map.get(key);
System.out.println(key + "=" + value);
}
}

第二种:键值对方式

  1.调用entrySet()方法获取到双列集合的所有键值对对象,存放到set集合中
  2.迭代器遍历或者增强for循环遍历单列set集合获取到键值对对象,通过getKey()和getValue()方法获得键和值。

/*
* 有2个数组,第一个数组内容为:[黑龙江省,浙江省,江西省,广东省,福建省],第二个数组为:[哈尔滨,杭州,南昌,广州,福州],
* 将第一个数组元素作为key,第二个数组元素作为value存储到Map集合中。如{黑龙江省=哈尔滨, 浙江省=杭州, …}
* */
public class Test07 {
public static void main(String[] args) {
String[] str1 = {"黑龙江省", "浙江省", "江西省", "广东省", "福建省"};
String[] str2 = {"哈尔滨", "杭州", "南昌", "广州", "福州"};
Map<String, String> map = new HashMap<>();
for ( int i = 0; i < str1.length; i++ ) {
map.put(str1[i],str2[i]);
} Set<Map.Entry<String, String>> entrySet = map.entrySet();
Iterator<Map.Entry<String, String>> iterator = entrySet.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}

Collection集合与Map集合的知识点梳理的更多相关文章

  1. 红黑树规则,TreeSet原理,HashSet特点,什么是哈希值,HashSet底层原理,Map集合特点,Map集合遍历方法

    ==学习目标== 1.能够了解红黑树 2.能够掌握HashSet集合的特点以及使用(特点以及使用,哈希表数据结构) 3.能够掌握Map集合的特点以及使用(特点,常见方法,Map集合的遍历) 4.能够掌 ...

  2. Java常用的几种集合, Map集合,Set集合,List集合

    Java中  Object是所有类的根 Java集合常用的集合List集合.Set集合.Map集合 Map接口常用的一些方法 size() 获取集合中名值对的数量 put(key k, value v ...

  3. java学习笔记—集合之Map集合

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; text-align: center; font: 12.0px Times } p.p2 { margin: 0.0p ...

  4. java集合(List集合与Map集合的数据转换)

    List集合与Map集合的数据转换 实现List和Map数据的转换. 具体要求如下: 功能1:定义方法public void listToMap( ){ }将List中Student元素封装到Map中 ...

  5. collection(list,set,map)集合详解

    一:java集合的体系结构如下: Java集合大致分为Set.List.Queue.Map四个体系 .Collection: List和Set,Queue继承自Collection接口. |--Lis ...

  6. java集合 之 Map集合

    Map用于保存具有映射关系的数据,具有两组值:一组用于保存Map中的key:另一组用于保存Map中的value,形成key-value的存储形式. Map集合中包含的一些方法: void clear( ...

  7. Java基础知识强化之集合框架笔记51:Map集合之Map集合的功能概述与测试

    1. Map集合的功能概述 (1)添加功能 V put(K key,V value):添加元素.这个其实还有另一个功能?先不告诉你,等会讲 如果键是第一次存储,就直接存储元素,返回null 如果键不是 ...

  8. Java基础知识强化之集合框架笔记50:Map集合之Map集合的概述和特点

    1. Map集合的概述: public interface Map<K,V> 作为学生来说,是根据学号来区分不同的学生的,那么假设我现在已经知道了学生的学号,我要根据学号去获取学生姓名,请 ...

  9. 集合框架-Map集合

    * Map集合和Collection集合的区别? * Map集合存储元素是成对出现的,Map集合的键是唯一的,值是可重复的.可以把这个理解为:夫妻对 * Collection集合存储元素是单独出现的, ...

随机推荐

  1. javascript基本特点,组成和应用

    JavaScript 是一种基于客户端浏览器.面向(基于)对象和事件驱动式的网页脚本语言. 1. 基于客户端浏览器:静态语言,跨平台: 2. 面向(基于)对象:本身是没有类class和对象这个概念,但 ...

  2. TreeSet类的排序

    TreeSet支持两种排序方法:自然排序和定制排序.TreeSet默认采用自然排序. 1.自然排序 TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间大小关系, ...

  3. 洛谷 P3811 题解

    题面 利用暴力快速幂O(nlogn)会TLE掉: 所以对于求1~n的所有逆元要用递推公式: #include <bits/stdc++.h> using namespace std; ]; ...

  4. js中数组和对象的合并

    1 数组合并 1.1 concat 方法 1 2 3 4 var a=[1,2,3],b=[4,5,6]; var c=a.concat(b); console.log(c);// 1,2,3,4,5 ...

  5. Tomcat+MySQL常见调优参数

    一.Tomcat 调优 (一).Tomcat内存优化 参数一: vim /tomcat/bin/catalina.sh CATALINA_OPTS="-server -Xms128m -Xm ...

  6. JavaScript数据结构——链表的实现与应用

    链表用来存储有序的元素集合,与数组不同,链表中的元素并非保存在连续的存储空间内,每个元素由一个存储元素本身的节点和一个指向下一个元素的指针构成.当要移动或删除元素时,只需要修改相应元素上的指针就可以了 ...

  7. 后端小白的VUE入门笔记, 进阶篇

    使用 vue-cli( 脚手架) 搭建项目 基于vue-cli 创建一个模板项目 通过 npm root -g 可以查看vue全局安装目录,进而知道自己有没有安装vue-cli 如果没有安装的话,使用 ...

  8. react学习(二)--元素渲染

    元素用来描述你在屏幕上看到的内容: const element = <h1>Hello, world</h1>; 与浏览器的 DOM 元素不同,React 当中的元素事实上是普 ...

  9. Powered by .NET Core 进展:第5次发布尝试(Windows部署)

    (图注:Windows 自带的性能监控,红色表示 CPU 占用,绿色表示 QPS) 今天中午 12:30 左右,我们进行了 .NET Core 博客站点的第5次发布(页脚会显示"Powere ...

  10. Linux:oracle11.2.0dbca静默建库

    1.关闭防火墙 systemctl stop firewalld.service #停止firewall systemctl disable firewalld.service #禁止firewall ...