Java集合
1.集合框架的概述
集合、数组都是对多个数据进行存储操作的结构,简称ava容器。
数组在存储多个数据方面的特点:
- —旦初始化以后,其长度就确定了。其长度就不可修改
- 数组一旦定义好,其元素的类型也就确定了。我们也就只能操作指定类型的数据了。
- 数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高。
- 数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。
Java集合可分为Collection和 Map 两种体系
Collection接口:单列数据,定义了存取一组对象的方法的集合
- List:元素有序、可重复的集合
- Set:元素无序、不可重复的集合
Map接口:双列数据,保存具有映射关系“key-value对”的集合
2.Collection接口
方法的使用
public class CollectionTest {
public static void main(String[] args) {
Collection coll = new ArrayList();
//add(Object o) 添加元素
coll.add("aa");
coll.add("bb");
coll.add(123);
coll.add(new Date());
//size() 返回此集合中的元素数
System.out.println(coll.size());//4
//addAll() 将指定集合中的所有元素添加到此集合
Collection coll1 = new ArrayList();
coll1.add(456);
coll.addAll(coll1);
System.out.println(coll.size());//5
//isEmpty() 判断当前集合是否为空
System.out.println(coll.isEmpty());//false
//从此集合中删除所有元素
coll1.clear();
System.out.println(coll1.isEmpty());//true
}
}
强烈建议:向ColLection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals().
public class CollectionTest {
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add(123);
System.out.println(coll.contains(123));//true
//Person p = new Person("Tom", 20);
// coll.add(p);
//System.out.println(coll.contains(p));//true
coll.add(new Person("Tom", 20));
System.out.println(coll.contains(new Person("Tom", 20)));//false.重写equal()方法后,为true
//向ColLection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals().
System.out.println("***********************");
Collection coll1 = new ArrayList();
coll1.add(new Person("Tom", 20));
//如果此集合包含指定 集合中的所有元素,则返回true。
System.out.println(coll.containsAll(coll1));//true
//remove() 从该集合中删除指定元素
System.out.println(coll.remove(123));
System.out.println(coll);
System.out.println("***************************");
//retainAll() 仅保留此集合中包含在指定集合中的元素,也就是交集
coll.retainAll(coll1);
System.out.println(coll);//[Person{name='Tom', age=20}]
}
}
public class CollectionTest {
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add(123);
coll.add("bb");
//hashCode() 返回此集合的哈希码值。
System.out.println(coll.hashCode());
// toArray 实现集合到数组的转换
Object[] objects = coll.toArray();
for (Object object : objects) {
System.out.println(object);
}
// 扩展:数组——>集合
List<String> list = Arrays.asList(new String[] {"aa","bb","cc"});
System.out.println(list);
List<int[]> ints = Arrays.asList(new int[]{123, 456});
System.out.println(ints);//[[I@1b6d3586]
List<Integer> integers = Arrays.asList(123, 456);
System.out.println(integers);//[123, 456]
}
}
3.iterator迭代器
public class IteratorTest {
//用iterator遍历集合
@Test
public void test1(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new String("Tom"));
Iterator iterator = coll.iterator();
// System.out.println(iterator.next());
//hasNest():判断是否有下一个元素
//next():指针下移,取出元素
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
4.List接口
ArrayList LinkedList Vector 三者的异同?
同:三个类都是实现了List接口、存储数据的特点相同:存储有序的、可重复的数据。
异:
ArrayList:作为list接口的主要实现类:线程不安全的,效率高;底层使用Object[] elementDate存储
linkedList:对于频繁的插入、删除操作,使用此类比ArrayList效率高,底层使用双向链表存储
Vector:作为List接口的古老实现类:线程安全的,效率低:底层使用Object[] elementDate存储
ArrayList
//ArrayList的源码分析:jdk 7情况下
ArrayList list = new ArrayList();//底层创建了长度是10的0bject[]数组
eLementDatalist.add(123); //eLementData[e] = new Integer(123);
...
list.add(11);//如果此次的添加导致底层eLementData数组容量不够,则扩容。
//默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
//结论:建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity)
//jdk 8情况下
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
ArrayList list = new ArrayList();//底层object[] elementData初始化为{}.并没有创建长度为10的数组
list.add(123);//第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到elementData...
后续的添加和扩容操作与jdk 7无异。
linkedList
LinkedList list = new LinkedList();内部声明了Node类型的first和last属性,默认值为Node list.add(123);//将123封装到Node中,创建了Node对象。
transient Node<E> first;
transient Node<E> last;
//其中Node定义为:
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
Vector
public Vector() {
this(10);
}
public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
//扩容长度为之前的2倍
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
list接口中的常用方法
void add(int index, object ele):在index位置插入ele元素
boolean addAll(int index, collection eLes): 从index位置开始将eLes中的所有元素添加进来
Object get(int index):获取指定index位置的元素
int indexof(object obj):返回obj在集合中首次出现的位置
int lastIndexof(0bject obj):返回obj在当前集合中末次出现的位置
Object remove(int index):移除指定index位置的元素,并返回此元素
Object set(int index,object ele):设置指定index位置的元素为eLe
List sublist(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
public class CollectionTest {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add("abc");
arrayList.add("abc");
arrayList.add("abc");
//void add(int index, object ele):在index位置插入ele元素
arrayList.add(1,123);
System.out.println(arrayList);//[abc, 123, abc, abc]
//boolean addAll(int index, collection eLes): 从index位置开始将eLes中的所有元素添加进来
List<Integer> integers = Arrays.asList(4, 5, 6);
arrayList.addAll(2, integers);
System.out.println(arrayList);//[abc, 123, 4, 5, 6, abc, abc]
//Object get(int index):获取指定index位置的元素
System.out.println(arrayList.get(0));//abc
//int indexof(object obj):返回obj在集合中首次出现的位置
System.out.println(arrayList.indexOf(123));//1
//int lastIndexof(0bject obj):返回obj在当前集合中末次出现的位置
System.out.println(arrayList.indexOf(123));//1
//Object remove(int index):移除指定index位置的元素,并返回此元素
System.out.println(arrayList.remove(2));
//Object set(int index,object ele):设置指定index位置的元素为eLe
arrayList.set(0, "aaa");
System.out.println(arrayList);
//List sublist(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
System.out.println(arrayList.subList(0, 2));//[aaa, 123]
}
}
5.Set接口
HashSet LinkedHashSet TreeSet
HashSet:作为Set接口的主要实现类:线程不安全的;可以存储null值
LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历,对于频繁的遍历操作,LinkedHashSet效率高于HashSet
TreeSet:可以按照添加对象的指定属性,进行排序。
Set:元素无序、不可重复的集合,如何理解?
(Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法)
无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值。
不可重复性:保证添加的元素按照equals()判断时,不能返回true. 即相同的元素只能添加一个。
public class SetTest {
public static void main(String[] args) {
HashSet hashSet = new HashSet();
hashSet.add(123);
hashSet.add(123);
hashSet.add(123);
hashSet.add(new Person("Tom",20));
hashSet.add(new Person("Tom",20));
hashSet.add("aaa");
hashSet.add("bbb");
Iterator iterator = hashSet.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
我们向HashSet中添加元素q,首先调用元素α所在类的hashcode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否己经有元素:
如果此位置上没有其他元素,则元素α添加成功
如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
如果hash值不相同,则元素α添加成功。
如果hash值相同,进而需要调用元素a所在类的equlas()方法:
equaLs()返回true,元素α添加失败
equais()返回false,则元素α添加成功。
使用Set接口添加数据,一定要重写hashCode和equal方法。
LinkedHashSet
public class SetTest {
public static void main(String[] args) {
HashSet hashSet = new LinkedHashSet();
hashSet.add(123);
hashSet.add(123);
hashSet.add(123);
hashSet.add(new Person("Tom",20));
hashSet.add(new Person("Tom",20));
hashSet.add("aaa");
hashSet.add("bbb");
Iterator iterator = hashSet.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
TreeSet
TreeSet 必须添加的是同类的对象
自然排序
public class Person implements Comparable{
private String name;
private int age;
public Person(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 "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
System.out.println("equals-------------");
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
//自然排序
@Override
public int compareTo(Object o) {
if(o instanceof Person){
Person person = (Person)o;
int compar = this.name.compareTo(person.name);
if(compar!=0){
return compar;
}else {
return Integer.compare(this.age, person.age);
}
}else {
throw new RuntimeException("输入的类型不匹配!");
}
}
}
public class SetTest {
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();
// 失败 必须是同类的对象
// treeSet.add("aaa");
// treeSet.add(111);
// treeSet.add("ccc");
// treeSet.add(123);
// treeSet.add(456);
// treeSet.add(789);
// 添加对象时,要确定对比方式
treeSet.add(new Person("Tom", 20));
treeSet.add(new Person("Aray", 21));
treeSet.add(new Person("Alice", 22));
treeSet.add(new Person("Ben", 23));
treeSet.add(new Person("Tin", 24));
// 出项姓名相同的数据
treeSet.add(new Person("Tin", 25));
Iterator iterator = treeSet.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
定制排序
public class SetTest {
public static void main(String[] args) {
Comparator comparator = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Person&& o2 instanceof Person){
Person person1 = (Person) o1;
Person person2 = (Person) o2;
int comper = person1.getName().compareTo(person2.getName());
if(comper!=0){
return comper;
}else {
return Integer.compare(person1.getAge(),person2.getAge());
}
}
throw new RuntimeException("输入的类型不匹配");
}
};
TreeSet treeSet = new TreeSet(comparator);
treeSet.add(new Person("Tom", 20));
treeSet.add(new Person("Aray", 21));
treeSet.add(new Person("Alice", 22));
treeSet.add(new Person("Ben", 23));
treeSet.add(new Person("Tin", 24));
treeSet.add(new Person("Tin", 25));
Iterator iterator = treeSet.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
6.Map接口
Map:双列数据,存储key-value对的数据
HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null的key 和vaLue
LinkedHashMap: 保证在遍历Map元素时,可以按照添加的顺序遍历排序,对于频繁的遍历操作,LinkedHashMap的效率高于HashMap。
TreeMap:可以按照添加的顺序遍历排序,底层使用的红黑树
Hashtable:作为古老的实现类,线程安全的,效率低;不能存储null的key和value
Properties:常用来处理配置文件。key和value都是String类型
HashMap的底层:
数组+链表(jdk7及之前)
数组+链表+红黑树(jdk 8)
Map 中的key:无序的、不可重复的,使用set存储所有的key
Map中的vaLue:无序的、可重复的,使用collection存储所有的value
—个键值对: key-value构成了一个Entry对象。
Map中的entry:无序的、不可重复的,使用Set存储所有的entry
HashMap的底层实现原理?
以jdk7为例说明: HashMap map = new HashMap():
在实例化以后,底层创建了长度是16的一维数组Entry[] table
…可能已经执行过多次put. . .
map. put(key1, vaLue1):
首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置。如果此位置上的数据为空,此时的key1-vaLue1添加成功。----情况1
如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值:
如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-vaLue1添加成功。----情况2
如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:
调用key1所在类的equals( key2)
如果equals()返回false:此时key1-value1添加成功。----情况3
如果equats()返回true:使用vaLue1替换value2。
补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。
在不断的添加过程中,会涉及到扩容问题,默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来。
jdk8相较于jdk7在底层实现方面的不同:
- new HashMap(():底层没有创建一个长度为16的数组
- jdk8底层的数组是:Node[].而非Entry[ ]
- 首次调用put()方法时,底层创建长度为16的数组
- jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树。
当数组的某一个索引位置上的元素以链表形式存在的数据个数>8且当前数组的长度>64时,此时此索引位置上的所有数据改为使用红黑树存储。
public HashMap() {
//loadFactor 加载因子 0.75
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
transient Node<K,V>[] table;
DEFAULT_INITIAL_ CAPACITY : HashMap的默认容量,16
DEFAULT_LOAD_FACTOR: HashMap的默认加载因子:0.75
threshold:扩容的临界值,=容量*填充因子:16*0.75 =>12
TREETFY_ THRESHOLD: Bucket中链表长度大于该默认值,转化为红黑树:8
MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64
map中的常用方法
object put(0bject key,object value):将指定key-value添加到(或修改)当前map对象中
void putALL(Map m):将m中的所有key-value对存放到当前map中
object remove(object key):移除指定key 的key-value对,并返回value
void clear():清空当前map中的所有数据
public class MapTest {
public static void main(String[] args) {
HashMap hashMap = new HashMap();
//put 添加
hashMap.put("aa", 12);
hashMap.put(12, 20);
hashMap.put("bb", 56);
hashMap.put("aa", 78);//修改
System.out.println(hashMap);//{aa=78, bb=56, 12=20}
//void putALL(Map m):将m中的所有key-value对存放到当前map中
HashMap map1 = new HashMap();
map1.put("cc", 20);
hashMap.putAll(map1);
System.out.println(hashMap);//{aa=78, bb=56, cc=20, 12=20}
//object remove(object key):移除指定key 的key-value对,并返回value
// System.out.println(hashMap.remove("aa"));//78
System.out.println(hashMap);//{bb=56, cc=20, 12=20}
//void clear():清空当前map中的所有数据
map1.clear();
System.out.println(map1.isEmpty());
System.out.println(map1.size());
}
}
//元素查询的操作:
object get(object key):获取指定key对应的value
boolean containskey (0bject key):是否包含指定的key
boolean containsvaLue(0bject value):是否包含指定的vaLue
int size():返叵map 中key-value对的个数
boolean isEmpty():判断当前map是否为空
boolean equals(0bject obj):判断当前map和参数对象obj是否相等
public class MapTest {
public static void main(String[] args) {
HashMap hashMap = new HashMap();
hashMap.put("aa", 12);
hashMap.put(12, 20);
hashMap.put("bb", 56);
//object get(object key):获取指定key对应的value
System.out.println(hashMap.get("aa"));//12
//boolean containskey (0bject key):是否包含指定的key
System.out.println(hashMap.containsKey("aa"));//true
//boolean containsvaLue(0bject value):是否包含指定的vaLue
System.out.println(hashMap.containsValue(56));//true
//boolean equals(0bject obj):判断当前map和参数对象obj是否相等
HashMap hashMap2 = new HashMap();
hashMap2.put("aa", 12);
hashMap2.put(12, 20);
hashMap2.put("bb", 56);
System.out.println(hashMap.equals(hashMap2));//true
}
}
元视图操作的方法:
set keyset():返回所有key构成的Set集合
collection values():返回所有value构成的collection集合
set entryset():返回所有key-value对构成的Set集合
public class MapTest {
public static void main(String[] args) {
HashMap hashMap = new HashMap();
hashMap.put("aa", 12);
hashMap.put(12, 20);
hashMap.put("bb", 56);
//set keyset():返回所有key构成的Set集合
System.out.println(hashMap.keySet());//[aa, bb, 12]
Set set = hashMap.keySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
//collection values():返回所有value构成的collection集合
System.out.println(hashMap.values());//[12, 56, 20]
Collection values = hashMap.values();
// Iterator iterator1 = values.iterator();
// while (iterator1.hasNext()){
// System.out.println(iterator1.next());
// }
for (Object value : values) {
System.out.println(value);
}
//set entryset():返回所有key-value对构成的Set集合
System.out.println(hashMap.entrySet());//[aa=12, bb=56, 12=20]
Set set1 = hashMap.entrySet();
Iterator iterator1 = set1.iterator();
while (iterator1.hasNext()){
Object next = iterator1.next();
Map.Entry entry = (Map.Entry) next;
System.out.println(entry.getKey()+"——>"+entry.getValue());
}
}
}
Properties
新建db.properties配置文件
name=happyboy
password=123456
public class PropertiesTest {
public static void main(String[] args) {
FileInputStream fileInputStream = null;
try {
Properties properties = new Properties();
fileInputStream = new FileInputStream("db.properties");
properties.load(fileInputStream);
String name = properties.getProperty("name");
String password = properties.getProperty("password");
System.out.println(name+"——>"+password);//happyboy——>123456
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if(fileInputStream!=null){
try {
fileInputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
7.Collections工具类
Collections是一个操作Set、List和l Map等集合的工具类
Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法
reverse(List):反转List中元素的顺序
shuffLe(list):对List集合元素进行随机排序
sort(List):根据元素的自然顺序对指定List集合元素按升序排序
sort(List,Comparator):根据指定的Comparator产生的顺序对List集合元素进行排序
swap(List, int,int):将指定list集合中的i处元素和j处元素进行交换
public class CollectionsTest {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(125);
list.add(12);
list.add(56);
list.add(192);
//sort(List):根据元素的自然顺序对指定List集合元素按升序排序
Collections.sort(list);
System.out.println(list);//[12, 56, 125, 192]
Collections.reverse(list);
//reverse(List):反转List中元素的顺序
System.out.println(list);//[192, 125, 56, 12]
//shuffLe(list):对List集合元素进行随机排序
Collections.shuffle(list);
System.out.println(list);//[12, 192, 56, 125]
//swap(List, int,int):将指定list集合中的i处元素和j处元素进行交换
Collections.swap(list, 0, 1);
System.out.println(list);//[192, 12, 56, 125]
}
}
Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素Object max(Collection,Comparator):根据Comparator指定的顺序,返回给定集合中的最大元素
Object min(Collection)
Object min(Collection,Comparator)
int frequency(Collection,Object):返回指定集合中指定元素的出现次数
void copy(List dest,List src):将src中的内容复制到dest中
boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List对象的所有旧值
public class CollectionsTest {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(125);
list.add(12);
list.add(56);
list.add(56);
list.add(192);
//int frequency(Collection,Object):返回指定集合中指定元素的出现次数
System.out.println(Collections.frequency(list, 56));//2
//ArrayList list1 = new ArrayList();
/*
if (srcSize > dest.size())
throw new IndexOutOfBoundsException("Source does not fit in dest");
* */
// 错误写法
// Collections.copy(list1, list);
List<Object> list2 = Arrays.asList(new Object[list.size()]);
Collections.copy(list2, list);
System.out.println(list2);//[125, 12, 56, 56, 192]
//boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List对象的所有旧值
Collections.replaceAll(list, 56, 0);
System.out.println(list);//[125, 12, 0, 0, 192]
}
}