现在的程序中,怎么存放多个数据? ------- 在学习集合之前就知道 数组
数组缺点:只能存放相同类型的数据。长度不可变。遍历方式单一。
由于数组存在缺点,所以产生了多种集合。
集合类存放于java.util包中;
集合类存放的都是对象的引用,而非对象本身,出于表达上的便利,我们称集合中的对象就是指集合中对象的引用(reference);
集合类型主要有3种:set(集)、list(列表)和map(映射);
集合接口分为:Collection和Map,list、set实现了Collection接口
一、Collection
(一)概述
英文名称Collection,是用来存放对象的数据结构,其长度可变,而且集合中可以存放不同类型的对象,并
提供了一组操作成批对象的方法。数组的缺点:长度是固定不可变的,访问方式单一,插入。删除等操作繁琐。
(二)Collection继承结构
Collection接口
list接口:数据有序,可以重复
ArrayList子类
LinkedList子类
Set接口:数据无序,不可以存重复值
HashSet子类
Map接口:键值对存数据
HashMap
Conllections工具类
(三)常用方法
package cn.tedu.collection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
//这个类用来测试Collection接口
public class Test_Collection {
public static void main(String[] args) {
//1、创建对象
Collection<String> col = new ArrayList<>();
//2、常用方法
col.add("jack");//添加元素
col.add("韩美美");
col.add("rose1");
col.add("rose2");
// col.add(100);
// col.clear();//清空集合
System.out.println(col.contains("jack"));//判断集合中是否包含指定元素
System.out.println(col.equals("韩美美")+"----");//判断集合是否和指定元素相等
System.out.println(col.hashCode());//返回集合对象在内存中的哈希码值
System.out.println(col.isEmpty());//判断是否为空,空返回true,不为空返回false
System.out.println(col.remove("rose1"));//移出某个元素,返回是否删除成功
System.out.println(col.size());//获取集合的长度
System.out.println(Arrays.toString(col.toArray()));//把集合转成Object[]数组
//集合间的操作
Collection<String> col2 = new ArrayList<>();
col2.add("1");
col2.add("2");
System.out.println(col.addAll(col2));//向col集合中添加col2集合的元素
System.out.println(col.containsAll(col2));//判断col中是否包含col2的元素
// System.out.println(col.removeAll(col2));//删除col集合中的col2的元素
// System.out.println(col.retainAll(col2));//删除col集合的元素
System.out.println(col);
//迭代器:迭代/遍历集合中的元素
Iterator it = col.iterator();
while (it.hasNext()) {//hasNext()判断集合中有下一个元素吗
System.out.println(it.next());//next()获取遍历到的元素
}
}
}
二、list接口
(一)概述
1.有序的Collection(也成为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制、
用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素;
2.与set不同,列表通常允许重复的元素;
(二)特点
1.数据有序
2.允许存放重复数组
3.元素都有缩影
(三)常用方法
package cn.tedu.collection;
import java.util.HashSet;
import java.util.Iterator;
//这个类用来测试Set集合去重
/*
* 总结:
* 如果想要使用set集合,给你的自定义对象去重的话,需要同时重写hashCode()和equals()
* 重写hashCode():原来的实现方式是:只要new就会给计算一个新的哈希码值
* 现在需要按照对象的属性值计算哈希码值,就算new两次,属性值一样时也必须产生相同哈希码值
* 重写equals():原来比的是两个对象间的地址值,现在要去比较两个对象间的属性值,如果属性值都一样,必须
*/
public class Test_Set {
public static void main(String[] args) {
HashSet<Student> set = new HashSet<>();
//创建元素
Student s1 = new Student("西门庆",20);
Student s2 = new Student("武大郎",19);
Student s3 = new Student("潘金莲",21);
Student s4 = new Student("小龙女",23);
//添加时,新元素会和老元素比
set.add(s1);
set.add(s2);
set.add(s3);
set.add(s4);
//往set中添加重复元素
Student s5 = new Student("武大郎",19);
Student s6 = new Student("潘金莲",21);
//默认:添加时查找对象的hash值,没有查到就存起来
//所以必须让hash值一致才可以
set.add(s5);
set.add(s6);
//问题1:属性相同时还是认为是两个对象...
System.out.println(set);
// System.out.println(s2.hashCode());
// System.out.println(s5.hashCode());
//遍历
Iterator it = set.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
//创建Student类/
class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
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 + "]";
}
//需求:属性值都一样就看做是同一个对象
@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;
}
//HashSet默认:添加时查找对象的hash值,没有查到就存起来
//所以必须让hash值一致才可以
//必须用算法,不然的话,hash值相同时会挂一串
public int hashCode() {
//return 0;//效率低
//让基本类型*31,引用类型就用自己的hash值
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
}
三、ArrayList子类
(一)概述
1.存在于java util包中
2.内部用数组存放数据,封装了数组的操作,每个对象都有下标
3.内部数组默认初识容量是10。如果不构会以1.5倍容量增长、
4.查询快,增删数据效率会降低
(二)创建对象
new ArrayList();//初始容量是10
(三)测试案例
package cn.tedu.collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
//这个类用来测试ArrayList
public class Test_ArrayList {
public static void main(String[] args) {
//1、创建对象
/*
* ArrayList特点:
* 1、底层是个数组,所以适合查询,不适合增删
* 2、底层会为数组自动扩容,1.5倍方式扩容:
*/
List<String> list = new ArrayList<>();
//2、常见方法
list.add("aaa");//存入数据
list.add("123");
list.add("ccc");
list.add("ddd");
System.out.println(list);//list中内容
System.out.println(list.size());//集合长度
System.out.println(list.get(1));//根据下标获取元素
System.out.println();
System.out.println(list.remove(2));//移除下标对应的元素
System.out.println(list);
//下标遍历
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i));
}
//Iterator迭代遍历,封装了下标
Iterator<String> it = list.iterator();
while (it.hasNext()) {//如果有数据
String s = it.next();//一个一个向后遍历
System.out.println(s);
}
}
}
四、LinkedList子类
(一)概述
双向链表,两端效率高。底层就是数组和链表实现的。
(二)特点
1.底层维护了一个链表结构;
2.适用于增删多而查询少的场景;
3.对与链表中的节点操作效率并不高,但是对与首尾两个节点操作效率高。
(三)创建对象
LinkedList<E> list = new LinkedList<>();
(四)常见方法
package cn.tedu.collection;
import java.util.Iterator;
import java.util.LinkedList;
public class Test_LinkedList {
public static void main(String[] args) {
//1、创建对象
//特点:和list一样,元素有序,可以重复
LinkedList<Integer> list = new LinkedList<>();
//2、常用方法
list.add(100);
list.add(200);
list.add(300);
list.add(400);
list.add(500);
System.out.println(list);
list.addFirst(0);//添加首元素
list.addLast(99);//添加尾元素
System.out.println(list);
System.out.println(list.getFirst());//获取首元素
System.out.println(list.getLast());//获取尾元素
System.out.println(list+"=====");
System.out.println(list.removeFirst());//移出首元素
System.out.println(list.removeLast());//移出尾元素
System.out.println(list);
//3、查询效率for iterator
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
System.out.println("============");
//迭代查询
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
// System.out.println(it.next());
Integer in=it.next();
System.out.println(in);
}
}
}
五、Set接口
(一)概述
1.一个不包含重复元素的Collection;
2.数据无序(因为set集合没有下标);
3,由于集合中的元素不可以重复,常用与给数据去重;
(二)特点
1.HashSet:底层是哈希表,包装了HashMap,相当于向HashSet中存入数据时,会把数据作为K,存入内部
的HashMap中,当然K仍然不需重复;
2.TreeSet:底层就是TreeMap,也是红黑树形式,便于查找数据
3.HashMap:事项总,当hash值相同的对象,会在同一个hash值得位置存储不用属性的数据。
(三)常用方法
package cn.tedu.collection;
import java.util.HashSet;
import java.util.Iterator;
//这个类用来测试Set集合去重
/*
* 总结:
* 如果想要使用set集合,给你的自定义对象去重的话,需要同时重写hashCode()和equals()
* 重写hashCode():原来的实现方式是:只要new就会给计算一个新的哈希码值
* 现在需要按照对象的属性值计算哈希码值,就算new两次,属性值一样时也必须产生相同哈希码值
* 重写equals():原来比的是两个对象间的地址值,现在要去比较两个对象间的属性值,如果属性值都一样,必须
*/
public class Test_Set {
public static void main(String[] args) {
HashSet<Student> set = new HashSet<>();
//创建元素
Student s1 = new Student("西门庆",20);
Student s2 = new Student("武大郎",19);
Student s3 = new Student("潘金莲",21);
Student s4 = new Student("小龙女",23);
//添加时,新元素会和老元素比
set.add(s1);
set.add(s2);
set.add(s3);
set.add(s4);
//往set中添加重复元素
Student s5 = new Student("武大郎",19);
Student s6 = new Student("潘金莲",21);
//默认:添加时查找对象的hash值,没有查到就存起来
//所以必须让hash值一致才可以
set.add(s5);
set.add(s6);
//问题1:属性相同时还是认为是两个对象...
System.out.println(set);
// System.out.println(s2.hashCode());
// System.out.println(s5.hashCode());
//遍历
Iterator it = set.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
//创建Student类/
class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
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 + "]";
}
//需求:属性值都一样就看做是同一个对象
@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;
}
//HashSet默认:添加时查找对象的hash值,没有查到就存起来
//所以必须让hash值一致才可以
//必须用算法,不然的话,hash值相同时会挂一串
public int hashCode() {
//return 0;//效率低
//让基本类型*31,引用类型就用自己的hash值
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
}
(六)HashSet子类
(一)概述
此类实现Set接口,有哈希表(实际上是一个HashMap实例)支持
他不保证set的迭代顺序;特别是他不保证顺序恒久不变。
此类允许使用null元素。
(二)创建对象
Set<E> set = new HashSet<>();
(三) 常用方法
package cn.tedu.collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Test_HashSet {
public static void main(String[] args) {
//1、创建对象
Set<Integer> set = new HashSet<>();
//2、常用方法
set.add(10);
set.add(2);
set.add(3);
set.add(4);
set.add(5);
set.add(4);
set.add(5);
//set集合的特点:元素无序,元素不能重复
System.out.println(set);
//TODO 测试常用方法,与collection一样
//迭代遍历
Iterator it = set.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
(七) Map接口
(一)概述
1.java util接口key<K,V)
2.类型参数:K-此映射所维护的键的类型,V-映射值得类型。
3.也叫哈希表、散列表。常用于存 键值对 结构的数据。其中的键不能重复,值可以重复
(二)特点
可以根据键 提取对应的值;
键不允许重复,如果重复值会被覆盖;
存放的都是无序数据
初始容量是16,默认的加载因子是0.75
(继承结构)
(四)常用方法
package cn.tedu.Map;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
//这个类用来测试Map接口
public class Test_Map {
public static void main(String[] args) {
//1、创建map对象
Map<Integer, String> map = new HashMap<>();
//2、常用方法
//向map集合中添加数据时,需要同时指定key和value的值
map.put(9529, "秋香");
map.put(9527, "唐伯虎");
map.put(9528, "如花");
map.put(9528, "石榴姐");
/*
* 特点:
* 1、数据无序
* 2、当key相同时,value值被覆盖
*/
System.out.println(map);
// map.clear();
System.out.println(map.containsKey(9527));//是否包含指定的key
System.out.println(map.containsValue("如花"));//是否包含指定的value
System.out.println(map.equals("石榴姐"));//判断两者是否相等
System.out.println(map.get(9528));//根据指定的key,找到对应的value
System.out.println(map.hashCode());//计算map在内存中的哈希码值
System.out.println(map.isEmpty());//判断集合是否为空
System.out.println(map.size());//返回集合的长度
System.out.println(map.remove(9527));//根据key删除元素,并把value返回
Collection<String> col = map.values();//把map中的所有值获取到并存入collection中
System.out.println(col);//打印
//迭代map集合
// Set<E> keySet()
// Set<Map.Entry<K, V>> entrySet()
//1、把所有的key放入set中,遍历set
Set<Integer> set = map.keySet();//把所有的key放入set中
//foreach可以用在数组遍历时,或者List遍历时,或者Set遍历时
for (Integer integer : set) {
String value = map.get(integer);
System.out.println("foreach遍历:key-"+integer+",value-"+value);
}
Iterator<Integer> it = set.iterator();
while (it.hasNext()) {
Integer key = (Integer) it.next();//获取set中的元素,也就是每个key
String value = map.get(key);//拿着每个key去map里面找对应的value
System.out.println(key +"===="+ value);
}
//2、把所有的key和value封装成Entry对象,并放入set中,遍历set
Set<Entry<Integer, String>> set2 = map.entrySet();
Iterator<Entry<Integer, String>> it2 = set2.iterator();
while (it2.hasNext()) {
Map.Entry<Integer, String> entry = (Map.Entry<Integer, String>) it2
.next();//遍历set得到每个entry对象
Integer key = entry.getKey();//获取每个key
String value = entry.getValue();//获取每个value
System.out.println(key+"----"+value);
}
}
}
(八) HashMap
hashMap的键要是用时重写hashCode()和equals()
hashCode()用来判断确定hash值是否相同
equals()用来判断属性的值是否相同
equals()判断数据如果相等,hashCode()必须相同
equals()判断数据如果不等,hashCode()尽量不同
(一)概述
基于哈希表的Map接口实现。次实现提供所有可选的映射操作,并允许使用Null值和null键
hashMap底层是一个Entry数组,当存放数据时会根据hash算法计算数据的存放位置;
算法:hash(key)%n,n就是数组的长度‘
当计算的位置没有数据时,就直接存放,当计算有数据时也就是发生hash冲突的时候、hash碰撞时,采用链表的方式来解决的,再对应的数据位置存放链表的头节点;
对链表而言,新加入的节点会从头节点加入;
(原理)
当需要向map中存储元素时,底层会根据hash算法计算对象的存储位置;
如果这个位置上没有存过元素,就直接存放,就相当于直接作为数组的元素存在;
如果这个位置上存过数据,就会形成链表的结构–hash碰撞/冲突;
我们尽量要让每个对象拥有自己的哈希值,尽量放在数组结构上。避免链表的形成,因为链 表查询效率低。
package cn.tedu.Map;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class Test_HashMap {
public static void main(String[] args) {
//1、创建对象
Map<String, String> map = new HashMap<>();
//2、存数据
map.put("1", "jack");
map.put("2", "rose");
map.put("3", "tom");
map.put("4", "jerry");
//3、迭代map集合 -- keySet() entrySet()
Set<String> set = map.keySet();//把key放在set里
for (String key : set) {//遍历set得到每个key
String value = map.get(key);
System.out.println(key + "-----" + value);
}
//把map里的每条记录封装成Entry对象存入set
Set<Entry<String,String>> set2 = map.entrySet();
for (Entry<String, String> entry : set2) {//遍历set得到每个Entry对象
System.out.println(entry.getKey()+ "====="+ entry.getValue());
}
}
}
(四)统计字符串中出现的次数
package cn.tedu.Map;
import java.util.HashMap;
import java.util.Scanner;
public class Test2_Map {
public static void main(String[] args) {
//1、接收用户输入的字符串
String input = new Scanner(System.in).nextLine();
//定义map存数据 -- 类似于a=1,b=2,c=5
HashMap<Character, Integer> map = new HashMap<>();
//2、遍历字符串,获取每个字符
/*
* 字符串长度用:length()
* 数组长度用:size()
*/
for (int i = 0; i < input.length(); i++) {
char key = input.charAt(i);//根据下标获取对应的字符
//3、统计字符出现过几次 -- map里的value是多少
Integer value = map.get(key);
if (value == null) {//如果之前没有存过字符,value存的就是默认值null
map.put(key, 1);//把value中的默认值null改成1,说明第一次统计这个字符的次数
}else {//如果value不是默认值,也就是之前统计过了
map.put(key, value+1); //把value的值改成原来的数字+1,就ok了
}
}
System.out.println(map);
}
}
(九)Collections工具类
(一)常用方法
1.Collections.sort(List<> list):根据元素的自然顺序 对指定列表按升序进行排序;
2.Collections.max():根据元素的自然顺序,返回给定 collection 的最大元素;
3.Collections.min():根据元素的自然顺序 返回给定 collection 的最小元素;
4.Collections.swap(List,i,j):在指定列表的指定位置处交换元素;
5.Collections.addAll():
(二)测试案例
package seday12;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Test4_Collections {
public static void main(String[] args) {
List<String> list = new ArrayList();
//添加多个元素
Collections.addAll(list,
"3","30","23","15","29","12","26");
//元素排序
Collections.sort(list);
//默认是字符顺序:[12,15,23,26,29, 3, 30]
System.out.println(list);
//自己定义比较方式
Collections.sort(list, new Comparator<String>() {
//自定义比较器,sort()自动调用
@Override
public int compare(String o1, String o2) {
//把字符串转成int比大小
int a = Integer.parseInt(o1);
int b = Integer.parseInt(o2);
//o1大是正数,o1小是负数,相等是0
return a-b;
}
});
System.out.println(list);
}
}