一、集合
- 常见的数据结构
- 数组:数组是最常用的数据结构,数组的特点是长度固定,可以用下标索引,并且所有的元素的类型都是一致的。
- 栈:是一种特殊的线性表,仅能在线性表的一端操作,栈顶允许操作,栈底不允许操作。 栈的特点是:先进后出,或者说是后进先出,从栈顶放入元素的操作叫入栈,取出元素叫出栈。
- 堆:是一种比较特殊的数据结构,可以被看做一棵树的数组对象,具有以下的性质:
1.堆中某个节点的值总是不大于或不小于其父节点的值;
2.堆总是一棵完全二叉树。
- 队列:队列与栈一样,也是一种线性表,不同的是,队列可以在一端添加元素,在另一端取出元素,也就是:先进先出。从一端放入元素的操作称为入队,取出元素为出队。
- 链表:链表是物理存储单元上非连续的、非顺序的存储结构,数据元素的逻辑顺序是通过链表的指针地址实现,每个元素包含两个结点,一个是存储元素的数据域 (内存空间),另一个是指向下一个结点地址的指针域。根据指针的指向,链表能形成不同的结构,例如单链表,双向链表,循环链表等。
优点:
链表是很常用的一种数据结构,不需要初始化容量,可以任意加减元素;
添加或者删除元素时只需要改变前后两个元素结点的指针域指向地址即可,所以添加,删除很快;
缺点:
因为含有大量的指针域,占用空间较大;
查找元素需要遍历链表来查找,非常耗时。
适用场景:
数据量较小,需要频繁增加,删除操作的场景
- 二叉树:树是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合。把它叫做 “树” 是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
1.每个节点有零个或多个子节点;
2.没有父节点的节点称为根节点;
3.每一个非根节点有且只有一个父节点;
4.除了根节点外,每个子节点可以分为多个不相交的子树;
- 散列表(哈希表)
散列表,也叫哈希表,是根据关键码和值 (key和value) 直接进行访问的数据结构,通过key和value来映射到集合中的一个位置,这样就可以很快找到集合中的对应元素。
记录的存储位置=f(key)
这里的对应关系 f 成为散列函数,又称为哈希 (hash函数),而散列表就是把Key通过一个固定的算法函数既所谓的哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该数字为下标的数组空间里,这种存储空间可以充分利用数组的查找优势来查找元素,所以查找的速度很快。
哈希表在应用中也是比较常见的,就如Java中有些集合类就是借鉴了哈希原理构造的,例如HashMap,HashTable等,利用hash表的优势,对于集合的查找元素时非常方便的,然而,因为哈希表是基于数组衍生的数据结构,在添加删除元素方面是比较慢的,所以很多时候需要用到一种数组链表来做,也就是拉链法。拉链法是数组结合链表的一种结构,较早前的hashMap底层的存储就是采用这种结构,直到jdk1.8之后才换成了数组加红黑树的结构
- 两个最大的集合父类:
1.Collection:单值存储的集合,每一个下标只存储一个数据
2.Map:以键值对(相当于钥匙和锁)存储的双值集合。
3.Iterator:迭代器
二、Collection
2.1 Collection
- Collection 接口是在整个 Java 类集中保存单值的最大操作父接口,里面每次操作的时候都只能保存一个对象的数据
基本方法
- 往往在开发中不会直接调用collection的方法,因为他有2个子类:List,Set,一般都是直接通过子类去调用方法。
- 个人理解:一般调用这些方法的基本格式就是
1.先new出这个类的对象
2.再用new出来的对象去直接 . 出来引用
所以去查JDK也是挺重要的
2.2 List
- 在整个集合中 List 是 Collection 的子接口,里面的所有内容都是允许重复的
- List 接口拥有比 Collection 接口更多的操作方法
- List下面还有3个子类:
- ArrayList
- Vector
- LinkedList(双向链表)
- ArrayList
- 特点:增加删除慢,查找快
默认扩容是原来的1.5倍
List<String> all = new ArrayList<String>();//初始容量为10
List<String> all = new ArrayList<String>(int 100);//容量变成100
ArrayList 是 List 接口的子类,此类的定义如下:
public classArrayList<E> extendsAbstractList<E>implements List<E>, RandomAccess, Cloneable, Serializable
此类继承了 AbstractList 类。AbstractList 是 List 接口的子类。AbstractList 是个抽象类,适配器设计模式。
package org.listdemo.arraylistdemo;
import java.util.ArrayList;
import java.util.List;
public class ArrayListDemo01 {
public static void main(String[] args) {
List<String> all = new ArrayList<String>(); // 实例化List对象,并指定泛型类型
all.add("hello "); // 增加内容,此方法从Collection接口继承而来
all.add(0, "LAMP ");// 增加内容,此方法是List接口单独定义的
all.add("world"); // 增加内容,此方法从Collection接口继承而来
System.out.println(all); // 打印all对象调用toString()方法
}
}
以上的操作向集合中增加了三个元素,其中在指定位置增加的操作是 List 接口单独定义的。随后进行输出的时候,实际上调用的是 toString()方法完成输出的。
package org.listdemo.arraylistdemo;
import java.util.ArrayList;
import java.util.List;
public class ArrayListDemo02 {
public static void main(String[] args) {
List<String> all = new ArrayList<String>(); // 实例化List对象,并指定泛型类型
all.add("hello "); // 增加内容,此方法从Collection接口继承而来
all.add(0, "LAMP ");// 增加内容,此方法是List接口单独定义的
all.add("world"); // 增加内容,此方法从Collection接口继承而来
all.remove(1); // 根据索引删除内容,此方法是List接口单独定义的
all.remove("world");// 删除指定的对象
System.out.print("集合中的内容是:");
for (int x = 0; x < all.size(); x++) { // size()方法从Collection接口继承而来
System.out.print(all.get(x) + "、"); // 此方法是List接口单独定义的
}
}
}
- Vector
与 ArrayList 一样,Vector 本身也属于 List 接口的子类,此类的定义如下:
public class Vector<E> extendsAbstractList<E>implements List<E>, RandomAccess, Cloneable, Serializable
- 此类与 ArrayList 类一样,都是 AbstractList 的子类。所以,此时的操作只要是 List 接口的子类就都按照 List 进行操作
package org.listdemo.vectordemo;
import java.util.List;
import java.util.Vector;
public class VectorDemo01 {
public static void main(String[] args) {
List<String> all = new Vector<String>(); // 实例化List对象,并指定泛型类型
all.add("hello "); // 增加内容,此方法从Collection接口继承而来
all.add(0, "LAMP ");// 增加内容,此方法是List接口单独定义的
all.add("world"); // 增加内容,此方法从Collection接口继承而来
all.remove(1); // 根据索引删除内容,此方法是List接口单独定义的
all.remove("world");// 删除指定的对象
System.out.print("集合中的内容是:");
for (int x = 0; x < all.size(); x++) { // size()方法从Collection接口继承而来
System.out.print(all.get(x) + "、"); // 此方法是List接口单独定义的
}
}
}
- 区别:
- Vector与ArrayList 相比他的线程安全系数更高
- LinkedList
- 双向链表,增删快查找慢。
2.2 Set
- Set 接口也是 Collection 的子接口,与 List 接口最大的不同在于,Set 接口里面的内容是不允许重复的。
- Set 接口并没有对 Collection 接口进行扩充,基本上还是与 Collection 接口保持一致。因为此接口没有 List 接口中定义的 get(int index)方法,所以无法使用循环进行输出(只能迭代器,因为没有下标,不可以通过下标找到指定的元素)。(List就是继承了Collection,自己加了东西。Set也是Collection,但是没加东西)
- 在此接口中有两个常用的子类:HashSet、TreeSet
- HashSet (散列存放)原理HashMap
Set 接口并没有扩充任何的 Collection 接口中的内容,所以使用的方法全部都是 Collection 接口定义而来的
HashSet 属于散列的存放类集,里面的内容是无序存放的
- 操作的时候已经指定了操作的泛型类型,那么现在最好的做法是由泛型所指定的类型变为指定的数组。
package org.listdemo.hashsetdemo;
import java.util.HashSet;
import java.util.Set;
public class HashSetDemo03 {
public static void main(String[] args) {
Set<String> all = new HashSet<String>(); // 实例化Set接口对象
all.add("A");
all.add("B");
all.add("C");
all.add("D");
all.add("E");
String[] str = all.toArray(new String[] {});// 变为指定的泛型类型数组
for (int x = 0; x < str.length; x++) {
System.out.print(str[x] + "、");
}
}
}
- TreeSet:(有序排列);原理二叉树
与 HashSet 不同的是,TreeSet 本身属于排序的子类,此类的定义如下:
public class TreeSet<E> extendsAbstractSet<E>implements NavigableSet<E>, Cloneable, Serializable
import java.util.Objects;
import java.util.TreeSet;
public class Demo8 {
public static void main(String[] args) {
TreeSet<Person> date = new TreeSet<>();
Person p = new Person("乔一成",18);
Person p2 = new Person("乔二强",19);
date.add(p);
date.add(p2);
System.out.println(date);
}
static class Person implements Comparable{
private String name;
private int age;
@Override
public int compareTo(Object o) {
//this与o比较
//返回值数据:负数-this小/0-一样大/正数-this大
if (this.age > ((Person)o).age) {//因为o是父类 向下强转 一般this就是新数据通过Person传进来的数据-第一个数据默认被放置在第一个 然后接下去比较
return 1;
}else if (this.age < ((Person)o).age){
return -1;
}else {
return this.name.compareTo(((Person)o).name);
}
}
public Person(String name, int age) {
this.name = name;
this.age = 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;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person)) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Person() {
}
}
}
-------------------------------
[Person{name='乔一成', age=18}, Person{name='乔二强', age=19}]
2.3 Iterator–迭代器
- 注意:只要是碰到了集合 ,则输出的时候想都不想就使用 Iterator 进行输出
Iterator 的返回值是布尔类型,用来判断是否有下一个 - 操作原理:是不断的判断是否有下一个元素,有的话,则直接输出
接口定义如下:
public interface Iterator<E>
要想使用此接口,则必须使用 Collection 接口,在 Collection 接口中规定了一个 iterator()方法,可以用于为 Iterator 接口进行实例化操作
三个基本方法:
package org.listdemo.iteratordemo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class IteratorDemo01 {
public static void main(String[] args) {
Collection<String> all = new ArrayList<String>();
all.add("A");
all.add("B");
all.add("C");
all.add("D");
all.add("E");
Iterator<String> iter = all.iterator();
while (iter.hasNext()) {// 判断是否有下一个元素
String str = iter.next(); // 取出当前元素
System.out.print(str + "、");
}
}
}
- 以上的操作是 Iterator 接口使用最多的形式,也是一个标准的输出形式。
public class IteratorDemo01 {
public static void main(String[] args) {
Collection<String> all = new ArrayList<String>();
all.add("A");
all.add("B");
all.add("C");
all.add("D");
all.add("E");
Iterator<String> iter = all.iterator();
iter.hasNext();
iter.remove();//remove这个函数必须在hasNext之后才可以使用因为在次之前指针可能没有指向数据而是空值即无法删除
System.out.print(all.size);
-----------------------------------------------
4
- ListIterator
如果要想使用 ListIterator 接口,则必须依靠 List 接口进行实例化
public class IteratorDemo01 {
public static void main(String[] args) {
Collection<String> all = new ArrayList<String>();
all.add("A");
all.add("B");
all.add("C");
all.add("D");
all.add("E");
ListIterator<Integer> iterator = date.listIterator();//这个迭代器是往上走的
iterator.add(100);
iterator.next();
iterator.next();
iterator.set(200);
iterator.previous();//在往上移的时候一定要确保前面有数据 如若上一个是null的话 出异常
iterator.previous();//因此一般用.next方法
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
--------------------------------
三、Map(Mapping–映射)
- Map是以键值对存放数据的,通过不可重复的键让相对应的内容也不发生改变,即每一个键对应一个值。
方法:
- Map 本身是一个接口,所以一般会使用以下的几个子类:HashMap、TreeMap、Hashtable
-
HashMap — 无序存放
-
HashMap 是 Map 的子类,此类的定义如下:
public class HashMap<K,V> extendsAbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable
import java.util.*;
public class Demo9 {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<>();
map.put(1,"乔一成");
map.put(2,"乔二强");
map.put(3,"乔三丽");
Set<Integer> set = map.keySet();//得到全部的key
Collection<String> value = map.values();//得到全部的value
Iterator<Integer> iterator = set.iterator();
Iterator<String> iterator1 = value.iterator();
System.out.println("全部的key:");
while (iterator.hasNext()){
Integer i = iterator.next();
System.out.println(i+"->"+map.get(i));//
// System.out.println(iterator.next()+",");//
}
System.out.println("全部的value:");
while (iterator1.hasNext()){
System.out.println(iterator1.next()+",");
}
}
}
----------------------------------------------------------------
全部的key:
1->乔一成
2->乔二强
3->乔三丽
全部的value:
乔一成,
乔二强,
乔三丽,
- Hashtable
import java.util.*;
public class Demo9 {
public static void main(String[] args) {
Map<Integer,String> map = new Hashtable<>();
map.put(1,"乔一成");
map.put(2,"乔二强");
map.put(3,"乔三丽");
String s = map.get(1);
if (s != null){
System.out.println(2+"="+s);
}
}
}
-------------------------------------------
2=乔一成
-
Hashtable 与 HashMap 基本上没有什么区别,而且本身都是以 Map 为操作标准的,所以操作的结果形式都一样。但是 Hashtable 中是不能向集合中插入 null 值的
-
哈希表的概述