Collection
主要接口方法:
int size()
boolean inEmpty()
boolean contains(Object element)
boolean add(E element)
boolean remove(Object element)
Interator<E> iterator()
boolean containsAll(Collection<?> c)
boolean addAll(Collection<? extends E> c)
boolean removeAll(Collection<?> c)
boolean retainAll(Collection<?> c)
void clear()
Object[] toArray()
<T> T[] toArray(T[] a)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package org.example;
import java.io.PrintStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List;
public class CollectionDemo { public static void main(String[] args) { PrintStream out = System.out;
Collection<Integer> collection = new ArrayList<Integer>(); List<Integer> list = List.of(1,2,3,4,5,6,7,8,9,10); collection.addAll(list); out.println(collection); out.println(collection.containsAll(list)); } }
|
Collection类是除了Map外所有其他集合类的根接口。
Java的java.util包主要提供了以下三种类型的集合:
List:一种有序列表的集合,例如,按索引排列的Student的List;
Set:一种保证没有重复元素的集合,例如,所有无重复名称的Student的Set;
Map:一种通过键值(key-value)查找的映射表集合,例如,根据Student的name查找对应Student的Map。
java集合的特点:
- 接口和实现类相分离,如:有序表的接口是
List,实现类是ArrayList、LinkedList等。
- 支持泛型
List
主要接口方法:
boolen add(E e):在末尾添加一个元素
boolen add(int index,E e):在指定索引添加一个元素
E remove(int index):删除指定索引的元素
boolean remove(Object e):删除某个元素
E get(int index):获取指定索引的元素
int size():获取链表大小(包含元素的个数)
List.of():返回一个不可变的list
Iterator类
使用Iterator类来遍历List。
iterator():获取一个迭代器
next():返回迭代器的下一个元素,并将迭代器的指针移到下一个元素
hasNext():用于判断集合中是否还有下一个元素可以访问
remove():从集合中删除迭代器最后访问的元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package one;
import java.util.ArrayList; import java.util.Iterator; import java.util.List;
public class collection { public static void main(String[] ages){ try{ List<String> list=new ArrayList<>(); list.add("one"); list.add("two"); for(Iterator<String> it=list.iterator();it.hasNext();){ String item=it.next(); System.out.println(item); } }catch(IndexOutOfBoundsException e){ throw new ListNumAdd(); } } } class ListNumAdd extends RuntimeException{ public ListNumAdd(){ super("不能添加超过集合长度的索引"); } }
|
List和Array转换
如果传入的数组大小比List元素个数小时,List内部会创建一个新的刚好够大的数组填充后返回;如果传入的数组大小比List元素个数大时,填充完元素后,剩余的元素一律填充null。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package one;
import java.util.ArrayList; import java.util.Iterator; import java.util.List;
public class collection { public static void main(String[] ages){ try{ List<Integer> list=List.of(11,13,22); Number[] array=list.toArray(new Number[list.size()]); for (Number integer : array) { System.out.println(integer); } }catch(ArrayStoreException e){ throw new ListNumAdd(); } } } class ListNumAdd extends RuntimeException{ public ListNumAdd(){ super("类型不正确"); } }
|
将Array变为List时,返回的List只是一个只读List,无法进行增删元素操作。
覆写equals()方法
boolean contains(Object o):判断List中是否包含某个指定元素
int indexOf(Object o):返回某个元素的索引,如果元素不存在就返回-1
contains、indexOf等方法是通过equals方法来判断两个元素是否相等,所以放入的实例必须正确覆写equals方法。String、Integer这些对象是已经正确实现了equals方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package one;
import java.util.List;
public class collection { public static void main(String[] ages){ List<Person> list=List.of(new Person("xiaodi"),new Person("xiaobai")); System.out.println(list.contains(new Person("xiaodi"))); System.out.println(list.contains(list.get(0))); } } class Person{ String name; public Person(String name){ this.name = name; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| package one;
import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Objects;
public class collection { public static void main(String[] ages){ List<Person> list=List.of(new Person("xiaodi"),new Person("xiaobai")); System.out.println(list.contains(new Person("xiaodi"))); System.out.println(list.contains(list.get(0))); } } class Person{ String name; public Person(String name){ this.name = name; } public boolean equals(Object o){ if (!(o instanceof Person p)) return false; return Objects.equals(this.name, p.name);
} }
1. list.contains(new Person("xiaodi")) 开始执行 2. List内部遍历每个元素,调用每个元素的equals方法 3. 第一次调用:list.get(0).equals(new Person("xiaodi")) - this → list.get(0)对象(name="xiaodi") - o → new Person("xiaodi")对象 4. 执行equals方法: if (!(o instanceof Person p)) return false; return Objects.equals(this.name, p.name); - this.name = "xiaodi"(第一个对象的name) - p.name = "xiaodi"(参数对象的name) 5. Objects.equals("xiaodi", "xiaodi") 返回 true 6. contains()立即返回true
|
Map
实现类:HashMap
boolean containsKey(K key):检查key是否存在。
Map中不存在重复的key(因为放入相同的key会把原有的key-value对应的value替换掉),但可以存在相同的value。
遍历Map
遍历key,可以使用Map实例的keySet()方法返回的不包含重复key的Set集合。
同时遍历key和value,可以使用Map对象的entrySet()集合,它包含了每一个key-value映射。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package one;
import java.util.HashMap; import java.util.Map;
public class collection { public static void main(String[] ages){ Map<String,Integer> map = new HashMap<>(); map.put("one",1); map.put("two",2); for(Map.Entry<String,Integer> entry : map.entrySet()){ System.out.println(entry.getKey() +":"+ entry.getValue()); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| package one;
import java.util.*;
public class collection { public static void main(String[] ages){ List<Student> list = List.of( new Student("Bob", 78), new Student("Alice", 85), new Student("Brush", 66), new Student("Newton", 99)); var holder = new Students(list); System.out.println(holder.getScore("Bob") == 78 ? "测试成功!" : "测试失败!"); System.out.println(holder.getScore("Alice") == 85 ? "测试成功!" : "测试失败!"); System.out.println(holder.getScore("Tom") == -1 ? "测试成功!" : "测试失败!"); } } class Students { List<Student> list; Map<String, Integer> cache; Students(List<Student> list) { this.list = list; cache = new HashMap<>(); }
int getScore(String name) { Integer score = this.cache.get(name); if (score == null) { return findInList(name); } return score == null ? -1 : score.intValue(); } Integer findInList(String name) { for (var ss : this.list) { if (ss.name.equals(name)) { return ss.score; } } return null; } } class Student { String name; int score;
Student(String name, int score) { this.name = name; this.score = score; } }
|
覆写HashCode()方法
HashMap是用一个大数组存储所有的value,再根据key计算出value应该存储在哪个索引,所以Map也需要覆写equals()方法来保证两个相同类型相同参数的key实例相等。
通过key计算索引的方式就是调用key对象的hashCode()方法,它返回一个int整数。也需要覆写来确保key实例相同时对应的索引也相同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package one;
import java.util.*;
public class collection { public static void main(String[] ages){ Map<Person,Integer> map = new HashMap<>(); Person key=new Person("John",20); map.put(key,1); System.out.println(map.get(key)); System.out.println(map.get(new Person("John",20))); } } class Person { String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } }
|
正确使用Map必须保证:
- 作为
key的对象必须正确覆写equals()方法,相等的两个key实例调用equals()必须返回true;
- 作为
key的对象还必须正确覆写hashCode()方法,且hashCode()方法要严格遵循以下规范:
- 如果两个对象相等,则两个对象的
hashCode()必须相等;
- 如果两个对象不相等,则两个对象的
hashCode()尽量不要相等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package one;
import java.util.*;
public class collection { public static void main(String[] ages){ Map<Person,Integer> map = new HashMap<>(); Person key=new Person("John",20); map.put(key,1); System.out.println(map.get(key)); System.out.println(map.get(new Person("John",20))); } } class Person { String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public boolean equals(Object o) { if(o instanceof Person p) return this.age=p.age && Objects.equals(this.name, p.name); return false; } @Override public int hashCode() { return Objects.hash(this.name); } }
|
拓展
1、hashCode()返回的int类型范围为+2147483647~-2147483647,而HashMap内部使用的数组默认大小只有16。当超过一定数量的键值对时,HashMap会自动扩容一倍并重新通过hashCode()计算索引位置。频繁扩容对HashMap的性能影响很大,所以创建HashMap时可以指定内存空间大小。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package one;
import java.util.*; import java.io.*;
public class collection { public static void main(String[] ages){ Map<String,Integer> map = new HashMap<>(100); map.put("one",1); map.put("two",2); map.put("three",3); for(Map.Entry<String,Integer> entry : map.entrySet()){ System.out.println(entry.getKey() + " " + entry.getValue()); } } }
|
2、不同的key具有相同的hashCode()的情况称之为哈希冲突。在冲突的时候,一种最简单的解决办法是用List存储hashCode()相同的key-value。显然,如果冲突的概率越大,这个List就越长,Map的get()方法效率就越低。
EnumMap
当key的对象是enum类型时可以使用。内部以一个非常紧凑的数组存储value,并且根据枚举类型的key直接定位到内部数组的索引,并不需要计算hashCode()。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package org.example;
import java.time.DayOfWeek; import java.util.*;
public class CollectionDemo { public static void main(String[] args) { Map<DayOfWeek, String> map = new EnumMap<>(DayOfWeek.class); map.put(DayOfWeek.MONDAY, "星期一"); map.put(DayOfWeek.TUESDAY, "星期二"); map.put(DayOfWeek.WEDNESDAY, "星期三"); map.put(DayOfWeek.THURSDAY, "星期四"); map.put(DayOfWeek.FRIDAY, "星期五"); map.put(DayOfWeek.SATURDAY, "星期六"); map.put(DayOfWeek.SUNDAY, "星期日"); System.out.println(map); System.out.println(map.get(DayOfWeek.MONDAY)); } }
|
SortedMap会自动对key进行排序。它的实现类是TreeMap。使用TreeMap时,放入的key必须实现Comparable接口。否则在创建TreeMap时就得同时指定一个自定义排序算法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| package org.example;
import java.time.DayOfWeek; import java.util.*;
public class CollectionDemo { public static void main(String[] args) { Map<Person, Integer> map = new TreeMap<>(new Comparator<Person>() { @Override public int compare(Person o1, Person o2) { return o1.name.compareTo(o2.name); } }); map.put(new Person("老白"),1); map.put(new Person("小白"),2); System.out.println(map); } } class Person{ String name; public Person(String name){ this.name = name; } public String toString(){ return name; } }
|
Properties
用来表示一组配置的集合,内部本质是一个Hashtable。
java默认配置文件以.properties为拓展名,每行以key=value表示,以#开头为注释。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package org.example;
import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.DayOfWeek; import java.util.*;
public class CollectionDemo { public static void main(String[] args)throws IOException { Path peizhi= Paths.get(".\\src\\main\\java\\demo\\one.properties"); Path parent= peizhi.getParent(); if(parent!=null&& Files.notExists(parent)) Files.createDirectories(parent); if(Files.notExists(peizhi)) Files.createFile(peizhi); try(InputStream input=new FileInputStream(peizhi.toFile())) { Properties properties = new Properties(); properties.load(input); System.out.println(properties.getProperty("open_file")); } } } class Person{ String name; public Person(String name){ this.name = name; } public String toString(){ return name; } }
|
Set