本博客java云同桌学习系列,旨在记录本人学习java的过程,并与大家分享,对于想学习java的同学,我希望这个系列能够鼓励大家一同与我学习java,成为“云同桌”。

每月预计保持更新数量三章起,每章都会从整体框架入手,介绍章节所涉及的重要知识点及相关练习题,并会设置推荐学习时间,每篇博客涉及到的点都会在开篇目录进行总览。(博客中所有高亮部分表示是面试题进阶考点)

集合

  • 1.常见数据结构简介
    • (1)栈(stack)
    • (2)队列(queue)
    • (3)链表(linked list)
    • (4)二叉树(binary tree)
  • 2.Collection接口及其分支
    • java.util.Collection(interface)
    • java.util.List(interface)
    • java.util.ArrayList
    • java.util.Vector
    • java.util.LinkedList
    • java.util.Iterator(Interface)
  • 3.forEach循环语句
  • 4.Set接口相关类
    • java.util.Set(interface)
    • java.util.HashSet
    • java.util.TreeSet
  • 5.哈希表概述
  • 6.Map接口相关类
    • java.util.Map(interface)
    • java.util.HashMap
    • HashMap线程不安全原因
    • 集合 list 转 Map
  • 7.集合与数组的转化
    • 数组转集合
    • 集合转数组

学习时间:一周
学习建议:集合相当于数组的进阶版本,未来可能会频繁使用集合开发,建议弄清楚各集合类之间的区别和其特点,不同问题采用不同集合解决

1.常见数据结构简介

(1)栈(stack)

栈又称堆栈,栈限定只能在线性表表尾进行插入和删除的操作,这一端允许插入和删除称为栈顶,另一端称为栈底,不含任何数据元素的称为空栈。

特点:

(1)先进先出
(2)栈的入口和出口在一端,都位于栈顶

图解:

专业术语:
压栈:存元素
弹栈:取元素

(2)队列(queue)

队列,简称队,只允许在队尾(rear)进行插入,只允许在队头(front)进行元素删除的操作。空队列是不含任何元素的空表

特点:

(1)先进先出
(2)队列的出口在一侧,入口在另一侧

图解:

(3)链表(linked list)

以单向链表为例,链表是有一系列(包含存储数据元素的数据域与下一个结点地址的指针域)结点组成,结点数量可以动态生成

图解:

特点:

(1)内存地址不连续
(2)长度可以动态增加
(3)查找元素较慢,增删元素较快

结点结构:

private class Node{private T t;private Node next;
}

链表还有一种是双向链表,即可以从某个节点找到它的前后两个节点。

手写数据结构如下:

//双链表节点
class Node{int value;Node left;Node right;public Node(int value) {this.value = value;left = null;right = null;}
}//双链表列表结构
class LinkList{int size;Node head;Node tail;public LinkList() {this.size = 0;this.head = null;this.tail = null;}public void add(int value){Node newNode = new Node(value);if(head == null){head = newNode;}else{tail.right = newNode;newNode.left = tail;}size ++;tail = newNode;}
}

(4)二叉树(binary tree)

通常java中常用的二叉树指,每个结点不超过2的有序树。即如何一个数比根结点数据更下,则存放在根结点的左子树,如果一个数比根结点数据更大,则存放在根结点的右子树,结点与结点之间依靠父与左右子的关系建立。

有序二叉树结点结构

public class Node {private int data;private BNode leftNode;private BNode rightNode;
}

2.Collection接口及其分支

Java中为了方便用户操作各个数据结构,引入了类集的概念,所有的类集的操作的接口或类都在java.util包中。

java.util.Collection(interface)

介绍:Collection 接口是在整个 Java 类集中保存单值的最大操作父接口,里面每次操作的时候都只能保存一个对象的数据。在开发中,通常不会直接使用Collection接口,而使用其操作的子接口,List(允许重复),Set(不允许重复)

常用方法:

  • add(E e), 向集合中添加元素
  • size(),返回此集合中的元素数量
  • iterator(),返回集合中的元素迭代器
  • toArray(),返回包含此集合中所有元素的一个数组。

java.util.List(interface)

介绍:java.util.List 接口继承自 Collection

接口特点:

  List集合中允许出现重复元素可以通过索引访问集合中任一个元素所有元素以线性方式存储,元素的存入顺序和取出顺序一致

接口常用的实现类: ArrayList(95%)、Vector(4%)、LinkedList(1%)

常用方法(不止继承了Collection接口的全部方法,还实现或建立了一些方法):

  • public void add(int index, E element) : 将指定的元素,添加到该集合中的指定位置上。
  • public E get(int index) :返回集合中指定位置的元素。
  • public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。
  • public E set(int index, E element) :用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
  • public ListIterator listIterator() :返回 ListIterator 接口的实例

java.util.ArrayList

介绍:ArrayList是List接口的实现类,使用最为广泛

特点:

 线程不安全数组结构,增加删除慢,查找快,可动态调整大小(初始默认为10,每次扩容为原来的1.5倍)

构造方法:

  • ArrayList(),构造一个初始容量为10的空列表。
  • ArrayList(int initialCapacity),构造具有initialCapacity大小初始容量的空列表。

常用方法(无特别方法,常用方法均继承于List接口)

使用举例:

ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(100);arrayList.add(200);
System.out.println(arrayList.get(1));//200
System.out.println(arrayList);//[100, 200]

java.util.Vector

介绍:大多数与ArrayList类使用的相同,它是为了解决线程不安全的问题,Vector是同步的, 如果不需要线程安全实现,建议使用ArrayList代替Vector

特点:

线程安全
数组结构,增加删除慢,查找快,可动态调整大小(初始默认为10,每次扩容倍数可指定)

构造方法:

  • Vector(),构造一个空向量,使其内部数据数组的大小为 10 ,其标准容量增量为零。
  • Vector(int initialCapacity),构造一个具有指定初始容量且容量增量等于零的空向量。
  • Vector(int initialCapacity, int capacityIncrement),构造具有指定初始容量和容量增量的空向量。

常用方法(无特别方法,常用方法均继承于List接口)

java.util.LinkedList

介绍:List接口的实现类之一,双链表结构,可以使用其中的一些方法实现队列和栈数据结构

特点:

双链表结构,增删快,查找慢
线程不安全

常用方法:(继承了一些List接口的方法,还有一部分新的方法)

  • add(E e),将指定的元素追加到此列表的末尾。
  • addFirst(E e),在此列表的开头插入指定的元素。
  • addLast(E e),将指定的元素追加到此列表的末尾。
  • getFirst(),返回此列表中的第一个元素。
  • getLast(),返回此列表中的最后一个元素。
  • removeFirst(),从此列表中删除并返回第一个元素。
  • removeLast(),从此列表中删除并返回最后一个元素。

可通过上述的方法,实现栈数据结构(笨比版)

//栈数据结构,先进后出
LinkedList<Integer> linkedList = new LinkedList<>();
linkedList.addFirst(1);
linkedList.addFirst(2);
System.out.println(linkedList.removeFirst());//2
System.out.println(linkedList.removeFirst());//1

实现队列数据结构

//队列数据结构,先进先出
LinkedList<Integer> linkedList = new LinkedList<>();
linkedList.addFirst(1);
linkedList.addFirst(2);
System.out.println(linkedList.removeLast());//1
System.out.println(linkedList.removeLast());//2
  • push(E e),将元素推送到此列表所表示的堆栈上。
  • pop(),弹出此列表所代表的堆栈中的元素。

类中提供了专门模拟堆栈的方法(高端版)

//栈数据结构,先进后出
LinkedList<Integer> linkedList = new LinkedList<>();
linkedList.push(1);
linkedList.push(2);
System.out.println(linkedList.pop());//2
System.out.println(linkedList.pop());//1

java.util.Iterator(Interface)

介绍:通常称为迭代器,可以对List集合和set集合进行遍历,从中取出数据,遍历过程可以理解为游标的概念,初始迭代器指针指向0下标的上一位,迭代器指针下移后,指到哪一位代表操作哪一位

特点:

通过集合对象继承下来的iterator()方法获取iterator对象
Iterator iterator = arrayList.iterator();

常用方法:

  • hasNext(),判断迭代器指针下一位有没有数据元素,根据结果返回boolean类型数
  • next(),迭代器指针向下移一位,并返回所指数据(注意指针初始在0下标上方,移动一次后才会指向0下标)

常搭配使用,指针向下移动遍历输出集合元素

ArrayList arrayList = new ArrayList();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);Iterator iterator = arrayList.iterator();
while(iterator.hasNext()){System.out.println(iterator.next());
}
/*输出:
1
2
3
*/
  • add(E e),在迭代器指针所指位置,插入数据
  • remove(),在迭代器指针所指位置,删除元素,并返回所删除的元素。
  • set(E e),在迭代器指针所指位置,修改数据

3.forEach循环语句

forEach又称为增强for循环,用于迭代数组或集合(Collection下的集合)

语法:

for(数据类型 存储数据元素的变量:集合或者数组名){循环体
}

对于集合,原理相当于为集合创建了一个迭代器,然后遍历,后续知识点中将会有实例

4.Set接口相关类

java.util.Set(interface)

介绍:Set接口也是Collection接口下的一个主要接口,是存储无重复元素的集合

常用方法:

iterator(),返回一个set中元素的迭代器

相比于List接口,Set接口并没有自己写一个用于取出数据的get()方法,因此,对于取出数据,只能通过获取迭代器,然后迭代器移动到指定位置后取出数据

此接口中有两个常用的子类:HashSet、TreeSet

java.util.HashSet

介绍:该类是Set接口的常用实现类之一,存储元素时会检测是否是重复元素,根据返回值决定是否存入数据,采用散列存储结构(哈希表)

特点:

1.散列存储结构
2.数据元素无序
3.无重复数据元素(重复则存不进去)

因为没有Set接口中没有get()方法,取数据时,可以获取迭代器遍历,或者直接使用forEach循环结构进行取出数据

java.util.TreeSet

介绍:该类是Set接口的常用实现类之一,采用有序二叉树的数据结构

特点:

 * 有序二叉树的存储结构* 数据元素有序(自然顺序,对于自己编写的类对象等进行排序会出错,需要重写)* 迭代器是快速失败的(指在原集合上迭代,集合元素改变时迭代器会报异常,没有特别说别的话,其他集合都是安全失败迭代器,即另外复制一份用于迭代)* 无重复数据元素(重复则存不进去)* 线程不同步,不安全

对于自然有序的数据,常使用以下代码进行输出

TreeSet<Character> treeSet = new TreeSet<Character>();
treeSet.add('c');
treeSet.add('b');
treeSet.add('a');
//forEach循环结构
for(char i:treeSet){System.out.println(i);
}
/*
输出:
a
b
b
*/

对于非自然有序的数据,即要存入自己定义的类对象,系统不知道到那个对象是较大的,会报错,此时就需要我们来制定比较规则;

通过将待比较的类实现Compparable接口,重载实现CompareTo抽象方法描述比较规则,返回具备大小的一系列int返回值

   TreeSet<Person> treeSet = new TreeSet<Person>();Person p1 = new Person(1);Person p2 = new Person(2);Person p3 = new Person(3);treeSet.add(p1);treeSet.add(p2);treeSet.add(p3);for(Person p:treeSet){System.out.println(p);}//Person类
public static class Person implements Comparable<Person>{private int age;//重载比较规则方法@Overridepublic int compareTo(Person o) {//将this对象属性与o对象属性进行比较if(this.age > o.age){return 2;}else if(this.age == o.age){return 1;}else{return 0;}}@Overridepublic String toString() {return "Person{" +"age=" + age +'}';}public Person(int age) {this.age = age;}
}

5.哈希表概述

结构:可以简单理解成“数组+链表”的结构,它根据关键码值(key value)映射到数组中的一个位置,对于位置冲突的情况,在此建立链表,尾插入新数据

对于上图结构,某个数组位置及其链表,专业术语称之为“哈希桶”,当哈希桶中数据过多,就会降低表的性能,不便于查找

因此,如果哈希桶>8,哈希桶内的链表就会转化成红黑树(一种平衡二叉查找树),然后当哈希桶减少到6时,红黑树又转变为链表

初始属性:初始哈希表长度:16,散列因子:0.75

散列因子:以0.75为例,当哈希表长度的75%已经被占用,则进行扩容为原长度的2倍,散列因子官方建议为0.75效率最高,散列因子过小,会造成空间浪费,过大,会导致检索速度变慢

如果数据较多,只设置长度为初始长度,需要多次扩容,造成内存的大量占用,建议数据较多时指定一个合适的初始长度

注意:非必要情况,不要修改已经存入哈希表内的键,否则容易造成数据错乱

6.Map接口相关类

java.util.Map(interface)

介绍:Map其实是mapping(映射)的缩写,相比于Collection接口存储单值,Map以一个个键值对的形式存储多个值,每个键名互不重复

特点:

 * 键值对形式的多值存储* 键值对的键名互不重复* 存储不连续,无法像集合一样直接遍历

常用方法:

  • clear(),清空此映射中的所有键值对映射
  • get(Object key),通过键名获取返回键名指定的数据
  • remove(Object key),通过键名删除所对应的数据,并返回该被删除的数据
  • keySet(),解决遍历问题,以set集合的形式返回此映射的所有键名
  • put(K key, V value),将一个键值对关系存入此映射中,如果此键名在此映射中已有对应的数据,则返回旧数据,否则返回null
  • containsKey(Object key),如果此映射包含指定键的映射,则返回 true 。
  • containsValue(Object value),如果此映射将一个或多个键映射到指定值,则返回 true
  • size(),获取此映射中键值对的数量

java.util.HashMap

介绍:基于哈希表的Map接口的实现,可以构造哈希表

特点:

线程不同步 ,允许键值为null

构造方法:

  • HashMap(),构造一个空HashMap,使用默认初始容量(16)和默认加载因子(0.75)
  • HashMap(int initialCapacity),构造一个空HashMap,使用初始容量(initialCapacity)和默认加载因子(0.75),建议对于大量数据存储,给定一个合适的初始容量,可以降低扩容次数,减少内存消耗

哈希表的存储原理方式详解:

1.调用HashMap的put(K key,V value),传入要存储的键值对数据
2.然后判断此哈希表是否为空的表,如果空则扩容,不空进行下一步
3.获取key值的哈希值(null为0)
4.然后哈希值对哈希表长度取余,得到的即要存储的哈希桶的位置,如果该位置没有数据,则直接插入,如果有数据,下一步
5.判断这个桶中的key是否与新加入的key相等,如果相等,则直接修改value,如果不相等,说明是新键值对,下一步
6.然后此时桶中的数据结构是否是红黑树结构,是则直接插入,不是红黑树则是链表,遍历链表插入,插入后判断链表长度是否>8,大于8则转变成红黑树
7.最后数据插入完毕后,判断哈希表长度决定是否扩容

哈希表相关类的区别:

  • HashMap:线程不安全,效率高
  • HashTable:线程安全,效率低
  • ConcurrentHashMap:采用分段锁机制(即将正在操作的哈希桶范围锁住,不允许同步操作,将未操作的剩余哈希桶范围放开,允许一个新线程进来操作),效率相对较高
  • TreeMap:有顺序(从小到大)二叉树结构存储键值
  • LinkedHashMap:将数据同时存入一个双向链表和HashMap,可以保存存储的顺序

哈希表类常用的存取代码:

HashMap<Integer,String> hashMap = new HashMap<Integer, String>();
hashMap.put(1,"这是第一条");
hashMap.put(2,"这是第二条");
hashMap.put(3,"这是第三条");
//获取单个的数据
System.out.println(hashMap.get(2));//这是第二条/*获取所有的键和数据(方法1):通过key转换的set集挨个单独获取
*///先将所有数据的键值转换成set集
Set<Integer> integers = hashMap.keySet();
//对set集进行迭代器遍历输出键值
for(int key:integers){System.out.print(key+ ",");//1,2,3System.out.println();
}
//对set集进行迭代器遍历输出键值所对应的数据
for(int key:integers){System.out.print(hashMap.get(key)+ ",");//这是第一条,这是第二条,这是第三条System.out.println();
}/*获取所有的键和数据(方法2):通过value转换的collection集直接输出
*/
Collection<String> values = hashMap.values();
for(String value:values){System.out.print(value+ ",");//这是第一条,这是第二条,这是第三条System.out.println();
}

HashMap线程不安全原因

上文说到,HashMap在达到默认加载因子0.75后,会进行扩容,而扩容,又是一次对所有HashKey重新取余哈希桶数量的过程,被称为Rehash,此操作很耗费性能,所以一般情况下建议减少自动扩容的次数。

那么在对所有数据重新分配的过程中,如果多线程进入,同时发现需要自动扩容,然后进行分配,那么在重新分配,给哈希碰撞的数据建立链表关系时,便很容易产生环形链表,那么只要后续有针对存在环形链表的哈希桶的查询,那么便发生了死循环,导致CPU占用爆表。

故如果需要线程安全的HashMap结果,更推荐使用HashTable、ConcurrentHashMap类

更详细的原因推荐阅读:JAVA HASHMAP的死循环

集合 list 转 Map

由于Map集合有一个Containkey()方法在查询某些数据时效率较高,所以平常可能需要将List转为Map然后利用该方法进行查询

如下代码所示,tomap的第一个参数返回值就是以后Map集合的key,tomap第二个参数的返回值就是以后Map集合的Value

public static void main(String[] args) {List<String> list  = new ArrayList<>();list.add("v1");list.add("v2");list.add("v3");Map<String, String> map = list.stream().collect(Collectors.toMap(str -> str.replace("v", "k"),str -> str));map.forEach((k,v) -> System.out.println(k + ":" + v));}
/*输出:k1:v1  k2:v2 k3:v3  */

若生成的Key有重复,会抛出一个叫做deplicate key value 的报错信息,这时候就需要利用tomap的第三个参数,处理重复Key的问题

public static void main(String[] args) {List<String> list  = new ArrayList<>();list.add("v1");list.add("v2");list.add("v3");Map<String, String> map = list.stream().collect(Collectors.toMap(str -> str.replace("v", "k"),str -> str,(k1,k2) -> k2));map.forEach((k,v) -> System.out.println(k + ":" + v));}
/*输出:k2:v2 k3:v3  */

7.集合与数组的转化

学完集合之后,可以明显感觉到集合相比数组的强大,在做一些算法题时,可以通过将数组转为集合来便捷的执行某些操作

数组转集合

网上有比较多的方法,需要甄别使用

  1. Arrays.asList(数组)
    使用时需要特别注意,这个方法转化后的List并非我们常用的util.ArrayList,而是util.Arrays的内部类ArrayList,不要使用此方法进行转化

  2. Collections.addAll(集合,数组)
    该方法需要先新建一个集合,然后以追加的方式,将数组内容追加转化为集合

    Double[] value = new Double[5];
    List<Double> valueList = new LinkedList<>(); Collections.addAll(valueList,value);
    
  3. Arrays.stream(数组).collect(Collectors.toList());
    流式函数真的是很方便,可以将数组转为Stream流,然后调用collect(Collectors.toList())

    不过,需要注意的是,对于非引用类型数组,因为只能创建其包装类的集合,所以类似于int[],double[]数组,还需要有一步装箱操作

     int[] worker = new int[5];List<Integer> workerList = Arrays.stream(worker).boxed().collect(Collectors.toList());
    

集合转数组

集合List本身提供了一个转数组的方法,toArray()

Object[] toArray();
<T> T[] toArray(T[] a);

第一个转化后为object数组,并不能直接使用,较为推荐使用第二个重载方法

需要传入一个目标类型数组来传入泛型,使用方法如下:

int[] worker = new int[5];
List<Integer> workerList = Arrays.stream(worker).boxed().collect(Collectors.toList());
Integer[] result = workerList.toArray(new Integer[0]);

结果数组长度并非是我们传入的泛型数组的长度,而是原列表的长度

都学习到这里了,不妨点击一个关注吧~

java云同桌学习系列(七)——集合相关推荐

  1. Java云同桌学习系列(十五)——MySQL数据库

    本博客java云同桌学习系列,旨在记录本人学习java的过程,并与大家分享,对于想学习java的同学,我希望这个系列能够鼓励大家一同与我学习java,成为"云同桌". 每月预计保持 ...

  2. java云同桌学习系列(十四)——JavaScript语言

    本博客java云同桌学习系列,旨在记录本人学习java的过程,并与大家分享,对于想学习java的同学,可以随着我的步伐一起进步,我希望这个系列能够鼓励大家一同与我学习java,成为"云同桌& ...

  3. Java云同桌学习系列(十三)——前端技术之HTML与CSS

    本博客java云同桌学习系列,旨在记录本人学习java的过程,并与大家分享,对于想学习java的同学,可以跟随我的步伐一起学习,我希望这个系列能够鼓励大家一同与我学习java,成为"云同桌& ...

  4. Java云同桌学习系列(十九)——Linux系统

    本博客java云同桌学习系列,旨在记录本人学习java的过程,并与大家分享,对于想学习java的同学,我希望这个系列能够鼓励大家一同与我学习java,成为"云同桌". 每月预计保持 ...

  5. java云同桌学习系列(十)——网络编程

    本博客java云同桌学习系列,旨在记录本人学习java的过程,并与大家分享,对于想学习java的同学,我希望这个系列能够鼓励大家一同与我学习java,成为"云同桌". 每月预计保持 ...

  6. Java IO流学习总结七:Commons IO 2.5-FileUtils

    Java IO流学习总结七:Commons IO 2.5-FileUtils 转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/5497 ...

  7. Android音视频学习系列(七) — 从0~1开发一款Android端播放器(支持多协议网络拉流本地文件)

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  8. scrapy爬虫学习系列七:scrapy常见问题解决方案

    scrapy爬虫学习系列七:scrapy常见问题解决方案 参考文章: (1)scrapy爬虫学习系列七:scrapy常见问题解决方案 (2)https://www.cnblogs.com/zhaoji ...

  9. 点云深度学习系列博客(二): 点云配准网络PCRNet

    目录 一. 简介 二. 基础结构 三. 项目代码 四. 实验结果 总结 Reference 今天的点云深度学习系列博客为大家介绍一个用于点云配准的深度网络:PCRNet [1].凡是对点云相关应用有些 ...

最新文章

  1. 开源大佬面对面:InsightFace带你打造开箱即用的人脸识别系统
  2. maven 安装_Maven的下载、安装、配置以及验证操作
  3. setTimeout and jquery
  4. php pdo mysql存储过程_MySQL从PHP PDO中的存储过程检索变量
  5. 使用AWS CloudWatch 调优Lambda函数 | 技术头条
  6. ad18敷铜为什么打的孔里面也有_PCB覆铜后放置大量过孔有什么作用?具体怎么放置?...
  7. mysql 事务sql_mysql存储过程之事务篇
  8. STM32工作笔记0040---认识MOS管
  9. 窥探Swift之新添数据类型元组与可选值
  10. HDU2100 Lovekey【大数+进制】
  11. jquery常用基本用法,让你爱上它!
  12. python参考手册文字版_Python参考手册(第4版)
  13. E430 加装固态硬盘(SSD)参考
  14. 小米平板完整bios_小米平板bios设置u盘启动操作步骤
  15. python 源代码 macd双底 高 低_通达信双底选股公式-MACD底背离通达信选股公式
  16. LogLog Counting
  17. 杨幂生日祝福贺卡!!~
  18. mysql:[ERR] 32> 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘createTime‘ at ro
  19. 巴菲特与搭档查理芒格手把手教你如何读财报,唯一一篇百看不厌炒股最实用文章
  20. 微信开通批量检测软件的常见问题解答(入手必备)

热门文章

  1. 如何利用业余时间快乐的赚钱
  2. 基于ssm DIY手工坊购物论坛网站mysql
  3. 免费生成Nginx的Https使用的SSL证书
  4. 荐读 | 英特尔:初探 “边缘控制”
  5. 系统架构设计师考试题库重点案例:软件系统建模
  6. 简历制作器App技术支持
  7. 整体学历较高,硕士占比达 40%,周星驰也开始招募Web3人才!
  8. torch.size()
  9. PostgreSQL或者GP数据库创建schema及添加search_path
  10. 计算机主机需要ccc,哪些计算机需要做3c认证?计算机3c认证