集合
- 集合分类
- Collection
- List
- ArrayList
- LinkedList
- LinkedList特有功能
- public void addFirst(E e) 在该列表开头插入指定的元素
- public void addLast(E e) 将指定的元素追加到此列表的末尾
- public E getFirst() 返回此列表中的第一个元素
- public E getLast() 返回此列表中的最后一个元素
- public int lastIndexOf(Object o)返回此列表中最后出现的指定元素的索引,如果此列表中不包含该元素,则返回-1
- public ListIterator< E > listIterator(int index)返回此列表中的元素的列表迭代器(按适当顺序),从列表中指定位置开始
- public E removeFirst 从列表中删除并返回第一个元素
- public E removeLast 从此列表中删除并返回最后一个元素
- Set
- Map
- 补充
- 图片示例
集合分类
集合分为Collection和Map(接口)
Collection:单列集合,每次添加一个元素
Map:键值对集合,每次添加一对元素(key–>value)
Colection分为List和Set(类)
List:[有序,不唯一]
Set:[无序,唯一]
List集合分为
ArrayList
LinkedList(链表结构)
Set集合分为
HashSet(哈希表结构)
LinkedHashSet(链表、哈希表结构)
TreeSet(树结构)
Map集合分为HashMap和TreeMap(类)
HashMap(哈希表结构)
LinkedHashMap(链表、哈希表结构)
TreeMap(树结构)
Collection
Collection是最基本集合接口,它定义了一组允许重复的对象。Collection接口派生了两个子接口Set和List
Collection接口中的方法是集合中所有实现类必须拥有的方法
- boolean add(Object e):向集合中添加一个元素
- boolean remove(Object o):删除该集合中指定的元素元素
- void clear():清空集合
- boolean contains(Object o):判断集合中是否包含某个元素
- boolean isEmpty():判断集合是否为空
- int size():获取集合内元素的数量
boolean add(Object e):向集合中添加一个元素
public class Test {
public static void main(String[] args) {
//因为Collection是接口,所以调用其具体实现类
Collection c = new ArrayList();
c.add("a");
c.add("b");
c.add("c");
c.add("d");
System.out.println(c);
//输出:[a, b, c, d]
}
}
boolean remove(Object o):删除该集合中指定的元素元素
public class Test {
public static void main(String[] args) {
//因为Collection是接口,所以调用其具体实现类
Collection c = new ArrayList();
c.add("a");
c.add("b");
c.add("c");
c.add("d");
c.remove("c");
c.remove("d");
System.out.println(c);
//输出:[a, b]
}
}
void clear():清空集合
public class Test {
public static void main(String[] args) {
//因为Collection是接口,所以调用其具体实现类
Collection c = new ArrayList();
c.add("a");
c.add("b");
c.add("c");
c.add("d");
c.clear();
System.out.println(c);
//输出:[]
}
}
boolean contains(Object o):判断集合中是否包含某个元素
public static void main(String[] args) {
//因为Collection是接口,所以调用其具体实现类
Collection c = new ArrayList();
c.add("a");
c.add("b");
c.add("c");
c.add("d");
System.out.println(c.contains("c"));
//输出:true
}
}
boolean isEmpty():判断集合是否为空
public class Test {
public static void main(String[] args) {
//因为Collection是接口,所以调用其具体实现类
Collection c = new ArrayList();
c.add("a");
c.add("b");
c.add("c");
c.add("d");
System.out.println(c.isEmpty());
//输出:false
}
}
int size():获取集合内元素的数量
public static void main(String[] args) {
//因为Collection是接口,所以调用其具体实现类
Collection c = new ArrayList();
c.add("a");
c.add("b");
c.add("c");
c.add("d");
System.out.println(c.size());
//输出:4
}
}
Collection遍历方式
Collection遍历方式
1.将结合转成数组
2.使用迭代器
3.增强for
使用数组toArray():返回包含此Collection中所有元素的数组
使用toArray()方法,将集合转成数组,之后使用for循环遍历数组取值即可
public class Test {
public static void main(String[] args) {
Collection list = new ArrayList();
list.add("张三");
list.add("李四");
list.add("王五");
list.add("赵六");
list.add(10);// 自动装箱int-->Integer
list.add(3.14);// 自动装箱double-->Double
// 获取Object[]数组
Object[] objArray = list.toArray();
for (int i = 0; i < objArray.length; i++) {
System.out.println(objArray[i]);
}
}
}
结果:
张三
李四
王五
赵六
10
3.14
迭代器Iterator
在Java中,有很多的数据容器,对于这些的操作有很多的共性。Java采用了迭代器来为各种容器提供了公共的操作接口。这样使得对容器的遍历操作与其具体的底层实现相隔离,达到解耦的效果。
迭代器中的方法:
- hasNext():如果任有元素可以迭代,则返回true
- next():返回迭代的下一个元素
- remove():从迭代器指向的collection中移除迭代器返回的最后一个元素
Collection获取迭代器方法
iterator():返回在此collection的元素上进行迭代的迭代器
public class Test {
public static void main(String[] args) {
Collection list = new ArrayList();
list.add("张三");
list.add("李四");
list.add("王五");
list.add("赵六");
list.add(10);// 自动装箱int-->Integer
list.add(3.14);// 自动装箱double-->Double
//使用迭代器
Iterator it = list.iterator();
while (it.hasNext()) {//如果有下一个元素,那么继续取值
System.out.println(it.next());
}
}
}
结果:
张三
李四
王五
赵六
10
3.14
增强for
foreach语句是for语句的特殊简化版本,但是foreach语句并不能完全取代for语句,然而,任何的foreach语句都可以改写为for语句版本
foreach的语句格式:
for(元素类型 元素变量名 : 遍历对象){
使用元素变量进行操作
}
优点:
1.简化编程操作
2.提高代码安全性(越界异常)
缺点:
1.无法对索引进行操作
public class Test {
public static void main(String[] args) {
Collection list = new ArrayList();
list.add("张三");
list.add("李四");
list.add("王五");
list.add("赵六");
// 增强for
for (Object o : list) {
System.out.println(o);
}
}
}
结果:
张三
李四
王五
赵六
List
List所代表的是有序的Collection,即它用某种特定的插入顺序来"维护元素顺序"。
List是有序集合(序列),用户可以精准控制列表中每个元素的插入位置,用户可以通过整数索引访问元素,并搜索列表中的元素
与Set不同,列表通常允许重复的元素
继承至Collection< E >
List集合特点
有序:存储和取出的元素顺序一致
可重复:存储的元素可以重复
List功能
继承自Collection的功能
List特有功能
- void add(int index, E element):在列表的指定位置插入指定的元素
- E get(int index):返回列表中指定位置的元素
- E remove(int index):移除列表中指定位置的元素
- E set(int index, E element):用指定元素替换列表中指定位置的元素
void add(int index, E element):在列表的指定位置插入指定的元素
public class Test {
public static void main(String[] args) {
// 实例化一个List
List list = new ArrayList();
// 填充集合
list.add("aaa");// Collection
list.add("bbb");// Collection
list.add("ccc");// Collection
System.out.println(list);
//输出:[aaa, bbb, ccc]
}
}
E get(int index):返回列表中指定位置的元素
public class Test {
public static void main(String[] args) {
// 实例化一个List
List list = new ArrayList();
// 填充集合
list.add("aaa");// Collection
list.add("bbb");// Collection
list.add("ccc");// Collection
System.out.println(list.get(2));
//输出:ccc
}
}
E remove(int index):移除列表中指定位置的元素
public class Test {
public static void main(String[] args) {
// 实例化一个List
List list = new ArrayList();
// 填充集合
list.add("aaa");// Collection
list.add("bbb");// Collection
list.add("ccc");// Collection
list.remove(2);
System.out.println(list);
//输出:[aaa, bbb]
}
}
E set(int index, E element):用指定元素替换列表中指定位置的元素
public class Test {
public static void main(String[] args) {
// 实例化一个List
List list = new ArrayList();
// 填充集合
list.add("aaa");// Collection
list.add("bbb");// Collection
list.add("ccc");// Collection
list.set(2,"ddd");
System.out.println(list);
//输出:[aaa, bbb, ddd]
}
}
List的特有遍历功能
List基础自Collection接口,所以也继承了父接口的两个变量方式
1.toArray() 转数组
2.iterator() 迭代器
除此之外,List还新增了一种迭代器方式
ListIterator listIterator()
ListIterator是Iterator的子接口;它继承了Iterator的所有方法;
另外还新增了几个方法:
1.E previous():返回列表中的前一个元素;
2.boolean hasPrevious():判断是否有前一个元素;
特点
1.Iterator:单项的,只能向下遍历;
2.ListIterator:双向的;可以向下遍历,也可以向上遍历;
注意:ListIterator只适用于List集合
演示:
public class Test {
public static void main(String[] args) {
// 1.实例化一个集合
List list = new ArrayList();
// 2.填充集合
list.add(new Student("张三", 20));
list.add(new Student("李四", 22));
list.add(new Student("王五", 24));
list.add(new Student("赵六", 26));
// 3.遍历集合
// 遍历方式一:ListIterator()
System.out.println("向后遍历:");
ListIterator listIt = list.listIterator();
while (listIt.hasNext()) {
System.out.println(listIt.next());
}
System.out.println("向前遍历:");
while (listIt.hasPrevious()) {
System.out.println(listIt.previous());
}
System.out.println("-----------------------");
// 遍历方式二:结合Collection的size(),和本接口的get(int index)方法,可以使用for循环遍历;
System.out.println("size()、get() 配合for循环");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
class Student {
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
结果:
向后遍历:
Student{name='张三', age=20}
Student{name='李四', age=22}
Student{name='王五', age=24}
Student{name='赵六', age=26}
向前遍历:
Student{name='赵六', age=26}
Student{name='王五', age=24}
Student{name='李四', age=22}
Student{name='张三', age=20}
-----------------------
size()、get() 配合for循环
Student{name='张三', age=20}
Student{name='李四', age=22}
Student{name='王五', age=24}
Student{name='赵六', age=26}
ArrayList
ArrayList是List接口的一个实现类,它是程序中最常见的一种集合,内部封装了一个长度可变的数组对象,当存入的元素超过数组长度时,ArrayList会在内存中分配一个更大的数组来存储这些元素。因此ArrayList集合看做一个长度可变的数组
特点:
1.底层数据结构是数组,随机访问速度快(根据索引访问),插入和移除较慢。
2.插入的数据有顺序,元素可以重复
3.线程不安全,效率高。
为什么效率低?
1.数组结构的优点是便于对集合进行快速的随机访问,如果经常需要根据索引位置访问集合中的对象,使用由ArrayList类实现的List集合的效率较好。
2.数组结构的缺点:在向指定索引位置插入、删除指定对象效率较慢,如果经常需要向List集合的指定索引位置插入或删除对象,那么使用由ArrayList类实现的List集合的效率则较低,并且插入、删除对象的索引位置越小效率越低,原因是当向指定的索引位置插入对象时,会同时将指定索引位置及之后的所有对象相应的向后移动一位。
ArrayList创建集合
1.ArrayList list = new ArrayList();
2.ArrayList<泛型> list = new ArrayList<>();
泛型在后
ArrayList list = new ArrayList(int 数组容量);
默认开辟10块空间
集合无论底层开辟多少块空间 都可以装无数个元素
集合会自动的扩容
ArrayList用法
与List用法一样
LinkedList
ArrayList 集合在查询元素时速度很快,但在删除时效率较低,为了克服这种局限性,可以使用LinkedList。
LinkedList内部维护了一个双向循环链表,链表中的每一个元素彼此相连起来。插入和删除元素只需要修改元素之间的应用关系即可。
特点:
1.增删效率很高
2.查询效率低
LinkedList特有功能
因为是链表结构,所以LinkedList提供了一些首尾操作的方法
- public void addFirst(E e) 在该列表开头插入指定的元素
- public void addLast(E e) 将指定的元素追加到此列表的末尾
- public E getFirst() 返回此列表中的第一个元素
- public E getLast() 返回此列表中的最后一个元素
- public int lastIndexOf(Object o)返回此列表中最后出现的指定元素的索引,如果此列表中不包含该元素,则返回-1
- public ListIterator< E > listIterator(int index)返回此列表中的元素的列表迭代器(按适当顺序),从列表中指定位置开始
- public E removeFirst 从列表中删除并返回第一个元素
- public E removeLast 从此列表中删除并返回最后一个元素
public void addFirst(E e) 在该列表开头插入指定的元素
public class Test {
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
//[a, b, c, d]
list.addFirst("5");
System.out.println(list);
//输出:[5, a, b, c, d]
}
}
public void addLast(E e) 将指定的元素追加到此列表的末尾
public class Test {
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
//[a, b, c, d]
list.addLast("5");
System.out.println(list);
//输出:[a, b, c, d, 5]
}
}
public E getFirst() 返回此列表中的第一个元素
public class Test {
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
//[a, b, c, d]
System.out.println(list.getFirst());
//输出:a
}
}
public E getLast() 返回此列表中的最后一个元素
public class Test {
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
//[a, b, c, d]
System.out.println(list.getLast());
//输出:d
}
}
public int lastIndexOf(Object o)返回此列表中最后出现的指定元素的索引,如果此列表中不包含该元素,则返回-1
public class Test {
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("d");
list.add("c");
list.add("b");
list.add("a");
//[a, b, c, d, d , c , b , a]
System.out.println(list.lastIndexOf("c"));
System.out.println(list.lastIndexOf("e"));
//输出:5
// -1
}
}
public ListIterator< E > listIterator(int index)返回此列表中的元素的列表迭代器(按适当顺序),从列表中指定位置开始
public ListIterator<E> listIterator() {
return new ListItr(0);
}
public E removeFirst 从列表中删除并返回第一个元素
public class Test {
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
//[a, b, c, d]
System.out.println(list.removeFirst());
System.out.println(list);
//输出:a
//输出:[b, c, d]
}
}
public E removeLast 从此列表中删除并返回最后一个元素
public class Test {
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
//[a, b, c, d]
System.out.println(list.removeLast());
System.out.println(list);
//输出:d
//输出:[a, b, c]
}
}
Set
Set 具有与 Collection 完全一样的接口,只是行为上不同,Set 不保存重复的元素,而且Set接口中元素无序。
Set集合特点
不包含重复元素的集合
没有带索引的方法,所以不能使用普通for循环遍历
public class Test {
public static void main(String[] args) {
// 实例化一个Set对象
Collection<String> set = new HashSet<>();
// 填充元素
set.add("aaa");
set.add("bbb");
set.add("ccc");
set.add("ddd");
// 存储重复元素
String s1 = new String("ccc");
String s2 = new String("ddd");
set.add(s1);// 添加失败
set.add(s2);// 添加失败
set.add("eee");
// 遍历
// 1.toArray()
// 2.iterator();
// 3.增强for;
for (String s : set) {
System.out.println(s);// 取出时,跟存入的顺序不同;
}
}
}
结果:
aaa
ccc
bbb
eee
ddd
HashSet
对集合的迭代顺序不作任何保证
HashSet存储自定义对象
当向HashSet集合中添加一个对象时,首先会调用该对象的hashCode() 方法来确定元素的存储位置,然后再调用对象的equals() 方法来确定该位置有没有重复元素。
特点:不包含重复元素,自定义的类必须重写hashCode、equals方法
IDEA可以用快捷键Alt + insert 快速生成
public class Test {
public static void main(String[] args) {
HashSet<String> hs = new HashSet<String>();
hs.add("hello");
hs.add("world");
hs.add("java");
//添加重复项
hs.add("java");
//遍历取出,会和存放顺序不同
for (String s : hs) {
System.out.println(s);
}
}
}
结果:
world
java
hello
public class Test {
public static void main(String[] args) {
// 实例化一个Set
Set<Student> stuSet = new HashSet<>();
// 填充元素
stuSet.add(new Student("张三", 20));
stuSet.add(new Student("李四", 22));
stuSet.add(new Student("王五", 24));
stuSet.add(new Student("赵六", 26));
// 添加重复元素
stuSet.add(new Student("赵六", 26));
// 遍历
for (Student stu : stuSet) {
System.out.println(stu.name + " " + stu.age);// 取出时,跟存入的顺序不同;
}
}
}
class Student {
String name;
int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.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;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
结果:
李四 22
王五 24
张三 20
赵六 26
LinkedHashSet
LinkedHashSet是一个特例集合,元素存取有序,使用链表维护元素的顺序,这样使得元素看起来是以插入的顺序保存的,取出时会按照元素的添加顺序来访问集合里的元素。
public class Test {
public static void main(String[] args) {
// 实例化一个LinkedHashSet
LinkedHashSet<String> strSet = new LinkedHashSet<>();
// 填充集合
strSet.add("张三");
strSet.add("李四");
strSet.add("王五");
strSet.add("赵六");
// 遍历
for (String s : strSet) {
System.out.println(s);// 存取有序的;
}
}
}
结果:
张三
李四
王五
赵六
TreeSet
TreeSet因为实现了Set接口,所以其集合特点是:元素不重复。TreeSet 里面存放的元素是按照元素的自然顺序来排列的
public class Test {
public static void main(String[] args) {
// 实例化一个TreeSet
TreeSet<String> strSet = new TreeSet<>();
// 填充集合
strSet.add("ddd");
strSet.add("aaa");
strSet.add("xxx");
strSet.add("ccc");
strSet.add("bbb");
strSet.add("yyy");
strSet.add("fff");
// 遍历
for(String s : strSet){
System.out.println(s);
}
}
}
结果:
aaa
bbb
ccc
ddd
fff
xxx
yyy
自然排序原因:
TreeSet内部采用平衡二叉树来存储元素,这样的结构可以保证TreeSet集合中没有重复的元素,并且可以对元素进行排序,其保证元素唯一性是通过元素的compareTo方法的返回值来确定
自然排序(Comparable)接口
TreeSet在进行数据存储时,都会对该对象进行排序,但是集合不知道该以什么样的方式进行排序,所以自定义类中需要实现Comparable接口,并在compareTo方法内实现排序比较。该方法用来比较元素的大小关系,然后将集合元素按照升序排列。
compareTo(T o) 方法
将此对象与指定的对象进行比较,以返回一个负整数、零或一个正整数,因为这个对象小于、等于或大于指定的对象。
参数 : o要比较的对象。
结果 : 一个负整数,零,或一个正整数,因为这个对象小于,等于,或大于指定的对象。
注意:只要放在TreeSet中的元素对象,在该对象的类中必须实现Comparable接口,必须覆盖该接口中的compareTo()方法,并在该方法中编写比较规则。
public class Test {
public static void main(String[] args) {
TreeSet<Student> stuSet = new TreeSet<>();
stuSet.add(new Student("张三", 20));// TreeSet的add()方法中,自动调用元素的compareTo()方法
stuSet.add(new Student("李四", 22));
stuSet.add(new Student("王五", 24));
stuSet.add(new Student("赵六", 22));
stuSet.add(new Student("赵六", 25));
// 遍历
for (Student stu : stuSet) {
System.out.println(stu.name + "," + stu.age);
}
}
}
//学生类
class Student implements Comparable<Student> {
String name;
int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public int compareTo(Student o) {
// System.out.println("this.name = " + this.name + " o.name = " + o.name);
//1.先按姓名排序
int n1 = this.name.compareTo(o.name);
//2.如果姓名相同,按年龄排序
int n2 = (n1 == 0 ? this.age - o.age : n1);
return n2;
}
}
结果:
张三,20
李四,22
王五,24
赵六,22
赵六,25
Comparator通过构造方法传入排序比较器
比较器排序,也叫定制排序通过使用该比较器,比较时需要创建第三方类,实现Comparator接口,并且覆盖其中的compare()方法,编写比较规则和排序方式。
方法int compare(T o1, T o2)
比较其两个顺序的参数。返回一个负整数、零或一个正整数作为第一个参数小于、等于或大于第二个参数。
比较器通过构造方法传入
public class Test {
public static void main(String[] args) {
// 实例化一个TreeSet
TreeSet<Student> stuSet = new TreeSet<>(new MyComparator());
stuSet.add(new Student("李四", 20));
stuSet.add(new Student("张三", 22));
stuSet.add(new Student("张三", 26));
stuSet.add(new Student("王五", 24));
for (Student stu : stuSet) {
System.out.println(stu.name + "," + stu.age);
}
}
}
//排序比较器
class MyComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
// System.out.println("o1.name = " + o1.name + " o2.name = " + o2.name);
// 先按姓名比
int n1 = o1.name.compareTo(o2.name);
// 如果姓名相同,比较年龄
int n2 = (n1 == 0 ? o1.age - o2.age : n1);
return n2;
}
}
//学生类
class Student {
String name;
int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
结果:
张三,22
张三,26
李四,20
王五,24
总结
1.当向TreeSet里面添加不是自定义对象时,元素会默认使用自然排序输出从小到大序列。(因为Java提供的对象实现了比较的接口)
2.当向TreeSet里面添加自定义的对象时,可以使用这2种比较方式进行比较,效果上没有区别。
Map
Map接口概述
Map接口是双列集合,它的每个元素都包含一个键对象(key)和一个值对象(value),对象之间存在一种对应关系,称为映射。
访问元素时,只要指定了key就能找到对应的value。
将键映射到值的对象,一个映射不能包含重复的键,每个键最多指引映射到一个值
取出顺序和存储顺序相反
Map 子类
HashMap:哈希表结构
LinkedHashMap:链表、哈希表结构
TreeMap:树结构
以上Map接口的各种"数据结构"全部是针对"键"有效,跟"值"无关;
HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
LinkedHashMap<K,V>:HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
TreeMap<K,V>:二叉树原理保证键唯一,键是可按照指定顺序排序的
注意:Map接口中的集合都有两个泛型变量<K,V>,在使用时,要为两个泛型变量赋予数据类型。两个泛型变量<K,V>的数据类型可以相同,也可以不同。
Map基本功能
- V put(K Key,V value) 添加元素
- V remove(Object key) 根据键删除键值对元素
- void clear() 移除所有的键值对元素
- boolean containsKey(Object key) 判断集合是否包含指定的键
- boolean containsValue(Object value) 判断集合是否包含指定的值
- boolean isEmpty() 判断集合是否为空
- int size() 集合的长度,也就是集合中键值对的个数
V put(K Key,V value) 添加元素
public class Test {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
System.out.println(map);
}
}
结果:
{杨过=小龙女, 郭靖=黄蓉, 张无忌=赵敏}
V remove(Object key) 根据键删除键值对元素
public class Test {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
System.out.println(map.remove("郭靖"));
System.out.println(map.remove("郭襄"));//null
System.out.println(map);
}
}
结果:
黄蓉
null
{杨过=小龙女, 张无忌=赵敏}
void clear() 移除所有的键值对元素
public class Test {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
map.clear();
System.out.println(map);
}
}
结果:
{}
boolean containsKey(Object key) 判断集合是否包含指定的键
public class Test {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
System.out.println(map.containsKey("杨过"));
System.out.println(map.containsKey("杨康"));//false
}
}
结果:
true
false
boolean containsValue(Object value) 判断集合是否包含指定的值
public class Test {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
System.out.println(map.containsValue("小龙女"));
System.out.println(map.containsValue("周芷若"));//false
}
}
结果:
true
false
boolean isEmpty() 判断集合是否为空
public class Test {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
System.out.println(map.isEmpty());
}
}
结果:
false
nt size() 集合的长度,也就是集合中键值对的个数
public class Test {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
System.out.println(map.size());;
}
}
结果:
3
Map集合获取功能
- V get(Object key) 根据键获取值
- Set < K > keySet() 获取所有键的集合
- Collection < V > values 获取所有值的集合
V get(Object key) 根据键获取值
public class Test {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
System.out.println(map.get("张无忌"));
System.out.println(map.get("张三丰"));//null
}
}
结果:
赵敏
null
SetkeySet() 获取所有键的集合
public class Test {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
Set<String> keySet = map.keySet();
// System.out.println(keySet);
for (String key:keySet){
System.out.println(key);
}
}
}
结果:
杨过
郭靖
张无忌
Collectionvalues 获取所有值的集合
public class Test {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
Collection<String> values = map.values();
for (String s : values) {
System.out.println(s);
}
}
}
结果:
小龙女
黄蓉
赵敏
Entry键值对对象
在Map类设计时,提供了一个嵌套接口:Entry。
Entry将键值对的对应关系封装成了对象。即键值对对象,这样我们在遍历Map集合时,就可以从每一个键值对(Entry)对象中获取对应的键与对应的值。
Entry是Map接口中提供的一个静态内部嵌套接口。
getKey()方法:获取Entry对象中的键
K getKey() :返回与此项对应的键
getValue()方法:获取Entry对象中的值
V getValue() :返回与此项对应的值
entrySet()方法:用于返回Map集合中所有的键值对(Entry)对象,以Set集合形式返回。
(Set<Map.Entry<K.V>> entrySet() :返回此映射中包含此映射关系的set视图
Entry应用:Map集合遍历键值对方式
键值对方式:即通过集合中每个键值对(Entry)对象,获取键值对(Entry)对象中的键与值。
操作步骤:
1.获取Map集合中,所有的键值对(Entry)对象,以Set集合形式返回。
2.遍历包含键值对(Entry)对象的Set集合,得到每一个键值对(Entry)对象
3.通过键值对(Entry)对象,获取Entry对象中的键与值。
案例:
public class Test {
public static void main(String[] args) {
// 创建Map对象
Map<String, String> map = new HashMap<String, String>();
// 给map中添加元素
map.put("111", "王五");
map.put("112", "李四");
map.put("113", "赵六");
map.put("114", "田七");
//获取Map中的所有key与value的对应关系
Set<Map.Entry<String,String>> entrySet = map.entrySet();
//遍历Set集合
Iterator<Map.Entry<String,String>> it =entrySet.iterator();
while(it.hasNext()){
//得到每一对对应关系
Map.Entry<String,String> entry = it.next();
//通过每一对对应关系获取对应的key
String key = entry.getKey();
//通过每一对对应关系获取对应的value
String value = entry.getValue();
System.out.println(key+"="+value);
}
//获取键值对集合
//Set<Entry<String, String>> entrySet = map.entrySet();
//增强for循环输出键值对
//for (Entry<String, String> entry : entrySet) {
// System.out.println(entry.getKey() + " = " + entry.getValue());
//}
}
}
结果:
111=王五
112=李四
113=赵六
114=田七
注意:Map集合不能直接使用迭代器或者foreach进行遍历。但是转成Set之后就可以使用了。
HashMap
HashMap方法
参上Map方法
HashMap存储自定义类型键值
案例:
/**
* 练习:每位学生(姓名,年龄)都有自己的家庭住址。那么,既然有对应关系,则将学生对象和家庭住址存储到map集合中。学生作为键, 家庭住址作为值。
* 注意,学生姓名相同并且年龄相同视为同一名学生。
*/
public class Test {
public static void main(String[] args) {
// 1. 创建hashmap集合对象。
Map<Student, String> map = new HashMap<Student, String>();
// 2. 添加元素。
map.put(new Student("李四", 28), "上海");
map.put(new Student("王五", 22), "北京");
map.put(new Student("赵六", 24), "成都");
map.put(new Student("田七", 25), "广州");
map.put(new Student("张三", 22), "南京");
// 3. 取出元素。键找值方式
Set<Student> keySet = map.keySet();
for (Student key : keySet) {
String value = map.get(key);
System.out.println(key.toString() + "....." + value);
}
System.out.println("---------------------------");
// 3. 取出元素。键值对方式
Set<Map.Entry<Student, String>> entrySet = map.entrySet();
for (Map.Entry<Student, String> entry : entrySet) {
Student key = entry.getKey();
String value = entry.getValue();
System.out.println(key.toString() + "....." + value);
}
}
}
class Student {
private String name;
private int age;
public Student(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 String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
结果:
Student{name='赵六', age=24}.....成都
Student{name='李四', age=28}.....上海
Student{name='田七', age=25}.....广州
Student{name='王五', age=22}.....北京
Student{name='张三', age=22}.....南京
---------------------------
Student{name='赵六', age=24}.....成都
Student{name='李四', age=28}.....上海
Student{name='田七', age=25}.....广州
Student{name='王五', age=22}.....北京
Student{name='张三', age=22}.....南京
当给HashMap中存放自定义对象时,如果自定义对象作为key存在,这时要保证对象唯一,必须复写对象的hashCode和equals方法(如果忘记,请回顾HashSet存放自定义对象)。
如果要保证map中存放的key和取出的顺序一致,可以使用LinkedHashMap集合来存放。
LinkedHashMap
保证map中存放的key和取出的顺序一致
public class Test {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new LinkedHashMap<>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
System.out.println(map);
}
}
结果:
{张无忌=赵敏, 郭靖=黄蓉, 杨过=小龙女}
TreeMap
二叉树原理保证键唯一,键是可按照指定顺序排序的
1.使用TreeMap会对"键"进行排序;
2.所以,作为"键"的对象,必须实现两种比较的方式之一
public class Test {
public static void main(String[] args) {
//1.实例化一个TreeMap
TreeMap<String,String> map = new TreeMap<>();
//2.填充集合
map.put("004", "张三");
map.put("002", "李四");
map.put("001", "王五");
map.put("003", "赵六");
//3.遍历
Set<String> keys = map.keySet();
for(String key : keys){
System.out.println(key + "," + map.get(key));
}
}
}
结果:
001,王五
002,李四
003,赵六
004,张三
补充
泛型
泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数类型。
泛型类
定义泛型类在"类名"后添加一对<>,里面定义"泛型名称";
格式:如ArrayList集合
class ArrayList< E > {
public boolean add(E e) { }
public E get(int index) { }
}
使用泛型类:创建对象时,确定泛型的类型
例如:ArrayList< String > list = new ArrayList< String >(); 此时相当于将E的类型替换为String类型。
例如:ArrayList< Integer > list = new ArrayList< Integer >(); 此时相当于将E的类型替换为Integer类型。
/*
泛型类定义格式
格式:修饰符 class 类名<类型> {}
范例:public class Generic<T>{}
此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
*/
public class Test {
public static void main(String[] args) {
Student s = new Student();
s.setName("张三");
System.out.println(s.getName());
Teacher t = new Teacher();
t.setAge(30);
// t.setAge("30");
System.out.println(t.getAge());
System.out.println("--------------");
Generic<String>g1=new Generic<String>();
g1.setT("张三");
System.out.println(g1.getT());
Generic<Integer>g2=new Generic<Integer>();
g2.setT(18);
System.out.println(g2.getT());
}
}
class Student {
private String name;
public Student() {
}
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Teacher {
private Integer age;
public Teacher() {
}
public Teacher(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
class Generic<T>{
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
结果:
张三
30
--------------
赵四
18
泛型方法
方法格式:
/**
* <T> 声明此方法持有一个类型T,也可以理解此方法为泛型方法
* T 返回值类型(入参类型是什么类型此返回值就是什么类型)
* @param t1 泛型参数
* @param t2 泛型参数
*/
public static <T> T m1(T t1) {
return t1;
}
详细介绍
/**
* 泛型方法的详细介绍
* @param value 传入的泛型实参
* @return T 返回值为T类型
* 说明:
* 1. public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
* 2. 只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
* 3. <T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
* 4. 与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
*/
public <T> T genericMethod(T value)throws InstantiationException ,
IllegalAccessException{
return value;
}
示例:
/*
泛型方法定义格式:
格式:修饰符 <类型> 返回值类型 方法名(类型 变量名){}
范例:public <T> void show(T t){}
*/
public class Test {
public static void main(String[] args) {
// Generic g = new Generic();
// g.show("张三");
// g.show(18);
// g.show(true);
// g.show(13.14);
// Generic<String> g1 = new Generic<String>();
// g1.show("赵四");
// Generic<Integer> g2 = new Generic<Integer>();
// g2.show(18);
// Generic<Boolean> g3 = new Generic<Boolean>();
// g3.show(true);
Generic g=new Generic();
g.show("张三");//String
g.show(18);//int
g.show(true);//boolean
g.show(13.14);//double
}
}
//class Generic {
// public void show(String s) {
// System.out.println(s);
// }
//
// public void show(Integer i) {
// System.out.println(i);
// }
//
// public void show(Boolean b) {
// System.out.println(b);
// }
//}
//泛型类改进
//class Generic<T> {
// public void show(T t) {
// System.out.println(t);
// }
//}
//泛型方法改进
class Generic {
public <T> void show(T t) {
System.out.println(t);
}
}
结果:
张三
18
true
13.14
泛型接口
接口格式
泛型不仅可以声明泛型类,也可以声明泛型接口,声明泛型接口和声明泛型类的语法相似,也是在接口名称后面加< T >
格式:权限修饰符 interface 接口名<泛型标示符>{}
/*
泛型接口定义格式:
格式:修饰符 interface 接口名<类型>{}
范例:public interface Generic<T>{}
*/
public class Test {
public static void main(String[] args) {
Generic<String> g1 = new GenericTmpl<String>();
g1.show("张三");
Generic<Integer> g2 = new GenericTmpl<Integer>();
g2.show(18);
Generic<Boolean> g3 = new GenericTmpl<Boolean>();
g3.show(true);
}
}
interface Generic<T> {
public void show(T t);
}
class GenericTmpl<T> implements Generic<T> {
@Override
public void show(T t) {
System.out.println(t);
}
}
泛型接口实现方式
方式一:
子类继续定义泛型类型,通过使用者来确定具体类型
然后使用泛型接口
public class Test {
public static void main(String[] args) {
Fan<String> i = null; // 声明接口对象
i = new Zi<String>("李四"); // 通过子类实例化对象
System.out.println("内容:" + i.getVal());
}
}
interface Fan<T> { // 在接口上定义泛型
public T getVal(); // 定义抽象方法,抽象方法的返回值就是泛型类型
}
//子类继续定义泛型类型,有使用者来确定
class Zi<T> implements Fan<T> { // 定义泛型接口的子类
private T val; // 定义属性
public Zi(T val) { // 通过构造方法设置属性内容
this.setVal(val);
}
public void setVal(T val) {//通过方法参数确定
this.val = val;
}
public T getVal() {
return this.val;
}
}
方式二:
子类确定泛型类型
interface Info<T> { // 在接口上定义泛型
public T getVar(); // 定义抽象方法,抽象方法的返回值就是泛型类型
}
class InfoImpl implements Info<String> { // 子类确定泛型类型
@Override
public String getVar() {//因为在类里面已经确定了泛型类型,所以约束了方法的泛型
return null;
}
}
泛型通配符
类型通配符一般是使用?代替具体的类型参数
例如 List<?> 在逻辑上是List< String >,List< Integer > 等所有List<具体类型实参>的类
通配符的使用
public void show( List < ? > list ){ }:表示可以接收具有"任何泛型"的"List类型"的集合
泛型上限指定
< ? extends E >:只能指向具有E,或者其子类的泛型的对象
public void show( List < ? extends Student > list ){ }:表示只能接收具有"Student"或者"其子类"泛型的集合对象;
泛型下限指定
< ? super E >:只能指向具有E,或者其父类的泛型的对象
还可以看到
public void show( List < ? super Student > stuList ){ } :表示只能接受Student类和其父类的集合对象
public class Test {
public static void main(String[] args) {
List<Student> students = new ArrayList<Student>();
List<Teacher> teachers = new ArrayList<Teacher>();
List<Person> persons = new ArrayList<Person>();
List<String> strings = new ArrayList<String>();
// 可以调用
m1(strings);
m1(strings);
// 因为m2()方法中的参数已经限定了参数泛型上限为Person,
// String类型不在这个范围之内,所以会报错
// m2(strings);
// 类型通配符下限通过形如 List<? super Student>来定义,
// 表示类型只能接受Student及其三层父类类型,如Objec类型的实例。
//m3(teachers);
m3(persons);// 可以通过
}
public static void m1(List<?> data) {
}
public static void m2(List<? extends Person> data) {
}
public static void m3(List<? super Student> stuList) {
}
class Person {
}
class Student extends Person {
}
class Teacher extends Person {
}
}
泛型好处
可以消除代码中的强制类型转换,同时获得一个附加的类型检查层,该检查层可以防止有人将错误类型的键或值保存在集合中。这就是泛型所做的工作。
1.类型安全
2.消除强制类型转换
Collections
Collections概述
Collections是一个集合工具类
Collections方法
public static < T > void sort(List< T > list) // 使用,默认随机源对指定列表进行置换
public static void shuffle(List< ? > list) // 根据元素的自然顺序,对指定列表按升序排序
public static void sort(List list) // 集合元素排序
//排序前元素list集合元素 [33,11,77,55]
Collections.sort( list );
//排序后元素list集合元素 [11,33,55,77]
Collections_sort()方法排序
Collections.sort(objects, new Comparator<TestEntry>() {
@Override
public int compare(TestEntry t1, TestEntry t2) {
//当返回0的时候排序方式是 t1,t2
//当返回1的时候排序方式是 t2,t1
//当返回-1的时候排序方式是 t1,t2
//注意
//返回值大于1效果等同于1
//返回值小于1 效果等同于0,-1
return 0;
}
});
Set集合中使用Comparator
public class Test {
public static void main(String[] args) {
// 实例化一个TreeSet
TreeSet<Student> stuSet = new TreeSet<>(new MyComparator());
stuSet.add(new Student("李四", 20));
stuSet.add(new Student("张三", 22));
stuSet.add(new Student("王五", 24));
System.out.println("-------------------------------");
for (Student stu : stuSet) {
System.out.println(stu.name + "," + stu.age);
}
}
}
//排序比较器
class MyComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
System.out.println("o1.name = " + o1.name + " o2.name = " + o2.name);
// 先按姓名比
int n1 = o1.name.compareTo(o2.name);
// 如果姓名相同,比较年龄
int n2 = (n1 == 0 ? o1.age - o2.age : n1);
return n2;
}
}
//学生类
class Student {
String name;
int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
结果:
o1.name = 李四 o2.name = 李四
o1.name = 张三 o2.name = 李四
o1.name = 王五 o2.name = 李四
-------------------------------
张三,22
李四,20
王五,24
public static void shuffle(List<?> list) // 集合元素存储位置打乱
//list集合元素 [11,33,55,77]
Collections.shuffle( list );
//使用shuffle方法后,集合中的元素为[77,33,11,55],每次执行该方法,集合中存储的元素位置都会随机打乱
图片示例
集合:
链表:
键值对
HashSet存储自定义对象流程
二叉树原理
如存入: 22 20 25 24 19 20 26
1.把第一个数据看成是根节点,从第二个数据开始,每个数据都从根节点开始比较
大于0: 就往右放
小于0: 就往左放
等于0: 说明是重复的元素
2.遍历从根节点开始看,按照左中右方式遍历
二叉树图解:每个节点最多两个子节点
排序二叉树:存储元素会按照大小排序,并能够去除重复元素