数据结构
一、数据存储的常用结构:栈 队列 数组 链表 红黑树 二、栈stack:先进后出 入口和出口在集合的同一侧 三、队列:先进先出 入口和出口在集合的两侧 四、数组:
- 查询快:数组的地址是连续的 我们通过数组的首地址可以找到数组 通过数组的索引可以快速查找某一个元素
- 增删慢:数组的长度是固定的 我们想要增加/删除一个元素 必须创建一个新数组 把源数组的数据复制过来。假设定义了一个数组:
int[] arr = {1,2,3,4}; 要把数组中索引是3的元素删除 必须创建一个新的数组,长度是源数组的长度-1 把源数组的其他元素复制到新数组中 把新数组的地址赋值给变量arr 源数组会在内存中被销毁(垃圾回收)。在堆内存中 频繁的创建数组 复制数组中的元素 销毁数组 效率低下
五、链表:
- 查询慢:链表中地址不是连续的 每次查询元素都必须从头开始查询
- 增删快:链表结构 增加/删除一个元素 对链表的整体结构没有影响 所以增删快
- 链表中每一个元素也称之为一个节点 一个节点包含了一个数据源(存储数组)两个指针域(存储地址)
- 链表结构:一个链表包含三部分 第一部分存放自己的地址 第二部分存储数据 第三部分存储下一个节点的地址
- 分类:
(1)单向链表:链表中只有一条链子 不能保证元素的顺序(存储元素和取出元素的顺序有可能不一致) (2)双向链表:链表中有两条链子 有一条链子是专门记录元素的顺序 是一个有序的集合
六、红黑树:
- 约束:
(1)节点可以是红色的也可以是黑色的 (2)根节点是黑色的 (3)叶子节点(空节点)是黑色的 (4)每个红色的节点的子节点都是黑色的 (5)任何一个节点到其每一个叶子节点的所有路径山黑色节点数相同 - 特点:趋近于平衡树 查询的速度非常快 查询叶子节点最大次数和最小次数不能超过2倍
public class Stack {}
java.util.list
一、java.util.list 接口继承了Collection接口 二、List接口的特点:
- 有序的集合 存储元素和取出元素的顺序是一致的 存储123 取出123
- 有索引 包含了一些带索引的方法
- 允许存储重复的元素
三、List接口中带索引的方法(特有): public void add(int index,E element) :将制定的元素添加到该集合中的指定位置上public E get(int index) :返回集合中指定位置的元素public E remove(int index) :移除列表中指定位置的元素 返回的是被移除的元素public E set(int index,E element) :用指定元素替换集合中指定位置的元素 返回值的更新前的元素
注意:操作索引的时候 一定要防止索引越界异常
四、list集合的实现类:
- ArrayList集合:此实现不是同步的 是多线程的
- LinkedList集合
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ListClass {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("a");
System.out.println(list);
list.add(3,"itheima");
System.out.println(list);
String remoString = list.remove(2);
System.out.println("被移除的元素:" + remoString);
System.out.println(list);
String setString = list.set(4, "A");
System.out.println("被替换的元素:" + setString);
System.out.println(list);
System.out.println("使用for循环遍历集合:================================================");
for (int i = 0; i < list.size(); i++) {
String string = list.get(i);
System.out.println(string);
}
System.out.println("使用迭代器遍历集合:================================================");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String string = (String) iterator.next();
System.out.println(string);
}
System.out.println("使用增强for循环遍历集合:================================================");
for (String string : list) {
System.out.println(string);
}
}
}
java.util.Set
一、java.util.Set 接口继承了Collection接口 二、Set接口的特点:
- 不允许存储重复的元素
- 没有索引 没有带索引的方法 也不能使用普通的for循环遍历
三、java.util.HashSet 集合实现了Set接口 四、HashSet的特点:
- 不允许存储重复的元素
- 没有索引 没有带索引的方法 也不能使用普通的for循环遍历
- 是一个无序的集合 存储元素和取出元素的顺序有可能不一致
- 底层是一个哈希表结构(哈希表结构的特点:查询的速度非常快)
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class SetClass {
public static void main(String[] args) {
Set<Integer> set = new HashSet<Integer>();
set.add(1);
set.add(3);
set.add(2);
set.add(1);
System.out.println("使用迭代器遍历set集合:=======================");
Iterator<Integer> iterator = set.iterator();
while (iterator.hasNext()) {
Integer integer = (Integer) iterator.next();
System.out.println(integer);
}
System.out.println("使用增强for遍历set集合:=======================");
for (Integer integer : set) {
System.out.println(integer);
}
}
}
五、Set集合不允许重复存储元素的前提:存储的元素必须重写hashCode方法和equals方法
import java.util.HashSet;
public class SetClass2 {
public static void main(String[] args) {
HashSet<String> set = new HashSet<String>();
String s1 = new String("abc");
String s2 = new String("abc");
set.add(s1);
set.add(s2);
set.add("重地");
set.add("通话");
set.add("abc");
System.out.println(set);
}
}
Set集合在调用add方法时 add方法会调用元素的hashCode方法和equals方法 判断元素是否重复,执行以上代码的过程:
- 先在堆内存中开辟一段空间 大小为16
- 执行
set.add(s1) :add方法会调用s1的hashCode方法 计算字符串"abc"的哈希值 计算结果为96354,然后在集合中找有没有96354这个哈希值的元素 发现没有 就会把s1存储到刚才开辟的空间中 索引为0的位置 - 执行
set.add(s2) :add方法会调用s2的hashCode方法 计算字符串"abc"的哈希值 计算结果为96354,然后在集合中找有没有96354这个哈希值的元素 发现有(此时发生哈希冲突),s2会调用equals方法和哈希值相同的元素进行比较,s2.equals(s1) 返回true,两个元素的哈希值相同 equals方法返回true 认定两个元素相同 就不会把s2存储到集合中 set.add("重地") :add方法会调用"重地"的hashCode方法 计算字符串"重地"的哈希值 计算结果为1179395,然后在集合中找有没有1179395这个哈希值的元素 发现没有 就会把"重地"存储到刚才开辟的空间中 索引为2的位置set.add("通话") :add方法会调用"通话"的hashCode方法 计算字符串"通话"的哈希值 计算结果为1179395,然后在集合中找有没有1179395这个哈希值的元素 发现有(此时发生哈希冲突),"通话"会调用equals方法和哈希值相同的元素进行比较,"通话".equals("重地") 返回false,两个元素的哈希值相同 equals方法返回false 认定两个元素不同 就会把"通话"存储到集合中 索引为2的位置
六、HashSet存储自定义类型元素:
- set集合保证元素唯一 :存储的元素(String Integer …… Person Student ……)必须重写hashCode方法和equals方法
- 要求:同名同年龄的人视为同一个人 只能存储一次
import java.util.HashSet;
public class HashSetSavePerson {
public static void main(String[] args) {
HashSet<People> set = new HashSet<People>();
People p1 = new People("小美女", 18);
People p2 = new People("小美女", 18);
People p3 = new People("小美女", 19);
System.out.println(p1.hashCode());
System.out.println(p2.hashCode());
System.out.println(p1 == p2);
System.out.println(p1.equals(p2));
set.add(p1);
set.add(p2);
set.add(p3);
System.out.println(set);
}
}
public class People {
private String nameString;
private int age;
@Override
public String toString() {
return "People{" + "name='" + nameString + '\'' + ",age=" + age + '}';
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((nameString == null) ? 0 : nameString.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
People other = (People) obj;
if (age != other.age) return false;
if (nameString == null) {
if (other.nameString != null) return false;
} else if (!nameString.equals(other.nameString)) return false;
return true;
}
public String getNameString() {return nameString;}
public void setNameString(String nameString) {this.nameString = nameString;}
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
public People(String nameString, int age) {
super();
this.nameString = nameString;
this.age = age;
}
public People() {super();}
}
java.util.LinkedHashSet
一、java.util.LinkedHashSet 集合继承了HashSet 集合 二、LinkedHashSet 特点:底层是一个哈希值(数组+链表/红黑树)+链表:多了一条链表(记录元素的存储顺序) 保证元素有序
import java.util.HashSet;
import java.util.LinkedHashSet;
public class LinkedHashSetClass {
public static void main(String[] args) {
HashSet<String> set = new HashSet<String>();
set.add("www");
set.add("abc");
set.add("abc");
set.add("itcast");
System.out.println(set);
LinkedHashSet<String> link = new LinkedHashSet<String>();
link.add("www");
link.add("abc");
link.add("abc");
link.add("itcast");
System.out.println(link);
}
}
java.util.LinkedList
一、LinkedList 集合:List接口的链表实现 该实现不是同步的 意味着是多线程的 多线程的速度快 二、java.util.LinkedList 集合继承了List接口 三、LinkedList 集合特点:
- 底层是一个链表结构:查询慢 增删快
- 里面包含了大量操作首尾元素的方法
- 使用
LinkedList 集合特有的方法不能使用多态
四、常用方法:
public void addFirst(E e) :将指定元素插入此列表的开头public void addLast(E e) :将指定元素添加到此列表的结尾public void push(E e) :将元素推入此列表所表示的堆栈public E getFirst() :返回此列表的第一个元素public E getLast() :返回此列表的最后一个元素public E removeFirst() :移除并返回此列表的第一个元素public E removeLast() :移除并返回此列表的最后一个元素public E pop() :从此列表所表示的的堆栈处弹出一个元素public boolean isEmpty() :如果列表不包含元素 则返回true
import java.util.LinkedList;
public class LinkedListClass {
public static void main(String[] args) {
show01();
show02();
show03();
}
private static void show03() {
LinkedList<String> linkedList = new LinkedList<String>();
linkedList.add("a");
linkedList.add("b");
linkedList.add("c");
String firString = linkedList.pop();
System.out.println("被移除的第一个元素:" + firString);
String laString = linkedList.removeLast();
System.out.println("被移除的第二个元素:" + laString);
System.out.println(linkedList);
}
private static void show02() {
LinkedList<String> linkedList = new LinkedList<String>();
linkedList.add("a");
linkedList.add("b");
linkedList.add("c");
String firString = linkedList.getFirst();
System.out.println(firString);
String laString = linkedList.getLast();
System.out.println(laString);
}
private static void show01() {
LinkedList<String> linkedList = new LinkedList<String>();
linkedList.add("a");
linkedList.add("b");
linkedList.add("c");
System.out.println(linkedList);
linkedList.push("www");
System.out.println(linkedList);
linkedList.addLast("com");
System.out.println(linkedList);
}
}
哈希值
一、哈希值:是一个十进制的整数 由系统随机给出(就是对象的地址值 是一个逻辑地址 是模拟出来得到的地址 不是数据实际存储的物理地址) 二、在Object 类有个方法 可以获取对象的哈希值:int hashCode() :返回该对象的哈希值 三、hashCode 方法的源码:public native int hashCode(); native :代表该方法调用的是本地操作系统的方法 四、HashSet 集合存储数据的结构(哈希表):
- JDK1.8之前 哈希表 = 数组 + 链表
- JDK1.8之后 哈希表 = 数组 + 链表 但是在链表中用到了红黑树 红黑树可以提高查询的速度 也就是 哈希表 = 数组 + 红黑树
五、哈希表特点:
- 查询快
- 初始容量为16
- 数组结构:把元素进行了分组 相同哈希值的元素是一组 链表/红黑树结构把相同哈希值的元素连接到一起
- 存储数据到集合中 先计算数据的哈希值
- 若同一个哈希值下包含的数据大于8个(小于8个数据在内存中以链表的形式存放)为了提高查询速度 就会将数据由链表转换成红黑树
public class HashCode {
public static void main(String[] args) {
Person p1 = new Person();
int h1 = p1.hashCode();
System.out.println(h1);
Person p2 = new Person();
int h2 = p2.hashCode();
System.out.println(h2);
System.out.println(p1);
System.out.println(p2);
System.out.println(p1 == p2);
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
System.out.println("重地".hashCode());
System.out.println("通话".hashCode());
}
}
public class Person extends Object{
@Override
public int hashCode() {return 1;}
}
Map
一、Map(K,V) :将键映射到值的对象 一个映射不能包含重复的键 每个键最多只能映射到一个值 K代表键的类型 V代表值的类型 二、Collection中的集合称为单列集合 元素是孤立存在的(理解为单身),向集合中存储元素只能一个一个存储 三、Map中的集合称为双列集合 元素是成对存在的(理解为夫妻) 每个元素由键与值两部分组成 通过键可以找到所对应的值 四、Map中的集合的特点:
- 键是唯一的 值可以重复
- 键和值一一映射 一个键对应一个值
- 靠键维护他们的关系
五、总结Map集合的特点:
- Map集合是一个双列集合 一个元素包含两个值(一个key 一个value)
- Map集合中的元素 key和value的数据类型可以相同 也可以不同
- Map集合中的元素 key是不允许重复的 value是可以重复的
- Map集合中的元素 key和value是一一对应
Map集合的遍历方式
一、Map集合的第一种遍历方式:通过键找值的方式
- Map集合中的方法:
Set<K> keySet() :返回此映射中包含的键的Set视图 - 实现步骤:
(1)使用map集合中的方法keyset() ,把map集合所有的key取出来 存储到一个set集合中 (2)遍历set集合 获取map集合中的每一个key (3)通过map集合中的方法get(key) 通过key找到value
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class KeySet {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("赵丽颖", 168);
map.put("杨颖", 165);
map.put("林志玲", 178);
Set<String> set = map.keySet();
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
String key = (String) iterator.next();
Integer valueInteger = map.get(key);
System.out.println(key + "=" + valueInteger);
}
System.out.println("也可以使用增强for遍历set集合:-------------------------------------------------------------------------------");
for (String key : set) {
Integer valueInteger = map.get(key);
System.out.println(key + "=" + valueInteger);
}
}
}
二、map集合遍历的第二种方式:使用entry对象遍历
- map集合中的方法:
Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射关系的Set视图 - 实现步骤:
(1)使用Map集合中的方法entrySet() 把map集合中多个Entry对象取出来 存储到一个Set集合中 (2)遍历Set集合 获取每一个Entry对象 (3)使用Entry对象中的方法getKey() 和getValue() 获取键与值
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class EntrySet {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("赵丽颖", 168);
map.put("杨颖", 165);
map.put("林志玲", 178);
Set<Map.Entry<String, Integer>> set = map.entrySet();
Iterator<Map.Entry<String, Integer>> iterator = set.iterator();
while (iterator.hasNext()) {
Map.Entry<java.lang.String, java.lang.Integer> entry = (Map.Entry<java.lang.String, java.lang.Integer>) iterator
.next();
String keyString = entry.getKey();
Integer valueInteger = entry.getValue();
System.out.println(keyString + "=" + valueInteger);
}
System.out.println("使用增强for循环遍历Set集合:============================================");
for (Map.Entry<String, Integer> entry : set) {
String keyString = entry.getKey();
Integer valueInteger = entry.getValue();
System.out.println(keyString + "=" + valueInteger);
}
}
}
java.util.HashTable
一、java.util.HashTable<K,V> 集合 implements Map<K,V> 接口 二、HashTable:底层也是一个哈希表 是一个线程的安全的集合 是单线程集合 速度慢 HashMap:底层是一个哈希表 是一个线程不安全的集合 是多线程的集合 速度快 三、HashMap集合(之前学得所有的集合):可以存储Null值 null键,HashTable集合不能存储null值 null键 四、HashTable和Vector集合一样 在JDK1.2版本之后被更先进的集合(HashMap ArrayList集合)取代了,HashTable的子类Properties依然活跃在历史的舞台 五、Properties集合是唯一一个和IO流相结合的集合
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
public class HashTableClass {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<String, String>();
map.put(null,"a");
map.put("b",null);
map.put(null,null);
System.out.println(map);
Hashtable<String, String> table = new Hashtable<String, String>();
table.put(null,"a");
table.put("b",null);
table.put(null,null);
System.out.println(table);
}
}
HashMap
一、java.util.HashMap<K,V>集合 implements Map<K,V>接口 二、HashMap集合的特点:
- HasMap集合底层是哈希表:查询的速度特别的快
(1)JDK1.8之前:数组+单向链表 (2)JDK1.8之后:数组+单向链表/红黑树(链表长度超过8):提高查询速度 - HasMap集合是一个无序的集合 存储元素和取出元素的顺序有可能不一致
三、java.util.LinkedHashMap<K,V> 集合 extends HashMap<K,V> 集合
LinkedHashMap<K,V> 集合特点: (1)LinkedHashMap<K,V> 集合底层是哈希表+链表(保证迭代的顺序) (2)LinkedHashMap<K,V> 集合是一个有序的集合 存储元素和取出元素的顺序是一致的
import java.util.HashMap;
import java.util.Map;
public class MapClass{
public static void main(String[] args) {
show01();
show02();
show03();
show04();
}
private static void show04() {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("赵丽颖", 168);
map.put("杨颖", 165);
map.put("林志玲", 178);
boolean b1 = map.containsKey("赵丽颖");
System.out.println("b1:" + b1);
boolean b2 = map.containsKey("赵颖");
System.out.println("b2:" + b2);
}
private static void show03() {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("赵丽颖", 168);
map.put("杨颖", 165);
map.put("林志玲", 178);
System.out.println(map);
Integer v1 = map.get("杨颖");
System.out.println("v1:" + v1);
Integer v2 = map.get("迪丽热巴");
System.out.println("v2:" + v2);
}
private static void show02() {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("赵丽颖", 168);
map.put("杨颖", 165);
map.put("林志玲", 178);
System.out.println(map);
Integer v1 = map.remove("林志玲");
System.out.println("v1:" + v1);
System.out.println(map);
Integer v2 = map.remove("林志颖");
System.out.println("v2:" + v2);
System.out.println(map);
}
private static void show01() {
Map<String, String> map = new HashMap<String, String>();
String v1 = map.put("李晨", "范冰冰1");
System.out.println("v1:" + v1);
String v2 = map.put("李晨", "范冰冰2");
System.out.println("v2:" + v2);
System.out.println(map);
map.put("冷锋", "龙小云");
map.put("杨过", "小龙女");
map.put("尹志平", "小龙女");
System.out.println(map);
}
}
HashMap存储自定义类型键值
Map集合保证key是唯一的:作为key的元素必须重写hashCode 方法和equals 方法 以保证key唯一
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class HashMapSavePersonClass {
public static void main(String[] args) {
show01();
show02();
}
private static void show02() {
HashMap<Person,String> map = new HashMap<>();
map.put(new Person("女王", 18),"英国");
map.put(new Person("秦始皇", 18),"秦国");
map.put(new Person("普京", 30),"俄罗斯");
map.put(new Person("女王", 18),"毛里求斯");
Set<Map.Entry<Person, String>> set = map.entrySet();
for (Map.Entry<Person, String> entry : set) {
Person keyPerson = entry.getKey();
String valueString = entry.getValue();
System.out.println(keyPerson + "-->" + valueString);
}
}
private static void show01() {
HashMap<String, Person> map = new HashMap<String, Person>();
map.put("北京", new Person("张三",18));
map.put("上海", new Person("李四",19));
map.put("广州", new Person("王五",20));
map.put("北京", new Person("赵六",18));
Set<String> set = map.keySet();
for (String key : set) {
Person valuePerson = map.get(key);
System.out.println(key + "-->" + valuePerson);
}
}
}
public class Person {
private String nameString ;
private int age;
public String getNameString() {
return nameString;
}
public void setNameString(String nameString) {
this.nameString = nameString;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person(String nameString, int age) {
super();
this.nameString = nameString;
this.age = age;
}
public Person() {
super();
}
@Override
public String toString() {
return "Person [nameString=" + nameString + ", age=" + age + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((nameString == null) ? 0 : nameString.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
Person other = (Person) obj;
if (age != other.age) return false;
if (nameString == null) {
if (other.nameString != null) return false;
} else if (!nameString.equals(other.nameString)) return false;
return true;
}
}
java.util.LinkedHashMap
一、java.util.LinkedHashMap<K,V> entends HashMap<K,V> 二、Map接口的哈希表和链接列表实现 具有可预知的迭代顺序 三、底层原理:哈希值+链表(记录元素的顺序)
import java.util.HashMap;
import java.util.LinkedHashMap;
public class LinkedHashMapClass {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
map.put("a","a");
map.put("c","c");
map.put("b","b");
map.put("a","d");
System.out.println(map);
LinkedHashMap<String, String> link = new LinkedHashMap<>();
link.put("a","a");
link.put("c","c");
link.put("b","b");
link.put("a","d");
System.out.println(link);
}
}
Debug
一、Debug调试过程:可以让代码逐行执行 查看代码执行的过程 调试程序中出现的bug 二、使用方式:
- 在行号的左边 鼠标左键双击 添加断点(建议初学者添加到每个方法的第一行 以后熟悉了,就哪里有bug添加到哪里),然后右键选择Debug执行程序 程序就会停留在添加的第一个断点处
- 执行程序:
(1)step over :逐行执行程序 (2)step into :进入方法中 (3)step out :跳出方法 (4)resume program :跳到下一个断点 如果没有下一个断点 那么就结束程序 (5)stop :退出debug模式 停止程序 (6)console :切换到控制台
public class Debug {
public static void main(String[] args) {
int a = 10;
int b = 20;
int sum = a + b;
System.out.println(sum);
}
}
jdk9新特性
一、List接口,set接口,map接口:里面增加了一个静态的方法of 可以给集合一次性添加多个元素:static <E> List<E> of (E...elements) 使用前提:当集合中存储的元素个数已经确定了 不再改变时使用
【注】:
of 方法只适用于List 接口,set 接口,map 接口 不适用于接口的实现类of 方法的返回值是一个不能改变的集合 集合不能再使用add put 方法添加元素 会抛出异常Set 接口和Map 接口在调用of 方法的时候 不能有重复的元素 否则会抛出异常
import java.util.List;
import java.util.Map;
import java.util.Set;
public class JDK9 {
public static void main(String[] args) {
List<String> list = List.of("a","b","a","c","d");
System.out.println(list);
list.add("w");
Set<String> set = Set.of("a","b","a","c","d");
System.out.println(set);
}
}
|