容器
文章目录
容器与数组的概念比较类似,数组用于存储多个数据,特点有定长,长度一旦确定不可改变;数据类型要一致;数组是有序的,有索引。
自定义容器类:只能存储字符串类型的数据,容器的大小可以随着数据的多少动态的改变。
public class AppDemo01 {
public static void main(String[] args) {
Container c=new Container();
//添加功能
c.add("蜡笔小新");
c.add("柯南");
c.add("海贼王");
System.out.println(c.size());
//获取方法
System.out.println(c.get(0));;
System.out.println(c.get(1));;
System.out.println(c.get(2));;
System.out.println("-------------");
//删除
c.romove(1);
System.out.println(c.get(0));;
System.out.println(c.get(1));;
System.out.println(c.size());
}
}
//自定义容器类
class Container{
//容器中存储数据的数组 永远指向最新的当前的数据对象
private String[] arr;
//容器中数据的个数
private int size;
public Container() {
arr=new String[0];
}
/**
* 删除数据方法
* @param i : 索引
*/
public void romove(int index) {
if(index<0 || index>=size){
throw new ArrayIndexOutOfBoundsException(index+"索引越界啦");
}
//备份原数组
String[] temp=arr;
arr=new String[size-1];
for(int i=0;i<size;i++){
if(i>=index){
if(i==index){
continue;
}else if(i>index){
arr[i-1]=temp[i];
}
}else{
arr[i]=temp[i];
}
}
size--;
}
//判断索引
public String get(int index) {
if(index<0 || index>=size){
throw new ArrayIndexOutOfBoundsException(index+"索引越界啦");
}
return arr[index];
}
/*
* 添加方法
*/
public void add(String value) {
//备份原数组
String[] temp=arr;
arr=new String[size+1];
//数组的拷贝
for(int i=0;i<size;i++){
arr[i]=temp[i];
}
arr[size]=value;
size++;
}
//长度
public int size(){
return this.size;
};
}
一、Collection接口
Collection容器体系的父接口,定义容器的一些规则。
根据动态根据数据的大小改变容量
只能存储引用类型的数据,如果要存储基本数据类型会进行自动装箱
可以存储任意类型的数据
public class CollectionDemo01 {
public static void main(String[] args) {
//容器对象
Collection con=new ArrayList();
Collection con2=new ArrayList();
Collection con3=new ArrayList();
//能够调用的必须是Collection接口中方法
//1.add
con.add(1);
con.add("啊哈哈");
con.add(false);
con2.add('a');
con2.add('b');
con3.add('a');
con3.add('b');
System.out.println(con);
//addAll()
con.addAll(con2);
System.out.println(con);
//2.clear() 清除全部
//con.clear();
//boolean remove(Object o)
con.remove(false);
System.out.println(con);
//3.contains() 判断是否包含
System.out.println(con.contains("啊哈哈"));
//4.equals()
System.out.println(con2.equals(con3));
//5.求交集
con.retainAll(con2);
System.out.println(con);
System.out.println(con.size());
//6.toArray
System.out.println(Arrays.toString(con.toArray()));
}
}
容器的遍历,可以使用增强for和迭代器遍历。
public class CollectionDemo02 {
public static void main(String[] args) {
//容器对象
//泛型 : 增强容器的稳定性和可读性
//规范容器中所有数据的数据类型
Collection<String> con=new ArrayList();
con.add("哈哈");
con.add("呵呵");
con.add("嘿嘿");
//con.add(1);
//1.增强for
for(String o:con){
System.out.println(o);
}
//2.迭代器 Iterator<E> iterator()
//1)获取迭代器对象
Iterator<String> it=con.iterator();
//2)循环判断是否存在下一个元素
while(it.hasNext()){
//3)如果存在,就获取
System.out.println(it.next());
}
}
}
Set接口
Set接口实现了Collection接口,无序且不可重复,没有新增方法,完全使用Collection中的方法,遍历方式:增强for和迭代器。
public class SetDemo01 {
public static void main(String[] args) {
//无序: 添加顺序和内部真实存储的顺序不一定相同,一旦存储不会改变
Set<String> set=new HashSet();
set.add("哈哈");
set.add("嘻嘻");
set.add("123");
set.add("abc");
set.add("呵呵");
set.add("啦啦");
set.add("嘿嘿");
System.out.println(set);
}
}
//输出:[123, abc, 哈哈, 嘻嘻, 呵呵, 啦啦, 嘿嘿]
1.TreeSet
底层实现:红黑树(二叉树的一种),是由TreeMap维护
特点:有序的,默认升序排序,查询等效率高,自动去重
新增方法:E ceiling(E e) 返回大于等于参数的元素,没有返回null,还有很多类似的新增方法。
存储自定义的引用数据类型的数据:必须实现内部比较器/外部比较器–>去重+排序
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet<Double> tree = new TreeSet();
tree.add(3.1);
tree.add(4.5);
tree.add(1.1);
tree.add(6.3);
tree.add(2.9);
tree.add(5.4);
System.out.println(tree);
System.out.println(tree.ceiling(5.5));//输出比输入的值大的数
////如果存储自定义的引用数据类型: 1)默认比较规则 2)必须指定比较规则
TreeSet<Person> set = new TreeSet();
//TreeSet<Person> set=new TreeSet((o1,o2)-> ((Person)o2).getName()-((Person)o1).getAge());指定比较规则
set.add(new Person("张三",10));
set.add(new Person("李四",12));
set.add(new Person("李四",13));
set.add(new Person("李四",14));
set.add(new Person("王五",11));
System.out.println(set);
//当外部比较器是使用年龄排序的结果:[Person [name=张三, age=10], Person [name=王五, age=11], Person [name=李四, age=12], Person [name=李四, age=13], Person [name=李四, age=14]]
}
}
//也可以重写Person类中的compareTO()方法
@Override
public int compareTo(Object o) {
//使用名字比较,有自带的比较规则
return this.name.compareTo(o.name);
}
2.HashSet
HashSet:无序 去重
底层实现:有哈希表(数组+链表+红黑树)结构实现的,内部是由HashMap维护
优点:查询,增加,删除效率高
缺点:无序,去重
Hash算法:
1.使用算法对数据进行计算,决定其存储在数组中的位置,位置=数据%9
,在程序中就使用hashCode()
方法来计算值。
2.找到对应的位置存储数据,位置中的数据默认链表的结构连接,先调用equals()方法判断位置中是否已经存在相同的数据,如果存在就去重,如果不存在就存放;如果位置中的数据大于8个,那么就转为红黑树的结构存储数据。
3.相同的数据,hashCode()结果一定相同;hashCode()结果相同的,数据不一定相同。
自定义类型对象去重:(所有成员属性的值都相同的对象,就是一个对象,应该去重)
重写hashCode()和equals()方法
如果hashcode()值不相同,不会调用equals()方法进一步比较内容
public class HashSetDemo03 {
public static void main(String[] args) {
HashSet<Person> set=new HashSet();
set.add(new Person("张三",10));
set.add(new Person("李四",12));
set.add(new Person("赵六",13));
set.add(new Person("赵六",13));
set.add(new Person("赵六",13));
set.add(new Person("王五",11));
System.out.println(set);
//去重输出:[Person [name=王五, age=11], Person [name=张三, age=10], Person [name=赵六, age=13], Person [name=李四, age=12]]
}
}
//重写hashCode()和equals()方法
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) 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;
}
二、List接口
List接口是有序可重复的,新增了一些根据索引操作的功能
public class ListDemo01 {
public static void main(String[] args) {
//使用引用数据类型
List<Integer> list=new ArrayList();
list.add(3);
list.add(0);
list.add(1);
list.add(2);
list.add(1);
//指定索引位置添加数据
list.add(2, 4);
//get(index)
System.out.println(list.get(1));
System.out.println(list);
//indexOf lastIndexOf
System.out.println(list.indexOf(1));
System.out.println(list.lastIndexOf(1));
//remove(index)
System.out.println(list.remove(3)); //如果出现数据和索引类型相同情况,优先决定是索引
System.out.println(list);
//set(int index, E element)
System.out.println(list.set(3, 333));
System.out.println(list);
//subList 结束索引获取不到
System.out.println(list.subList(1, 3));
}
}
List接口的遍历方式可以使用增强for、迭代器和普通for。
public class ListDemo02 {
public static void main(String[] args) {
List<Integer> list=new ArrayList();
list.add(3);
list.add(0);
list.add(1);
list.add(2);
list.add(1);
//普通for遍历
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
}
List接口下所有的实现类都是有序的可重复的
1.ArrayList
该类的底层实现是用数组/可变数组实现的,在内存中一块连续的内存空间。
优点:根据索引遍历,获取效率高
缺点:添加、删除操作需要大量的拷贝数据,效率低
扩容机制:根据空构造器创建对象,默认构建空数组,当第一次添加以后才会扩容,初始容量是10,扩容机制每次扩容的是原容量的1.5倍(int newCapacity = oldCapacity + (oldCapacity >> 1);
使用了copyOf
方法进行动态扩容
推荐使用的情况:大量做查询多推荐使用,做增删多不推荐使用
//创建一个存储字符的ArrayList对象
ArrayList<Character> list=new ArrayList();
/*
* ArrayList(Collection<? extends E> c) 构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。
* ArrayList(int initialCapacity) 构造一个具有指定初始容量的空列表。
*/
//添加数据到列表的尾部
list.add('a');
System.out.println(list.size());
//使用ArrayList存储对象类型数据,并使用常用方法测试
//创建一个存储引用类型的ArrayList对象
ArrayList<Person> persons=new ArrayList();
//添加信息
persons.add(new Person("金珍",26));
persons.add(new Person("田国",23));
persons.add(new Person("闵其",25));
//输出列表信息
System.out.println(persons);
//查找该信息第一次出现的索引位置
System.out.println(persons.indexOf(new Person("闵其",25))); //默认调用equals()方法比较,需要在Person类中重写
//重写equals()方法
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
2.Vector
Vector:向量
Vector和ArrayList非常像
Vector和ArrayList的区别:
1.Vector线程安全,ArrayList线程不安全
2.每次扩容原容量的2倍,ArrayList是1.5倍,更有效的节省内存
3.LinkedList
LinkedList的底层实现是使用双向链表的。
优点:做增删效率高
缺点:根据索引获取或遍历效率低(本身链表结构不存在索引,模拟索引使用)
使用推荐:大量做增删推荐使用LinkedList,大量做查询推荐使用ArrayList
新增方法:添加了一写操作与链表头尾的方法
自定义LinkedList:使用简单的单向链表实现
链表结构中的每一个数据元素都是以字节为单位的,单项链表的节点中存储当前数据和下一个节点的地址。
public class LinkListDemo02 {
public static void main(String[] args) {
MyLinkedList my=new MyLinkedList();
my.add("haha1");
my.add("haha2");
my.add("haha3");
System.out.println(my.size());
System.out.println(my.get(0));
System.out.println(my.get(1));
System.out.println(my.get(2));
}
}
//自定义的MyLinkedList容器类
class MyLinkedList{
//链表头节点
private Node head=null; //链表头的不能随意改变
//链表的长度
private int size;
public MyLinkedList() {
// TODO Auto-generated constructor stub
}
/*
* 获取功能
*/
public String get(int index) {
//备份一个链表头数据,便于操作查找节点使用
Node temp=head; //永远指向当前节点,从链表头节点开始
for(int i=0;i<size;i++){
if(i==index){
return temp.getData();
}
temp=temp.getNext(); //指向下一个节点
}
return null;
}
//添加
public void add(String value) {
//1.构建新节点,作为链表尾部
Node newNode=new Node(value,null);
//2.判断是否已存在链表头节点,如果不存在,新节点作为链表头,如果存在
if(head == null){
head=newNode;
size++;
return;
}
//找到原链表的链表尾部
//备份一个链表头数据,便于操作查找节点使用
Node temp=head;
while(temp.getNext()!=null){
temp=temp.getNext();
}
//3.让原链表的链表尾指向新节点
temp.setNext(newNode);
//4.长度+1
size++;
}
public int size(){
return this.size;
}
}
//节点类
class Node{
private String data;
private Node next; //存储下一个对象的地址,代表下一个节点对象
public Node() {
// TODO Auto-generated constructor stub
}
public Node(String data, Node next) {
super();
this.data = data;
this.next = next;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
@Override
public String toString() {
return "Node [data=" + data + ", next=" + next + "]";
}
}
三、比较器
自然排序/内部比较器
类实现java.lang.Comparable 接口,重写compareTo方法,自定义比较规则
不够符合开闭原则,不灵活,可能频繁修改源代码
public class Demo01 {
public static void main(String[] args) {
Person[] arr=new Person[3];
arr[0]=new Person("张三",10);
arr[1]=new Person("李四",12);
arr[2]=new Person("王五",11);
System.out.println(Arrays.toString(arr));
//排序前:[Person [name=张三, age=10], Person [name=李四, age=12], Person [name=王五, age=11]]
Arrays.sort(arr);//默认调用内部比较器,默认升序排序
System.out.println(Arrays.toString(arr));
//排序后:[Person [name=张三, age=10], Person [name=王五, age=11], Person [name=李四, age=12]]
}
}
//重写person类中的compare()方法,自定义比较规则
@Override
public int compareTo(Object o) {
//o是object类型,无法使用person类型的,所以需要强转
//想改降序排序可以改成:((Person)o).age-this.age
return this.age-((Person)o).age;
}
自定义排序/外部比较器
java.util Comparator<T> 重写compare()方法,自定义比较规则
public class Demo01 {
public static void main(String[] args) {
Person[] arr=new Person[3];
arr[0]=new Person("张三",10);
arr[1]=new Person("李四",12);
arr[2]=new Person("王五",11);
Arrays.sort(arr,new Test());
//排序后:[Person [name=李四, age=12], Person [name=王五, age=11], Person [name=张三, age=10]]
//匿名内部类
Arrays.sort(arr,new Comparator<Person>(){
@Override
public int compare(Person o1, Person o2) {
return o2.getAge()-o1.getAge();
}});
//Lambda表达式
Arrays.sort(arr,(o1,o2)-> o2.getAge()-o1.getAge());
System.out.println(Arrays.toString(arr));
}
}
//外部比较器
class Test implements Comparator<Person>{
@Override
public int compare(Person o1, Person o2) {
return o2.getAge()-o1.getAge();//降序
}
}
四、Map
HashMap
Map:存储键值对的形式的数据 Key-Value
k 和 v之间存在映射关系,可以通过k找到v
k 的数据 无序的, 不可重复 -->Set集合
v 的数据 无序的, 可以重复 -->Collection集合
一个key只能对应一个value(v中可以存放集合List)
Map无序,去重根据key决定;如果key相同,value会覆盖
遍历:
1.获取所有的key Set<K> keySet()
2.获取所有的值 Collection<V> values()
3.Set<Map.Entry<K,V>> entrySet()
public class MapDemo01 {
public static void main(String[] args) {
Map<Integer,String> map=new HashMap();
// V put(K key, V value) 将指定的值与此映射中的指定键关联(可选操作)。
map.put(01, "哪吒");
map.put(02, "烈火英雄");
System.out.println(map.put(03, "上海堡垒"));//返回null
System.out.println(map.put(03, "上海堡垒2"));//替换原内容,返回原内容“上海堡垒”
System.out.println(map);
//get(key)方法
System.out.println(map.get(02));//返回“烈火英雄”
/* boolean containsKey(Object key)
如果此映射包含指定键的映射关系,则返回 true。
boolean containsValue(Object value)
*/
System.out.println(map.containsKey(03));//true
System.out.println(map.containsValue("上海堡垒"));//false
//遍历
//1.获取所有的key Set<K> keySet()
Set<Integer> keys=map.keySet();
for(Integer i:keys){
System.out.println(i+"---->"+map.get(i));
}
//2.获取所有的值 Collection<V> values()
Collection<String> col=map.values();
Iterator<String> it=col.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
//3.Set<Map.Entry<K,V>> entrySet()
Set<Map.Entry<Integer,String>> set=map.entrySet();
for(Map.Entry i:set){
System.out.println(i.getKey()+"---->"+i.getValue());
}
}
}
使用HashMap容器存储数据,要求key为引用数据类型Hero类型的对象
key是对象类型去重:重写hashCode()和equals()
HashMap:认初始容量 (16):可以根据要存储数据设置 默认加载因子 (0.75) :不建议修改
public class MapDemo02 {
public static void main(String[] args) {
HashMap<Hero,Integer> map=new HashMap();
map.put(new Hero("钢铁侠","复仇者联盟"), 5);
map.put(new Hero("毒液","复仇者联盟"), 2);
map.put(new Hero("灭霸","复仇者联盟"), 2);
map.put(new Hero("灭霸","复仇者联盟"), 2);
System.out.println(map);
//比较key,改变value就会覆盖
//去重后输出:{Hero [name=毒液, tv=复仇者联盟]=2, Hero [name=灭霸, tv=复仇者联盟]=2, Hero [name=钢铁侠, tv=复仇者联盟]=5}
}
}
class Hero{
private String name;
private String tv;
public Hero() {
// TODO Auto-generated constructor stub
}
public Hero(String name, String tv) {
super();
this.name = name;
this.tv = tv;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTv() {
return tv;
}
public void setTv(String tv) {
this.tv = tv;
}
@Override
public String toString() {
return "Hero [name=" + name + ", tv=" + tv + "]";
}
//重写方法
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((tv == null) ? 0 : tv.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Hero other = (Hero) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (tv == null) {
if (other.tv != null)
return false;
} else if (!tv.equals(other.tv))
return false;
return true;
}
}
HashMap线程不安全的问题:
1.Hashtable
2.synchronizedMap(Map<K,V> m) 返回由指定映射支持的同步(线程安全的)映射。
3.java.util.concurrent 类 ConcurrentHashMap<K,V> 线程安全的哈希表 —推荐
TreeMap
底层实现:红黑树的结构默认升序排序
key是自定义引用数据类型时,必须实现内部比较器/外部比较器(在创建TreeMap构造器中指定)
public class TreeMap03 {
public static void main(String[] args) {
TreeMap<Character,Double> map=new TreeMap();
map.put('c', 2.2);
map.put('b', 2.3);
map.put('a', 2.2);
map.put('a', 2.5);
System.out.println(map);
//输出:{a=2.5, b=2.3, c=2.2},map会覆盖value值
}
}
Properties
properties用于配置文件,继承Map类。
properties的key和value都是String类型的数据。
使用方法:
1.创建一个资源文件夹,在里面新建File文件,在文件里写代码。
2.在主方法中创建properties对象,加载File文件,可以获取文件中的内容。
//db.properties文件中的内容
name=zhangsan;
pwd=125;
public class PropertiesDemo01 {
public static void main(String[] args) throws IOException {
Properties pro=new Properties();
//加载
pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));
System.out.println(pro.getProperty("name"));
System.out.println(pro.getProperty("pwd"));
/*输出:
zhangsan
125
*/
}
}
五、Collections
Collections和Arrays很像,Arrays是操作数组的工具类,Collections是操作容器的工具类。
Collection和Collections的区别:
Collection是容器体系的父接口,Collections是操作容器的工具类。
public class Demo01 {
public static void main(String[] args) {
List<Integer> lis=new ArrayList();
lis.add(5);
lis.add(2);
lis.add(4);
lis.add(3);
lis.add(1);
System.out.println(lis);
//void sort(List) //对 List 容器内的元素排序,按照升序进行排序。
Collections.sort(lis);
System.out.println(lis);
//void shuffle(List) //对 List 容器内的元素进行随机排列
Collections.shuffle(lis);
System.out.println(lis);
//void reverse(List) //对 List 容器内的元素进行逆续排列
Collections.reverse(lis);
System.out.println(lis);
//void fill(List, Object) //用一个特定的对象重写整个 List 容器
Collections.fill(lis, null);
System.out.println(lis);
//int binarySearch(List, Object)//采用折半查找的方法查找特定对象
System.out.println(Collections.binarySearch(lis, 6));
}
}
rays很像,Arrays是操作数组的工具类,Collections是操作容器的工具类。
Collection和Collections的区别:
Collection是容器体系的父接口,Collections是操作容器的工具类。
public class Demo01 {
public static void main(String[] args) {
List<Integer> lis=new ArrayList();
lis.add(5);
lis.add(2);
lis.add(4);
lis.add(3);
lis.add(1);
System.out.println(lis);
//void sort(List) //对 List 容器内的元素排序,按照升序进行排序。
Collections.sort(lis);
System.out.println(lis);
//void shuffle(List) //对 List 容器内的元素进行随机排列
Collections.shuffle(lis);
System.out.println(lis);
//void reverse(List) //对 List 容器内的元素进行逆续排列
Collections.reverse(lis);
System.out.println(lis);
//void fill(List, Object) //用一个特定的对象重写整个 List 容器
Collections.fill(lis, null);
System.out.println(lis);
//int binarySearch(List, Object)//采用折半查找的方法查找特定对象
System.out.println(Collections.binarySearch(lis, 6));
}
}
来源:https://blog.csdn.net/weixin_44264545/article/details/99756305