Version: Next

1. 集合

一方面,面向对象语言对事务的体现都是以对象的形式,为了方便多个对象的操作,就要对对象进行存储。另一方面,使用Array存储对象具有一些弊端,而Java集合就像一种容器,可以动态的把多个对象的引用存入集合中

Java集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的关联数组

1.1 Java集合的组织结构

Java集合的组织结构

image-20200508103212548

集合工具类

  • Collections
  • Arrays

排序接口

  • Comparable
  • Comparator

1.2 Collection接口

主要API:

  • size()返回集合长度

  • add(Object object) 添加 返回boolean

  • addAll(Collection collection) 把集合中的所有元素添加到另一个集合里

  • isEmpty() 返回boolea是否为空

  • clear() 清空集合

  • contains(Object obj) 集合是否包含某对象

  • containsALL(Collection coll) 是否包含集合中的所有元素

  • retainAll(Collection coll) 保留两个集合共同的元素,交集

  • remove(Object obj) 移除一个元素

  • equals()

  • hashcode()

  • toArray() 将集合转化为数组

  • iterator() 返回Iterator接口实现类的对象,遍历集合对象

public class TestCollection {
@Test
public void testCollcetion1() {
//因为Collection是接口,所以用List接口下的实现类 ArrayList来创建对象
Collection collection = new ArrayList();
//1.size()返回集合长度
System.out.println(collection.size());
//2.add(Object object)添加 返回boolean
collection.add(123);
collection.add("AA");
collection.add(new Date());
collection.add("BB");
//3.addAll(Collection collection) 把集合中的所有元素添加到另一个集合里
//Arrays数组转集合
Collection collection2 = Arrays.asList(1, 2, 3);
collection.addAll(collection2);
System.out.println(collection.size());
System.out.println(collection);
//4.isEmpty() 返回boolea是否为空
System.out.println(collection.isEmpty());
//5.clear() 清空集合
collection.clear();
System.out.println(collection.isEmpty());
}
@Test
public void testCollection2() {
Collection collection = new ArrayList();
collection.add(123);
collection.add("AA");
collection.add(new Date());
collection.add("BB");
collection.add(new Person("班", 18));
//6.contains(Object obj)
//根据元素所在类的equals方法,重写,判断成员变量的值而不是判断地址值
boolean flag = collection.contains(new Person("班", 18));
System.out.println(flag);
//7.containsALL(Collection coll) 是否包含集合中的所有元素
Collection collection2 = new ArrayList();
collection2.add(123);
collection2.add(new String("AA"));
boolean flag2 = collection.containsAll(collection2);
System.out.println(flag2);
//8 retainAll(Collection coll) 保留两个集合共同的元素,交集
//结果返回到调用集合
collection.retainAll(collection2);
System.out.println(collection); // [123, AA]
//9.remove(Object obj)
boolean flag4 = collection.remove("bb");
System.out.println(flag4);
//10.removeAll(Collection coll)
boolean flag5 = collection.retainAll(collection2);
//11.equals
//12.hashcode() 计算集合中的Hash值
System.out.println(collection.hashCode());
//13.toArray 将集合转化为数组
Object[] obj = collection.toArray();
for(Object o:obj){
System.out.println(o);
}
//14.iterator() 返回Iterator接口实现类的对象,遍历集合对象
Iterator iterator = collection.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}

1.3 List接口

List也是接口,继承自Collection接口,Collection的15个方法皆可用

  • 元素有序可重复
  • ArrayList(动态数组):List的主要实现类
    • 基于数组实现,增删慢,查询快,线程不安全
  • LinkedList(链表):对于频繁的插入和删除建议选择
    • 基于双向链表实现,增删快,查询慢,线程不安全
  • Vector: 古老的实现类,线程安全
    • 基于数组实现,增删慢,查询快,线程安全
    • 为了线程安全损失了性能,总体上性能不如ArrayList

List集合里添加了一些根据索引来操作集合元素的方法

  • void add(int index,Object ele) 在指定的位置添加

  • boolean addALL(int index,Collection eles) 在指定位置加上好多元素,以集合的方式传入

  • Object get(int index) 根据下标获得一个元素

  • int indexOf(Object obj) 获得一个元素的下标

  • int lastIndexOf(Object obj) 返回最后一个元素的下标
  • Object remove(int index) 删除指定位置的元素
  • Object set(int index,Object ele) 设定指定位置的元素变成一个新值
  • List subList(int fromIndex,int toIndex) 返回从fromIndex到 toIndex之间的子list !左闭右开

List常用方法

  • add or 插入 add(index,Object obj)
  • remove 元素/下标
  • set
  • get(index)
@Test
public void testList1() {
//List是接口 , ArrayList是实现类
List list = new ArrayList();
list.add(123); //用的是Collection的方法
list.add(456);
list.add(new String("AA"));
list.add(new String("GG"));
//打印结果是有序的
System.out.println(list);
list.add(0, new String("first"));
System.out.println(list);
Object obj = list.get(1);
System.out.println(obj);
//subList
List list1 = new ArrayList();
list1.add(0);
list1.add(1);
list1.add(2);
list1.add(3);
List list1Sub = list1.subList(0, 3);
System.out.println(list1);
System.out.println(list1Sub);
List vectorList = new Vector();
vectorList.add(123);
System.out.println(vectorList);
}

1.4 Set接口

Set

  • 无序不可重复
  • HashSet(主要实现类)
  • LinkedHashSet
  • TreeSet

1.无序 != 随机 真正的无序性指的是元素在顶层存储的位置是无序的,位置根据哈希值来确定

2.不可重复性: 当向Set中提娜佳相同的元素时,后面的这个不能添加进去

Set中的元素是如何存储的呢?

  • 使用了哈希算法,先算Hash值,不重复就直接存,重复了就和原值对比。

  • 在Hash值不同的情况下,即使不对值进行对比,也可以默认两个值不同

  • 而Hash值是在值本地计算的不需要牵扯其他值,因此效率高

    1.调用HashCode() 计算哈希值,决定了对象在Set中的存储位置

    2.若Hash值对应内存已有值,则调用equals进行对比,如果相同则后一个对象不能再添加进来

    3.如果值不相同

    ​ -> 正常情况下相同的值一定产生相同的HashCode,这是HashCode方法的事

1.4.1 HashSet——HashMap实现,无序

  • 进去就new了个HashMap
  • 自己的添加方法,调的是HashMap的put方法。为了得到那个Key
  • 至于Value,用PRESENT表示,通过查看其定义,得知就是一个写死的常量
@Test
public void testHashSet() {
Set<Integer> set = new HashSet<Integer>();
set.add(123);
set.add(456);
set.add(null);
System.out.println(set.size());
System.out.println(set);
System.out.println("==================================");
Set set1 = new HashSet();
Person person1 = new Person("班", 18);
Person person2 = new Person("班", 18);
set1.add(person1);
set1.add(person2);
//对象可以重复
//1.需要重写Object的equals方法
//2.需要重写hashCode方法
//才能保证Set判断到两个对象相同
System.out.println(set1);
//打印出来HashSet中只有一个Person对象
}

1.4.2 LinkedHashSet——继承HashSet、HashMap实现数据存储、双向链表记录顺序

/***
* 维护了一个添加进集合中的顺序。导致我们遍历LinkedHashSet集合元素
* 时是按照添加进去的顺序遍历的 -> 这不代表是有序的
*/
@Test
public void testLinkedHashSet() {
Set set = new LinkedHashSet();
set.add(123);
set.add(456);
set.add(null);
System.out.println(set);
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}

1.4.1 TreeSet——二叉树实现

1.向TreeSet中添加的元素必须是同一个类,(自动初始化泛型)

2.可以按照添加进集合中的元素的指定的顺序遍历。 String,包装类默认按照从小到大

3.自定类没有实现Comparable接口时,当向TreeSet中添加自定义对象时,报ClassCastException

  • 自然排序:

    要求自定义类实现java.lang.Comparable接口并重写compareTo(Object obj)在此方法中指定按照自定义类的哪个属性进行排序

    TreeSet中添加元素时首先按照compareTo()进行比较,一旦返回0,虽然仅是两个对象的age属性值相同,但TreeSet会认为两个对象都是相同的,进而后一个对象添加不进去TreeSet

    对于TreeSet: compareTo() -> hashCode() -> equals() 三者保持一致

    compareTo()中,先按一个属性排序,如果相同再按另一个属性排序

  • 定制排序:

    1.创建一个实现了Comparator接口的匿名类对象 2.将此对象作为形参传递给TreeSet的构造方法 3.向TreeSet中添加Comparator接口中compareTo方法涉及到的Object object

  • 自然排序
@Test
public void testTreeSet() {
Set set = new TreeSet();
set.add(new Person("CC", 23));
set.add(new Person("MM", 21));
set.add(new Person("GG", 25));
set.add(new Person("JJ", 24));
set.add(new Person("DD", 20));
set.add(new Person("AA", 20));
set.add(new Person("AA", 20));
for (Object obj : set) {
System.out.println(obj);
}
}
Person{name='AA', age=20}
Person{name='DD', age=20}
Person{name='MM', age=21}
Person{name='CC', age=23}
Person{name='JJ', age=24}
Person{name='GG', age=25}
  • 定制排序
@Test
public void testTreeSet2() {
//new一个匿名实现Comparator接口的对象,大括号重写接口方法
Comparator comparator = new Comparator() {
//向TreeSet中添加Customer类的对象,在此Comparator方法中指明按照Customer的哪个属性排序
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof Customer && o2 instanceof Customer) {
Customer customer1 = (Customer) o1;
Customer customer2 = (Customer) o2;
int flagId = customer1.getId().compareTo(customer2.getId());
if (flagId == 0) {
return customer1.getName().compareTo(customer2.getName());
}
return flagId;
}
return 0;
}
};
Set set = new TreeSet(comparator);
set.add(new Customer("AA", 1003));
set.add(new Customer("BB", 1002));
set.add(new Customer("GG", 1004));
set.add(new Customer("CC", 1001));
set.add(new Customer("DD", 1007));
set.add(new Customer("AA", 1007));
for (Object object : set) {
System.out.println(object);
}
}
Customer{name='CC', id=1001}
Customer{name='BB', id=1002}
Customer{name='AA', id=1003}
Customer{name='GG', id=1004}
Customer{name='AA', id=1007}
Customer{name='DD', id=1007}
@Test
public void testTreeSet3() {
Set set = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof Customer && o2 instanceof Customer) {
Customer customer1 = (Customer) o1;
Customer customer2 = (Customer) o2;
int flagId = customer1.getId().compareTo(customer2.getId());
if (flagId == 0) {
return customer1.getName().compareTo(customer2.getName());
}
return flagId;
}
return 0;
}
});
set.add(new Customer("AA", 1003));
set.add(new Customer("BB", 1002));
set.add(new Customer("GG", 1004));
set.add(new Customer("CC", 1001));
set.add(new Customer("DD", 1007));
set.add(new Customer("AA", 1007));
for (Object object : set) {
System.out.println(object);
}
}
Customer{name='CC', id=1001}
Customer{name='BB', id=1002}
Customer{name='AA', id=1003}
Customer{name='GG', id=1004}
Customer{name='AA', id=1007}
Customer{name='DD', id=1007}

1.5 Map接口

  • Map接口与Collection并列存在,用于保存具有映射关系的数据 Key-Value
  • Map中的Key和Value都可以是任何引用类型的数据
  • Map中的Key用Set来存放,不允许重复
  • 常用String类作为Map的"键"
  • key和value之间存在单向一一映射,即通过指定的key总能找到唯一的,确定的value
  • Map -> [ Set : Collection ] -> Entry一条记录 当HashMap的值都为null,则HashMap退化为HashSet

实现类:

  • HashMap
  • LinkedHashMap
  • TreeMap
  • HashTable
    • Properties

添加、删除操作:

  • Object put(Object key, Object value)
  • Object remove(Object key) 按照指定Key删除Entry,返回值是value
  • void putAll(Map t)
  • void clear()

遍历

  • Set keySet() 返回key的集合
  • Collection values() 返回value的集合
  • Set<Map.Entry<K, V>> entrySet() 返回键值对的集合

元素查询的操作

  • Object get(Object key)
  • boolean containsKey(Object key)
  • boolean containsValue(Object value)
  • int size()
  • boolean isEmpty()
  • boolean equals(Object obj)

1.5.1 Properties类

  • jdbc.properties文件
user=root
password=rootpass
  • 使用Properties加载properties文件
/***
* Properties类
* load(输入流)
*/
@Test
public void testProperties() throws IOException, FileNotFoundException {
Properties properties = new Properties();
properties.load(new FileInputStream(new File("jdbc.properties")));
String user = properties.getProperty("user");
System.out.println(user);
//root
}

1.5.2 TreeMap类——基于二叉树

  • 定制排序
@Test
public void testTreeMap2() {
Comparator comparator = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof Customer && o2 instanceof Customer) {
Customer customer1 = (Customer) o1;
Customer customer2 = (Customer) o2;
int flagId = customer1.getId().compareTo(customer2.getId());
if (flagId == 0) {
return customer1.getName().compareTo(customer2.getName());
}
return flagId;
}
return 0;
}
};
Map map = new TreeMap(comparator);
map.put(new Customer("AA", 1001), 87);
map.put(new Customer("CC", 1001), 67);
map.put(new Customer("MM", 1004), 97);
map.put(new Customer("GG", 1002), 37);
for (Object key : map.keySet()) {
System.out.println(key + " ---> " + map.get(key));
}
}
Customer{name='AA', id=1001} ---> 87
Customer{name='CC', id=1001} ---> 67
Customer{name='GG', id=1002} ---> 37
Customer{name='MM', id=1004} ---> 97
  • 按key排序
/***
* TreeMap
* - 按照添加进Map中的元素的指定属性key进行排序
* 故,要求Key是同一个类的对象
*/
@Test
public void testTreeMap() {
Map map = new TreeMap();
map.put(new Person("AA", 23), 89);
map.put(new Person("AA", 21), 99);
map.put(new Person("MM", 22), 79);
map.put(new Person("GG", 33), 99);
map.put(new Person("JJ", 13), 69);
for (Object key : map.keySet()) {
System.out.println(key + " ---> " + map.get(key));
}
}
Person{name='JJ', age=13} ---> 69
Person{name='AA', age=21} ---> 99
Person{name='MM', age=22} ---> 79
Person{name='AA', age=23} ---> 89
Person{name='GG', age=33} ---> 99

1.5.3 LinkedHashMap类

  • 遍历结果与存放顺序一致
  • 基于链表实现
/***
* LinkedHashMap
* 遍历结果与存放顺序一致
*/
@Test
public void testMap3() {
Map map = new LinkedHashMap();
map.put("key", "value");
map.put("AA", 123);
map.put("BB", 456);
map.put(123, "bb");
map.put(null, null);
for (Object object : map.entrySet()) {
Map.Entry entry = (Map.Entry) object;
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
}
key -> value
AA -> 123
BB -> 456
123 -> bb
null -> null

1.5.4 HashMap类

  • 遍历
/***
* 如何遍历Map
* - 遍历所有Key
* - 遍历所有Value
* - 遍历所有Entry
*/
@Test
public void testMap2() {
Map map = new HashMap();
map.put("key", "value");
map.put("AA", 123);
map.put("BB", 456);
map.put(123, "bb");
map.put(null, null);
System.out.println(map.keySet());
System.out.println(map.values());
for (Object object : map.entrySet()) {
Map.Entry entry = (Map.Entry) object;
System.out.println("Key : " + entry.getKey()
+ " | Value : " + entry.getValue());
}
}
[AA, BB, null, 123, key]
[123, 456, null, bb, value]
Key : AA | Value : 123
Key : BB | Value : 456
Key : null | Value : null
Key : 123 | Value : bb
Key : key | Value : value
  • Key是Set类型不可重复;Value是Collection类型,可重复;Entry键值对,用Set存放,不可重复

添加元素时,调用Key的equals方法; 键重复时,value会被新值覆盖

@Test
public void testMap1() {
Map map = new HashMap();
map.put("key", "value");
map.put("AA", 123);
map.put("BB", 456);
map.put(123, "bb");
map.put(null, null);
System.out.println(map.size());
System.out.println(map.remove("BB"));
}

1.5.5 HashTable类

HashTable是遗留类,很多功能都与HashMap类似,不同的是它继承自Dictionary类,并且是线程安全的买同一时刻只能有一个线程写HashTable,并发性不如CuncurrentHashMap

1.6 Iterator

用来遍历集合

public class TestIterator {
@Test
public void test1() {
Collection collection = new ArrayList();
collection.add(123);
collection.add("AA");
collection.add(new Date());
collection.add("BB");
collection.add(new Person("豆豆", 18));
Iterator iterator = collection.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
@Test
public void test2() {
Collection collection = new ArrayList();
collection.add(123);
collection.add("AA");
collection.add(new Date());
collection.add("BB");
collection.add(new Person("豆豆", 18));
for (Object object : collection) {
System.out.println(object);
}
}
//面试题
@Test
public void testFor3() {
String[] str = new String[]{"AA", "BB", "DD"};
for (int i = 0; i < str.length; i++) {
str[i] = i + "";
}
for (int i = 0; i < str.length; i++) {
System.out.println(str[i]);
}
//问执行结果 0 1 2
}
//面试题
@Test
public void testFor4() {
String[] str = new String[]{"AA", "BB", "DD"};
for (String s : str) {
s = "MM";
System.out.println(s);
}
for (int i = 0; i < str.length; i++) {
System.out.println(str[i]);
}
//问执行结果 MM MM MM AA BB DD
}
}

1.7 Collections与Arrays工具类

1.7.1 Collections工具类

Collections工具类 static方法 , 不需要创建对象

  • reverse(List):反转List中元素的顺序
  • shuffle(List): 对List集合元素进行随机排序
  • sort(List): 根据元素的自然顺序对指定List集合元素进行升序排序
  • swap(List,i,j)及那个指定list集合中的i处元素和j处元素进行交换
  • Object max(Collection)
  • Object max(Collection, Comparator) 定制排序规则下的最大
  • int frequency(Collection, Object) 指定元素在集合中出现的次数
  • void copy(List destination, List source) 将source中的内容复制到destination中
  • boolean repalceAll(List list, Object oldValue, Object newValue)将旧值替换为新值
  • reverseOrder() 提供一个降序Comparator实现类

Collections类中提供了多个synchronizedXxx()方法,该方法可使将指定集合包装成线程同步集合,从而可以解决多线程并发访问集合时的线程安全问题

@Test
public void test() {
Collection coll = new ArrayList();
Collections.synchronizedCollection(coll);
Map map = new HashMap();
Collections.synchronizedMap(map);
}

1.7.2 Arrays工具类

Arrays工具类 static方法 , 不需要创建对象

常用API:

  • toString 将数组转换为可输出的字符串
  • asList 将参数包装为一个List
  • sort 默认将数组升序排序
@Test
public void testArrays() {
Integer[] arr = {1, 3, 2};
System.out.println("arr =" + Arrays.toString(arr));
List<Integer> asList = Arrays.asList(2, 3, 4);
System.out.println("as list -> " + asList);
Arrays.sort(arr);
System.out.println("arr after sort -> " + Arrays.toString(arr));
//降序
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
//lambda表达式写法
//Arrays.sort(arr, (o1, o2) -> o2 - o1);
System.out.println("用Comparator接口降序 -> " + Arrays.toString(arr));
arr = new Integer[]{1, 3, 2};
//降序
Arrays.sort(arr, Collections.reverseOrder());
System.out.println("用Collections.reverseOrder()降序 -> " + Arrays.toString(arr));
}

1.8 Comparator与Comparable排序接口

1.8.1 Comparator

案例: 先按id升序排序,如果id相等,按name升序排序

  • 重写compare方法
@Test
public void testTreeSet2() {
//new一个匿名实现Comparator接口的对象,大括号重写接口方法
Comparator comparator = new Comparator() {
//向TreeSet中添加Customer类的对象,在此Comparator方法中指明按照Customer的哪个属性排序
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof Customer && o2 instanceof Customer) {
Customer customer1 = (Customer) o1;
Customer customer2 = (Customer) o2;
int flagId = customer1.getId().compareTo(customer2.getId());
if (flagId == 0) { //两个值相等
return customer1.getName().compareTo(customer2.getName());
}
return flagId;
}
return 0;
}
};
Set set = new TreeSet(comparator);
set.add(new Customer("AA", 1003));
set.add(new Customer("BB", 1002));
set.add(new Customer("GG", 1004));
set.add(new Customer("CC", 1001));
set.add(new Customer("DD", 1007));
set.add(new Customer("AA", 1007));
for (Object object : set) {
System.out.println(object);
}
}
Customer{name='CC', id=1001}
Customer{name='BB', id=1002}
Customer{name='AA', id=1003}
Customer{name='GG', id=1004}
Customer{name='AA', id=1007}
Customer{name='DD', id=1007}

1.8.2 Comparable

Comparable接口可以实现对象数组排序,也就可以对整型数组进行直接排序

  • 重写compareTo方法
class Node implements Comparable<Node> {
private int value;
public Node(int value) {
this.value = value;
}
@Override
public int compareTo(Node node) {
if (this.value == node.value) {
return 0;
} else {
return this.value > node.value ? 1 : -1;
}
}
public static void sortInt(int[] a) {
Node[] node = new Node[a.length];
for(int i = 0; i < a.length; i ++) {
node[i]=new Node(a[i]);
}
Arrays.sort(node);
for(int i = 0; i < a.length; i ++) {
a[i] = node[i].value;
}
}
public static void main(String[] args) {
int[] a = {1, 4, 5, 7, 6, 5, 9, 12, 34, 23};
sortInt(a);
for(int i = 0; i < a.length; i ++) {
System.out.print(a[i] + " ");
}
}
}