容器
容器的概念
之前学习的数组,是一种有序的相同数据类型的数据的集合,他是简单的线性数据,但是因为其不可修改性,所以非常的不灵活,因此java中提供了功能强大,使用简便的其他容器来增加我们开发的 效率。
常用集合的简单继承如下:
Collection接口
定义了简单的插入,移除,判断,清空等方法。他是允许有重复对象的。
Set 接口继承 Collection,无序不允许重复,使用自己内部的一个排列机制。
List 接口继承 Collection,有序允许重复,以元素安插的次序来放置元素, 和数组一样。
Map接口是键值对接口,定义了数据必须是键值对形式,并且键不能重复。内部也有自己的排列机制。
容器中的元素类型都为引用类型,不能放置原生数据类型(使用装箱即可),使用泛型保留类型。
Collection接口
定义了一些简单的方法
-
boolean add(E e);——添加元素
-
int size(); ——获取容器内数据数量
-
boolean contains(Object o);——判断o在容器中是否存在,引用类型需要重写equals。
使用这个去进行判断的。
-
boolean isEmpty(); ——容器是否为空
可以用来避免空指针。
-
boolean equals(Object o);——判断两个容器是否相等
-
boolean remove(Object o);——移除o元素。
-
boolean removeAll(Collection<?> c); 移除两个容器具有的相同的元素。
-
void clear();——清空元素
-
Object[] toArray();——转成数组,把所有元素储存到数组中。
-
Iterator iterator();——用来遍历,是一个迭代器。
Iterator接口
简单定义了如下方法
boolean hasNext(); //判断是否还有下一个没有被遍历的元素。
Object next(); //返回当前位置的值,并且把下标移到下一个元素。
void remove(); //删除游标左面的元素
进行迭代操作
List<String> list = new ArrayList();
Iterator<String> iterator= list.iterator(); //获取迭代器
while(iterator.hasNext()){ //进行判断是否有下一个元素
System.out.println(iterator.next()); //拿到元素,并且将下标跳到下一个元素
}
除了获取迭代器来进行遍历之外,容器还提供了Stream流来进行操作
stream流
Collection接口中提供了 Stream stream() 默认方法。所有继承他的子类的实例对象都可以使用。
使用方式 ——参数为一个Consumer 接口。使用lambda进行编程。
List<String> list = new ArrayList();
list.stream().forEach((x)->System.out.println(x));
遍历的第三种方式是使用for循环,或者增强for循环,这里不加赘述
List接口
- void add(int index, E element);——在指定位置插入元素,其位置后的元素都后移。
- int indexOf(Object o);——引用类型需要重写equals方法,这里的判断都是根据equals和hashcode来判断的。
- List subList(int fromIndex, int toIndex); 根据下标位置获取子集合,左闭右开。
ArrayList
ArrayList 是 List的子类,底层实现是数组原理,增加了自动扩容,添加元素也是从0开始依次添加。获取元素可以通过get方式传入下标索引。
List arrayList = new ArrayList();
arrayList.add("111"); //添加一个元素
arrayList.add(1,"222");//指定位置插入元素。不能比当前size大。
arrayList.remove(1);//删除索引1位置上的元素。
arrayList.set(1,"333");//修改索引1上的元素。
arrayList.get(1);//查询索引为1上的元素
遍历方式参照上面的Iterator迭代器和stream流。
LinkedList
LinkedList 是一种可以在任何位置进行高效地插入和删除操作的有序序列。底层实现是通过双向链表的形式。因此查询的效率不如ArrayList;
LinkedList实现类在List接口基础上新增了几个方法
- public E getFirst() 获取第一个元素
- public E getLast() 获取最后一个元素
- public E removeFirst() 移除第一个元素
- public E removeLast() 移除最后一个元素
其他方法和List没区别,用法都相同,不加赘述。
Set接口
Set 接口中的元素无序不可重复:不包含重复元素,最多包含一个 null,元素没有顺序 。
引用类型需要重写equals方法。否则比较的是地址值。0的位置给了null,专门存null。
set中使用hashcode值来计算座位号,那么引用对象就必须重写其Hashcode方法。
HashSet
HashSet 是 Set 接口的一个子类,主要的特点是:里面不能存放重复元素,而且采用散列的存储方法,所以没有顺序。这里所说的没有顺序是指:元素插入的顺序与输出的顺序不 一致。 使用重写 equals 和 hashcode 来实现自定义的类型的去重。
Set<Person> set = new HashSet<>();
set.add(new Person("小张"));
set.add(new Person("小张"));
set.add(null);
set.add(null);
System.out.println(set.contains(new Person("小张")));//判断是否含有此内容
System.out.println(set.size());//输出大小
//类定义
class Person{
public String name;
Person(String name){
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
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 (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "Person [name=" + name + "]";
}
}
遍历
HashSet因为是无序的。因此不能使用普通for循环进行遍历。
方式一:Set sets = new HashSet<>();统一遍历该set
for(Person set:sets){ //需要知道类型,也可以使用泛型。
System.out.println(set);
}
方式二:使用迭代器Iterator
Iterator tor = sets.iterator();
while(tor.hasNext()){
System.out.println(tor.next());
}
//方式2.5
tor.forEachRemaining((x)->System.out.println(x)); //使用lambda
两种只能选择其中一种,因为迭代器的游标不会重回,要么重新获取游标。
方式三:使用stream流
sets.stream().forEach((x)->System.out.println(x));
Map接口
Map存储的数据形式为键(key)-值(value) 对。实现类有HashMap和TreeMap。Map类中存储的键-值对通过的健通过hashcode计算出,并且作为唯一标识,因此不能重复。
HashMap: 线程不安全,效率高。 允许key或value为null
HashTable:线程安全,效率低.。不允许key或value为null
Properties: HashTable的子类,key和value都是string
Map常用的方法
- Object put(Object key, Object value); ——弹入键值对
- Object get(Object key); ——通过键来获取值。
- Object remove(Object key); ——通过键去删除元素。
- boolean containsKey(Object key); ——判断是否包含key。引用类型需要重写hashcode和equals
- boolean containsValue(Object value); ——判断是否包含某值。引用类型需要重写hashcode和equals
- int size();——返回容器大小
- boolean isEmpty(); ——判断是否为空
- void putAll(Map t); ——复制容器。
- void clear(); ——清空容器。
同个键传入数据,新的值会覆盖旧的值。set容器中因为只取了hashmap的键来使用,相同的数据来直接弹出。
maps.put(1,new Person("张三"));
maps.put(3,new Person("李四"));
maps.put(5,new Person("张三"));
maps.put(6,new Person("a黑"));
System.out.println(maps.size());
System.out.println(maps.get(1));
System.out.println(maps.remove(1));
System.out.println("-----------");
System.out.println(maps.containsKey(1));
System.out.println(maps.containsValue(new Person("张三")));
System.out.println(maps.isEmpty());
maps.clear();
map的遍历
因为map是键值对,并且没有继承Collection接口,所以无法使用增强for循环遍历。
那么map的循环方式主要有3中思路
第一种,获取所有的key,然后用get方法遍历,通过keyset来获取。
获取到Collection后又可以又3中方式进行遍历。迭代器和stream流此处不加举例
Collection<Integer> cools= maps.keySet(); //通过keyset来获取所有的key值
for(Integer p:cools){
System.out.println(maps.get(p));
}
第二种,使用Entry<>接口,map底层是Entry方式存储的。使用entrySet()获取
获取到set后依然有3中方法,但这里只举例一种 最优方式。
public static void mapFor2(Map<Integer,Person>maps){
Set<Entry<Integer,Person>> set= maps.entrySet();
Iterator<Entry<Integer,Person>> it=set.iterator();
while(it.hasNext()){
Entry<Integer,Person> en=it.next();
System.out.println(en.getKey()+"-----"+en.getValue());
}
}
第三种,直接拿到所有的value值,此方法不能得到key值。
public static void mapFor3(Map<Integer,Person>maps){
Collection<Person> c = maps.values();
c.stream().forEach((x)->System.out.println(x));
}
Properties为Hashtable的子类,要求键与值只能为字符串 ,不能为null,长与 配置文件(与外界交互 的信息) 即内 存与存储介质(文件、数据库、网络、服务器内存等)交互。这里先不做介绍。