本博客java云同桌学习系列,旨在记录本人学习java的过程,并与大家分享,对于想学习java的同学,我希望这个系列能够鼓励大家一同与我学习java,成为“云同桌”。
每月预计保持更新数量三章起,每章都会从整体框架入手,介绍章节所涉及的重要知识点及相关练习题,并会设置推荐学习时间,每篇博客涉及到的点都会在开篇目录进行总览。(博客中所有高亮部分表示是面试题进阶考点)
学习时间:一周 学习建议:集合相当于数组的进阶版本,未来可能会频繁使用集合开发,建议弄清楚各集合类之间的区别和其特点,不同问题采用不同集合解决
栈又称堆栈,栈限定只能在线性表表尾进行插入和删除的操作,这一端允许插入和删除称为栈顶,另一端称为栈底,不含任何数据元素的称为空栈。
特点:
(1)先进先出 (2)栈的入口和出口在一端,都位于栈顶图解:
专业术语: 压栈:存元素 弹栈:取元素
队列,简称队,只允许在队尾(rear)进行插入,只允许在队头(front)进行元素删除的操作。空队列是不含任何元素的空表
特点:
(1)先进先出 (2)队列的出口在一侧,入口在另一侧图解:
以单向链表为例,链表是有一系列(包含存储数据元素的数据域与下一个结点地址的指针域)结点组成,结点数量可以动态生成
图解:
特点:
(1)内存地址不连续 (2)长度可以动态增加 (3)查找元素较慢,增删元素较快结点结构:
private class Node{ private T t; private Node next; }通常java中常用的二叉树指,每个结点不超过2的有序树。即如何一个数比根结点数据更下,则存放在根结点的左子树,如果一个数比根结点数据更大,则存放在根结点的右子树,结点与结点之间依靠父与左右子的关系建立。
有序二叉树结点结构
public class Node { private int data; private BNode leftNode; private BNode rightNode; }Java中为了方便用户操作各个数据结构,引入了类集的概念,所有的类集的操作的接口或类都在java.util包中。
介绍:Collection 接口是在整个 Java 类集中保存单值的最大操作父接口,里面每次操作的时候都只能保存一个对象的数据。在开发中,通常不会直接使用Collection接口,而使用其操作的子接口,List(允许重复),Set(不允许重复)
常用方法:
add(E e), 向集合中添加元素size(),返回此集合中的元素数量iterator(),返回集合中的元素迭代器toArray(),返回包含此集合中所有元素的一个数组。介绍: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 接口的实例介绍: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]介绍:大多数与ArrayList类使用的相同,它是为了解决线程不安全的问题,Vector是同步的, 如果不需要线程安全实现,建议使用ArrayList代替Vector
特点:
线程安全 数组结构,增加删除慢,查找快,可动态调整大小(初始默认为10,每次扩容倍数可指定)构造方法:
Vector(),构造一个空向量,使其内部数据数组的大小为 10 ,其标准容量增量为零。Vector(int initialCapacity),构造一个具有指定初始容量且容量增量等于零的空向量。Vector(int initialCapacity, int capacityIncrement),构造具有指定初始容量和容量增量的空向量。常用方法(无特别方法,常用方法均继承于List接口)
介绍: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介绍:通常称为迭代器,可以对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),在迭代器指针所指位置,修改数据forEach又称为增强for循环,用于迭代数组或集合(Collection下的集合)
语法:
for(数据类型 存储数据元素的变量:集合或者数组名){ 循环体 }对于集合,原理相当于为集合创建了一个迭代器,然后遍历,后续知识点中将会有实例
介绍:Set接口也是Collection接口下的一个主要接口,是存储无重复元素的集合
常用方法:
iterator(),返回一个set中元素的迭代器相比于List接口,Set接口并没有自己写一个用于取出数据的get()方法,因此,对于取出数据,只能通过获取迭代器,然后迭代器移动到指定位置后取出数据
此接口中有两个常用的子类:HashSet、TreeSet
介绍:该类是Set接口的常用实现类之一,存储元素时会检测是否是重复元素,根据返回值决定是否存入数据,采用散列存储结构(哈希表)
特点:
1.散列存储结构 2.数据元素无序 3.无重复数据元素(重复则存不进去)因为没有Set接口中没有get()方法,取数据时,可以获取迭代器遍历,或者直接使用forEach循环结构进行取出数据
介绍:该类是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; //重载比较规则方法 @Override public int compareTo(Person o) { //将this对象属性与o对象属性进行比较 if(this.age > o.age){ return 2; }else if(this.age == o.age){ return 1; }else{ return 0; } } @Override public String toString() { return "Person{" + "age=" + age + '}'; } public Person(int age) { this.age = age; } }结构:可以简单理解成“数组+链表”的结构,它根据关键码值(key value)映射到数组中的一个位置,对于位置冲突的情况,在此建立链表,尾插入新数据
对于上图结构,某个数组位置及其链表,专业术语称之为“哈希桶”,当哈希桶中数据过多,就会降低表的性能,不便于查找
因此,如果哈希桶>8,哈希桶内的链表就会转化成红黑树(一种平衡二叉查找树),然后当哈希桶减少到6时,红黑树又转变为链表
初始属性:初始哈希表长度:16,散列因子:0.75
散列因子:以0.75为例,当哈希表长度的75%已经被占用,则进行扩容为原长度的2倍,散列因子官方建议为0.75效率最高,散列因子过小,会造成空间浪费,过大,会导致检索速度变慢
如果数据较多,只设置长度为初始长度,需要多次扩容,造成内存的大量占用,建议数据较多时指定一个合适的初始长度
注意:非必要情况,不要修改已经存入哈希表内的键,否则容易造成数据错乱
介绍:Map其实是mapping(映射)的缩写,相比于Collection接口存储单值,Map以一个个键值对的形式存储多个值,每个键名互不重复
特点:
* 键值对形式的多值存储 * 键值对的键名互不重复 * 存储不连续,无法像集合一样直接遍历常用方法:
clear(),清空此映射中的所有键值对映射get(Object key),通过键名获取返回键名指定的数据remove(Object key),通过键名删除所对应的数据,并返回该被删除的数据keySet(),解决遍历问题,以set集合的形式返回此映射的所有键名put(K key, V value),将一个键值对关系存入此映射中,如果此键名在此映射中已有对应的数据,则返回旧数据,否则返回nullcontainsKey(Object key),如果此映射包含指定键的映射,则返回 true 。containsValue(Object value),如果此映射将一个或多个键映射到指定值,则返回 truesize(),获取此映射中键值对的数量介绍:基于哈希表的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,3 System.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(); }都学习到这里了,不妨点击一个关注吧~