这一部分在工作中用的比较多,建议好好看一下。
容器:Java Api所提供的一系列类的实例,用于在程序中存放对象。
JDK所提供的容器API位于java.util包内。
容器API的类图结构如下图所示:
记住几点:1.Collection是一个一个的往里装,Map是一对一对(键值对key-value映射对)的往里装。
2.Collection下的Set接口数据对象没有顺序且不能重复(可以理解为数学中的集合),List中的数据对象有顺序且可以 重复。那怎么判断里面存的对象是否重复呢?若中间存的对象互相equals就算是重复了。
3.HashSet、LinkedList、ArrayList、HashMap都是图中对应接口的实现类。
(1)Collection接口
Collection接口中所定义的方法(方法名比较直观,如果英语还将就一看基本就能猜出是干啥的):
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
void clear();
boolean equals(Object o);
int hashCode();
...
collection方法的基本使用举例
import java.util.ArrayList;
import java.util.Collection;
/**
* 说明:Collection方法应用
*
* @author huayu
* @date 2018/8/31 12:06 PM
*/
public class CollectionDemo {
public static void main(String[] args) {
//这儿想想父类引用指向子类对象是怎么回事来着,在面向对象那篇博客中说过
Collection collection=new ArrayList();
//可以放入不同类型的对象
collection.add("yuhua");
collection.add(18);
collection.add(21.0);
collection.add(new Integer(10));
collection.addAll(collection);
System.out.println(collection.size());
System.out.println(collection.hashCode());
//内部执行调用了tostring()方法
System.out.println(collection);
}
}
结果:
8
-1207994999
[yuhua, 18, 21.0, 10, yuhua, 18, 21.0, 10]
/**
* 说明:Collection方法应用HashSet
*
* @author huayu
* @date 2018/8/31 12:06 PM
*/
public class CollectionDemo {
public static void main(String[] args) {
//hashset是拿hash表实现的set
Collection collection=new HashSet();
//可以放入不同类型的对象
collection.add("yuhua");
collection.add(18);
collection.add(21.0);
collection.add(new Integer(10));
collection.addAll(collection);
System.out.println(collection.size());
System.out.println(collection.hashCode());
//内部执行调用了tostring()方法
System.out.println(collection);
}
}
结果(观察一下跟上一个例子arraylist的区别)
4
1192550516
[18, 21.0, yuhua, 10]
import java.util.Collection;
import java.util.LinkedList;
/**
* 说明:Collection方法应用LinkedList
*
* @author huayu
* @date 2018/8/31 12:06 PM
*/
public class CollectionDemo {
public static void main(String[] args) {
Collection collection=new LinkedList();
//可以放入不同类型的对象
collection.add("yuhua");
collection.add(18);
collection.add(21.0);
collection.add(new Integer(10));
collection.addAll(collection);
System.out.println(collection.size());
System.out.println(collection.hashCode());
//内部执行调用了tostring()方法
System.out.println(collection);
}
}
结果:
8
-1207994999
[yuhua, 18, 21.0, 10, yuhua, 18, 21.0, 10]
容器类对象在调用remove,contains等方法时需要比较对象是否相等,这会涉及到对象类型的equals方法和hashCode方法,对于自定义的类型,需要重写equals和hashCode方法以实现自定义的对象相等规则(注意:相等的对象应该具有相等的hash codes,比如,我们在查找词典的时候,通过索引去找字,在严格意义上讲那个索引就是一个对象,找的字就是一个值,所以当两个值一样的时候它的hashcode必须得一样,当重写equals方法时必须重写hashCode方法,在这个类的某个对象被当作索引(键)的时候使用)。
(2)Iterator接口
所有实现了Collection接口的容器类都有一个iterator()方法用以返回一个实现了Iterator接口的对象,这个对象可以是多种类型,不同的Collection实现类型遍历方式不同(用于遍历集合类)。
Iterator对象称作迭代器,用以方便的实现对容器内元素的遍历操作。
一句话总结,Iterator就是一个统一的遍历Collection中的元素的接口。
Iterator接口定义了如下方法
//判断游标右边是否有元素
boolean hasNext();
//返回游标右边的元素并将游标移动到下一个位置
E next();
//删除游标左边的元素,在执行完next之后该操作只能执行一次
default void remove()
Iterator方法使用举例:
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
/**
* 说明:Iterator方法使用简单举例
*
* @author huayu
* @date 2018/8/31 12:06 PM
*/
public class CollectionDemo {
public static void main(String[] args) {
Collection collection=new LinkedList();
//可以放入不同类型的对象
collection.add("yuhua");
collection.add(18);
collection.add(21.0);
collection.add(new Integer(10));
collection.addAll(collection);
collection.remove(18);
Iterator iterator=collection.iterator();
while(iterator.hasNext()){
Object o=iterator.next();
System.out.println(o);
}
}
}
结果
yuhua
21.0
10
yuhua
18
21.0
10
Iterator对象的remove方法是在迭代过程中删除元素的唯一安全的方法
import fundation.Student;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
/**
* 说明:用Iterator迭代器遍历删除集合元素
*
* @author huayu
* @date 2018/8/31 12:06 PM
*/
public class CollectionDemo {
public static void main(String[] args) {
Collection collection = new HashSet();
collection.add(new Student("yu", 10));
collection.add(new Student("hua", 20));
//在Iterator执行的时候,会将涉及的元素锁住,只能它自己动
for (Iterator iterator = collection.iterator(); iterator.hasNext(); ) {
Student student = (Student) iterator.next();
if (student.getName().length() < 3) {
iterator.remove();
}
}
System.out.println(collection);
}
}
结果
[Student1 [name=hua, age=20]]
补充:JDK1.5增强的for循环(除了简单遍历并读出其中的内容外,不建议使用增强for循环)
增强的for循环对于遍历array或Collection的时候相当简便
缺陷:1.数组不能方便的访问下标值
2.集合与使用Iterator相比,不能方便的删除结合中的内容,在内部也是调用Iterator
import java.util.ArrayList;
import java.util.Collection;
/**
* 说明:增强for循环的使用
*
* @author huayu
* @date 2018/9/3 1:01 PM
*/
public class ForDemo {
public static void main(String[] args) {
int[] arr={1,2,3,4,5};
for(int i:arr){
System.out.println(i);
}
Collection collection=new ArrayList();
collection.add("yu");
collection.add("hua");
for(Object c:collection){
System.out.println(c);
}
}
}
结果:
1
2
3
4
5
yu
hua
(3)Set接口
Set接口是Collection的子接口,Set接口没有提供额外的方法,但实现Set接口的容器类中的元素是没有顺序的,而且不可以重复。
Set容器可以与数学中“集合”的概念相对应。
JDK API中所提供的Set容器类有HashSet,TreeSet等。
Set接口中定义的方法
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean retainAll(Collection<?> c);
boolean removeAll(Collection<?> c);
void clear();
boolean equals(Object o);
int hashCode();
Set方法举例
import java.util.HashSet;
import java.util.Set;
/**
* 说明:Set举例
*
* @author huayu
* @date 2018/9/3 1:13
*/
public class HashSetDemo {
public static void main(String[] args) {
Set set=new HashSet();
set.add("yu");
set.add("hua");
set.add("yuhua");
//相同的元素不会被加入
set.add("yu");
set.add(new Integer(27));
System.out.println(set);
}
}
结果
[hua, yuhua, 27, yu]
import java.util.HashSet;
import java.util.Set;
/**
* 说明:用set方法求交集与并集
* 交集:以属于A且属于B的元素为元素的集合称为A与B的交(集)
* 并集:以属于A或属于B的元素为元素的集合称为A与B的并(集)
* 集合特性1:一个集合中,任何两个元素都认为是不相同的,即每个元素只能出现一次。
* @author huayu
* @date 2018/9/3 1:13
*/
public class HashSetDemo {
public static void main(String[] args) {
Set set1=new HashSet();
Set set2=new HashSet();
set1.add("a");
set1.add("b");
set1.add("c");
set2.add("b");
set2.add("c");
set2.add("d");
//求交集
Set set=new HashSet(set1);
set.retainAll(set2);
//求并集
Set set3=new HashSet(set1);
set3.addAll(set2);
System.out.println("set1、set2交集" + set);
System.out.println("set1、set2并集" +set3);
}
}
结果
set1、set2交集[b, c]
set1、set2并集[a, b, c, d]
(4)List接口
List接口是Collection的子接口,实现List接口的容器类中的元素是有顺序的,而且可以重复。
List容器中的元素都对应一个整数型的序号记载其容器中的位置,可以根据序号存取容器中的元素。
JDK所提供的List容器类有ArrayList、LinkedList等。
List接口中方法:
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean addAll(int index, Collection<? extends E> c);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
default void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
final ListIterator<E> li = this.listIterator();
while (li.hasNext()) {
li.set(operator.apply(li.next()));
}
}
default void sort(Comparator<? super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c);
ListIterator<E> i = this.listIterator();
for (Object e : a) {
i.next();
i.set((E) e);
}
}
int hashCode();
E set(int index, E element);
void add(int index, E element);
E remove(int index);
int indexOf(Object o);
int lastIndexOf(Object o);
ListIterator<E> listIterator();
ListIterator<E> listIterator(int index);
List<E> subList(int fromIndex, int toIndex);
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, Spliterator.ORDERED);
}
List方法举例
import java.util.LinkedList;
import java.util.List;
/**
* 说明:list方法举例
*
* @author huayu
* @date 2018/9/3 2:31 PM
*/
public class ListDemo {
public static void main(String[] args) {
List list=new LinkedList();
for (int i = 0; i < 5 ; i++) {
list.add("a"+i);
}
System.out.println(list);
list.set(1,"a10");
System.out.println(list);
System.out.println(list.get(1)+"");
System.out.println(list.indexOf("a10"));
list.remove("a0");
System.out.println(list);
}
}
结果:
[a0, a1, a2, a3, a4]
[a0, a10, a2, a3, a4]
a10
1
[a10, a2, a3, a4]
Collections类List常用算法
类java.util.Collections提供了一些静态方法实现了基于List容器的一些常用算法。
//对List容器内的元素排序
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
//对List容器内的对象进行随机排列
public static void shuffle(List<?> list) {
Random rnd = r;
if (rnd == null)
r = rnd = new Random(); // harmless race.
shuffle(list, rnd);
}
//对容器内的对象进行逆序排列
public static void reverse(List<?> list)
//用一个特定的对象重写整个List容器
public static <T> void fill(List<? super T> list, T obj)
//将src List容器内容拷贝到dest List容器
public static <T> void copy(List<? super T> dest, List<? extends T> src)
//对于顺序的List容器,采用折半查找的方法查找特定的对象
int binarySearch(List<? extends Comparable<? super T>> list, T key)
List常用算法举例:
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* 说明:Collections类中List方法举例
*
* @author huayu
* @date 2018/9/3 2:55 PM
*/
public class CollectionsMethodDemo {
public static void main(String[] args) {
List list1 = new LinkedList();
List list2 = new LinkedList();
for (int i = 0; i < 9; i++) {
list1.add("a" + i);
}
for (int i = 0; i < 9; i++) {
list2.add("1");
}
Collections.shuffle(list1);
System.out.println(list1);
Collections.reverse(list1);
System.out.println(list1);
Collections.sort(list1);
System.out.println(list1);
System.out.println("list2复制前"+list2);
Collections.copy(list2, list1);
System.out.println("list2复制后"+list2);
System.out.println(Collections.binarySearch(list1, "a4"));
}
}
结果:
[a8, a3, a1, a7, a2, a6, a4, a5, a0]
[a0, a5, a4, a6, a2, a7, a1, a3, a8]
[a0, a1, a2, a3, a4, a5, a6, a7, a8]
list2复制前[1, 1, 1, 1, 1, 1, 1, 1, 1]
list2复制后[a0, a1, a2, a3, a4, a5, a6, a7, a8]
4
知识链接:
Collection 和 Collections 的区别?
1.Collection 是一个接口,是 java 提供用于内存中存储单列数据的集合框架
2.Collections 是个一个工具类,提供 了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等。
(5)Comparable接口(注意,跟自己的类比较才有意义,类不同比较没有意义,比如狗跟猫比)
针对上面排序算法想一下根据什么确定对象“大小”顺序?
所有可以“排序”的类都实现了java.lang.Comparable接口,Comparable接口中只有一个方法
public int compareTo(T o);
该方法:
返回0 表示this==o
返回正数 表示this>obj
返回负数 表示this<obj
实现了Comparable接口的类通过实现compareTo方法从而确定该对象的排序方式(不实现Comparable接口直接调用Collections类中的sort方法对对象进行比较大小是不好使的!!!)。
对象之间比较大小方法举例:
/**
* 说明:实现Comparable接口的Name类
*
* @author huayu
* @date 2018/9/3 6:57 PM
*/
public class Name implements Comparable {
private String firstName;
private String lastName;
public Name(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
@Override
public String toString() {
return "Name{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Name) {
Name name = (Name) obj;
return (firstName.equals(name.firstName)) && (lastName.equals(name.lastName));
}
return super.equals(obj);
}
@Override
public int hashCode() {
return firstName.hashCode();
}
public int compareTo(Object o) {
Name name = (Name) o;
int lastComparation = lastName.compareTo(name.lastName);
//先比较lastName再比较firstName
return
(lastComparation != 0 ? lastComparation : firstName.compareTo(name.firstName));
}
}
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* 说明:测试Name的比较
*
* @author huayu
* @date 2018/9/3 7:15 PM
*/
public class TestNameComparable {
public static void main(String[] args) {
List list=new LinkedList();
list.add(new Name("shan","liu"));
list.add(new Name("hua","yu"));
list.add(new Name("cong","yu"));
System.out.println("排序前"+list);
Collections.sort(list);
System.out.println("排序后"+list);
}
}
结果
排序前[Name{firstName='shan', lastName='liu'}, Name{firstName='hua', lastName='yu'}, Name{firstName='cong', lastName='yu'}]
排序后[Name{firstName='shan', lastName='liu'}, Name{firstName='cong', lastName='yu'}, Name{firstName='hua', lastName='yu'}]
知识链接:
1.TreeMap 和 TreeSet 在排序时如何比较元素?
TreeSet 要求存放的对象所属的类必须实现 Comparable 接口,该接口提供了比较元素的 compareTo()方法,当插入元素时会回调该方法比较元素的大小。
TreeMap 要求存放的键值对映射的键必须实现 Comparable 接口从而根据键对元素进行排序。
2.Collections 工具类中的 sort()方法如何比较元素?
Collections 工具类的 sort 方法有 两种重载的形式:
第一种要求传入的待排序容器中存放的对象比较实现 Comparable 接口以实现 元素的比较;
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是 Comparator 接口的子类型(需要重写 compare 方法实现元素的比较),相当于一个临时定义的排序规则,其实就是通过接口注入比较元素大小的算法,也是对回调模式的应用(Java 中对函数式编程的支持)。
public static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}
(6)Map接口
实现Map接口的类用来存储 键-值(key-value) 对。
Map接口的实现类又HashMap和TreeMap等。
Map类中存储的键-值对通过键来标识,所以键值(作为索引)不能重复(若两个对象相互equals,他们的hashCode必须一样)。
Map接口的方法:
int size();
boolean isEmpty();
boolean containsKey(Object key);
boolean containsValue(Object value);
V get(Object key);
V put(K key, V value);
V remove(Object key);
void putAll(Map<? extends K, ? extends V> m);
void clear();
Set<K> keySet();
Collection<V> values();
Set<Map.Entry<K, V>> entrySet();
//Entry<K,V>接口
interface Entry<K,V> {
K getKey();
V getValue();
V setValue(V value);
boolean equals(Object o);
int hashCode();
public static <K extends Comparable<? super K>, V>
Comparator<Map.Entry<K,V>> comparingByKey() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getKey().compareTo(c2.getKey());
}
}
boolean equals(Object o);
int hashCode();
...
Map方法举例:
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* 说明:Map方法举例
*
* @author huayu
* @date 2018/9/3 8:12 PM
*/
public class MapDemo {
public static void main(String[] args) {
Map hashMap = new HashMap();
Map treeMap = new TreeMap();
hashMap.put("one", new Integer(1));
hashMap.put("two", new Integer(2));
hashMap.put("three", new Integer(3));
treeMap.put("A", new Integer(1));
treeMap.put("B", new Integer(2));
System.out.println("hashMap中键值对个数" + hashMap.size());
System.out.println(hashMap.containsKey("one"));
System.out.println(treeMap.containsValue(new Integer(1)));
if (hashMap.containsKey("two")) {
int i = ((Integer) hashMap.get("two")).intValue();
System.out.println(i);
}
Map hashMap1 = new HashMap(hashMap);
System.out.println(hashMap1);
hashMap1.putAll(treeMap);
/*
HashMap散列图、Hashtable散列表是按“有利于随机查找的散列(hash)的顺序”。
并非按输入顺序。遍历时只能全部输出,而没有顺序。
甚至可以rehash()重新散列,来获得更利于随机存取的内部顺序。
总之,遍历HashMap或Hashtable时不要求顺序输出,即与顺序无关。
*/
System.out.println("hashMap1中的东西" + hashMap1);
}
}
结果
hashMap中键值对个数3
true
true
2
{two=2, three=3, one=1}
hashMap1中的东西{A=1, B=2, two=2, three=3, one=1}
扩展:java的拆装包问题,jdk1.5之后就支持了(Auto-boxing/unboxing)
在合适的时机自动打包、解包(自动将基础类型转换为对象,自动将对象转换为基础类型)
以上的例子也可以这么写:
/**
* 说明:自动打包,解包释例
*
* @author huayu
* @date 2018/9/3 8:12 PM
*/
public class MapDemo {
public static void main(String[] args) {
Map hashMap = new HashMap();
Map treeMap = new TreeMap();
//虽然在这输入的是基本数据1,但是它实际扔进去的仍是个对象(自动装箱)
hashMap.put("one", 1);
hashMap.put("two",2);
hashMap.put("three", 3);
treeMap.put("A", 1);
treeMap.put("B", 2);
System.out.println("hashMap中键值对个数" + hashMap.size());
System.out.println(hashMap.containsKey("one"));
System.out.println(treeMap.containsValue(new Integer(1)));
if (hashMap.containsKey("two")) {
//自动拆箱
int i = (Integer) hashMap.get("two");
System.out.println(i);
}
Map hashMap1 = new HashMap(hashMap);
System.out.println(hashMap1);
hashMap1.putAll(treeMap);
/*
HashMap散列图、Hashtable散列表是按“有利于随机查找的散列(hash)的顺序”。
并非按输入顺序。遍历时只能全部输出,而没有顺序。
甚至可以rehash()重新散列,来获得更利于随机存取的内部顺序。
总之,遍历HashMap或Hashtable时不要求顺序输出,即与顺序无关。
*/
System.out.println("hashMap1中的东西" + hashMap1);
}
}
结果:
hashMap中键值对个数3
true
true
2
{two=2, three=3, one=1}
hashMap1中的东西{A=1, B=2, two=2, three=3, one=1}
(7)在这一模块的知识做个系统的学习还缺泛型这一部分,以下是链接
https://blog.csdn.net/Myuhua/article/details/82380691
知识链接:
1)如何选择数据结构?
衡量标准:读的效率和改的效率
Aarry读快改满
Linked改快读慢
Hash在两者之间
2)List 和 Set 以及 Map 之间的区别是什么?
List:Collection 下的子接口,保存有序的、可重复的数据
Set:Collection 下的子接口,保存无序的、不可重复的数据
Map:定义用来保存键-值对特点的数据。要求键不能重复。
Set 和 Map 容器都有基于哈希存储和排序树的两种实现版本,基于哈希存储的版本理 论存取时间复杂度为 O(1),而基于排序树版本的实现在插入或删除元素时会按照元素或元素的 键(key)构成排序树从而达到排序和去重的效果。
3)HashMap 的工作原理是什么?
HashMap map = new HashMap();//底层创建了长度为 16 的 Entry 数组向 HashMap 中添加 entry1(key,value),需要首先计算 entry1 中 key 的哈希值(根据 key 所在类的 hashCode()计算得到),此哈希值经过处理以后,得到在底层 Entry[]数组中要存储的位置 i.如果位置 i 上没有元素,则 entry1 直接添加成功。如果位置 i 上已经存在 entry2(或还有链表存在的 entry3,entry4),则需要通过循环的方法,依次比较 entry1 中 key 和其他的 entry 是否 equals.如果返回值为 true.则使用 entry1 的 value 去替换 equals 为 true 的 entry 的 value.如果遍历一遍以后,发现所有的 equals 返回都为 false,则 entry1 仍可添加成功。entry1 指向原有的 entry 元素。
4)HashMap 和 Hashtable 的区别?
HashMap:1作为 Map 的主要实现类2线程安全的3可以存储 null 的 key 和 value
Hashtable:2Map 的古老实现类2线程不安全的3不可以存储 null 的 key 和 value
(8)最后,对这一模块的知识总结一下。
对于这章我们使用1136总结:
一张图
一个类:Collections
三个知识点:增强For循环、Generic、Auto-boxing/unboxing
六个接口
若有问题欢迎大家与我互动交流,可评论,可留言,以后每周我会坚持至少更新一篇博客文章,喜欢的朋友可以加一下关注。