集合
想一下,目前为止,我们学过哪些可以存储元素的容器:
1、数组,查询快,增删慢。
既可以存储基本数据类型的元素,又可以存储引用数据类型的元素
对于同一个数组而言,元素类型都是一样
长度一旦创建旧固定了,不能改变长度。
2、StringBuffer
长度可以随着添加的字符个数而改变
StringBuffer容器中的元素就一种类型,字符类型
针对不同的数据结构,存储方式以及取出方式不同,java提供了许多不同种类特点的容器【集合】
也就是java是通过类去描述一个事物的,不同的容器,也会提供不同的集合类
Collection【接口】:
- List【接口】:
- ArrayList【子类】
借助实现类:ArrayList学习Collection接口中的方法
ArrayList类中的构造方法:
ArrayList() 构造一个初始容量为十的空列表。
学习任意集合的步骤:
1、创建集合对象
2、创建元素对象
3、将元素对象放入到集合中
4、遍历集合
集合关系图
Java 集合可分为 Collection 和 Map 两种体系:
-
Collection接口:单列数据,定义了存取一组对象的方法的集合
- List:元素有序、可重复的集合
- Set:元素无序、不可重复的集合
-
Map接口:双列数据,保存具有映射关系“key-value对”的集合
集合面试提问
- 为什么出现集合类?
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,Java就提供了集合类。 - 数组和集合类同是容器,有何不同?
数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。数组中可以存储基本数据类型,集合只能存储对象。 - 集合类的特点
集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象
Collection接口及方法
-
Collection 接口是 List、Set 和 Queue 接口的父接口,该接口里定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。
Collection接口中的方法: boolean add(Object e) boolean remove(Object o) void clear() boolean contains(Object o) boolean isEmpty() int size()
public class CollectionDemo1 {
public static void main(String[] args) {
Collection c1 = new ArrayList();
System.out.println("c1: " + c1);
// boolean add(Object e) 向集合中添加元素
c1.add(10); // 自动装箱,将int类型的值封装成包装类对象
c1.add(12.34);
c1.add(true);
c1.add("hello");
System.out.println("c1: " + c1);
//[10, 12.34, true, hello]
//boolean remove(Object o) 从集合中移除某一个元素
c1.remove(100);
System.out.println("c1: " + c1);
//void clear() 将集合中元素清空
// c1.clear();
// System.out.println("c1: " + c1);
//boolean contains(Object o) 判断集合中是否包含某个类型的元素
System.out.println(c1.contains("hello"));
//boolean isEmpty() 判断集合中是否有元素存在
System.out.println(c1.isEmpty());
//int size() 获取集合中元素的个数【集合的长度】
System.out.println(c1.size());
}
}
/*
Collection中的方法:
boolean addAll(Collection c)
boolean removeAll(Collection c)
boolean containsAll(Collection c)
boolean retainAll(Collection c)
*/
public class CollectionDemo2 {
public static void main(String[] args) {
// 借助子类ArrayList创建Collection集合
Collection c1 = new ArrayList();
c1.add(11);
c1.add(22);
c1.add(33);
c1.add(44);
System.out.println("c1: " + c1);
Collection c2 = new ArrayList();
c2.add(33);
c2.add(44);
c2.add(55);
c2.add(66);
System.out.println("c2: " + c2);
System.out.println("-----------------------------");
//boolean addAll(Collection c) 将另一个集合中的元素批量添加
c1.addAll(c2);
System.out.println("c1: "+c1);
System.out.println("c2: "+c2);
System.out.println("-----------------------------");
// boolean removeAll(Collection c) 从一个集合中移除另一个集合所有的元素
c1.removeAll(c2);
System.out.println("c1: "+c1);
System.out.println("c2: "+c2);
System.out.println("-----------------------------");
//boolean containsAll(Collection c) 判断A集合中是否完整包含集合B中的元素
System.out.println(c1.containsAll(c2));
System.out.println("-----------------------------");
//boolean retainAll(Collection c)
// c1.retainAll(c2);
c2.retainAll(c1);
System.out.println("c1: " + c1);
System.out.println("c2: " + c2);
}
}
遍历集合
增强for循环
/*
增强for循环:
是用来代替迭代器的,可以遍历数组和Collection集合
语句定义格式:
for(元素的数据类型 变量名 : 数组或Collection集合){
使用变量名;
}
*/
public class ZengForDemo {
public static void main(String[] args) {
// ArrayList<String> list1 = new ArrayList<>();
ArrayList<String> list1 = new ArrayList<>();
list1.add("hello");
list1.add("world");
list1.add("java");
list1.add("hadoop");
list1.add("world");
list1.add("hello");
list1.add("flink");
System.out.println("list1: "+list1);
System.out.println("----------------------------");
// for (String s : list1) {
// System.out.println(s + "-" + s.length());
// }
////
for (String s : list1) {
System.out.println(s+"-"+s.length());
}
// for (String s : list1) {
// System.out.println(s + "-" + s.length());
// }
System.out.println("----------------------------");
int[] arr = {11,22,33,44,55};
for (int i : arr) {
System.out.println(i);
}
}
}
如何遍历一个Collection集合,得到每一个元素。
Object[] toArray()
把集合转成数组,可以实现集合的遍历
public class CollectionDemo3 {
public static void main(String[] args) {
Collection c1 = new ArrayList();
c1.add("hello");
c1.add("world");
c1.add("java");
c1.add("hadoop");
System.out.println("c1: " + c1);
System.out.println("-------------------------");
Object[] array = c1.toArray();
for (int i = 0; i < array.length; i++) {
// array[i] - "java"
// Object o = "java";
// System.out.println("元素:"+array[i]);
//此时,这里需要进行向下转型才可以使用元素自身类型中的方法
String s = (String) array[i];
System.out.println("元素:" + s + ", 长度:" + s.length());
}
}
}
Iterator iterator()迭代器
集合的专用遍历方式
概述:
Iterator接口概述
对 collection 进行迭代的迭代器
依赖于集合而存在
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/*
Iterator iterator() 迭代器,Collection集合的专用遍历方式
*/
public class CollectionDemo4 {
public static void main(String[] args) {
Collection c1 = new ArrayList();
c1.add("hello");
c1.add("world");
c1.add("java");
c1.add("hadoop");
c1.add("redis");
System.out.println("c1: " + c1);
System.out.println("-------------------------");
//调用方法获取迭代器对象
Iterator iterator = c1.iterator(); //Iterator iterator = new Itr()
// 遍历迭代器对象,就可以获取迭代器中的元素了
// 我们点进Iterator观察发现,Iterator是一个接口,c1.iterator()结果一定是一个实现了Iterator接口的实现类对象
// 要想使用迭代器对象中的方法,就去iterator()如何实现的。
// 我们这里是借助了ArrayList子类创建的对象,去到ArrayList类中观察发现该方法返回的是一个new Itr()对象
// 因为Itr类实现了Iterator接口,可以直接接收
// 去Itr类中查找方法的实现,去使用并获取迭代器中的元素值
// 我们发现Itr中有两个重要的方法:
// hasNext()和next()
hashNext()判断游标和集合长度是否相等,若相等,说明已经在末尾,下一个无元素。
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
while (iterator.hasNext()){
Object o = iterator.next(); //Object o = "hello"
String s = (String)o;
System.out.println(s+": "+s.length());
}
}
}
-
存储自定义对象并遍历 Student(name,age)
-
1、创建集合对象
-
2、创建元素对象
-
3、将元素添加到集合中
-
4、遍历集合
public class CollectionDemo5 {
public static void main(String[] args) {
Collection c1 = new ArrayList();
Student s1 = new Student("张三", 18);
Student s2 = new Student("李四", 17);
Student s3 = new Student("王五", 18);
Student s4 = new Student("赵六", 19);
c1.add(s1);
c1.add(s2);
c1.add(s3);
c1.add(s4);
//方式1:转数组遍历
Object[] array = c1.toArray();
for (int i = 0; i < array.length; i++) {
Object o = array[i]; // new Student("张三", 18);
Student s = (Student) o;
System.out.println(s.getName() + "-" + s.getAge());
}
System.out.println("-----------------------");
//方式2:使用迭代器的方式遍历
Iterator iterator = c1.iterator();
while (iterator.hasNext()){
Object o = iterator.next();// new Student("张三", 18);
Student s = (Student) o;
System.out.println(s.getName() + "-" + s.getAge());
}
}
}
泛型
回忆下,在此之前,我们的集合可以存放任意类型的元素,但是实际开发的时候,一个集合规定只允许放一种数据类型的元素
java中的集合提供了一种类似于数组定义时确定元素类型的方式,泛型
泛型:
语句定义格式:<引用数据类型>
将引用数据类型当作参数一样传递
泛型的好处:
1、减少了程序中的黄色警告
2、遍历的时候不需要再做向下转型了,因为在创建集合对象的时候,给定了泛型的类型
*/
public class FanXingDemo1 {
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<>(); // 泛型定义了集合中的元素数据类型,右边的尖括号泛型可以不写,自动类型推断
// ArrayList list1 = new ArrayList();
// list1.add()
// list1.add(10);
list1.add("hello");
list1.add("world");
list1.add("java");
list1.add("hadoop");
list1.add("world");
list1.add("hello");
list1.add("flink");
// list1.add(false);
System.out.println("list1: "+list1);
System.out.println("----------------------------");
Iterator<String> iterator = list1.iterator();
while (iterator.hasNext()){
String s = iterator.next();
System.out.println(s+"-"+s.length());
}
}
}
泛型场景1:
泛型类:将泛型定义在类上
<>里面的参数是为了将来调用时接收传入的引用数据类型,相当于一个形参一样,符合变量标识符命名规则就可以了
但是规范来说,泛型的名字,由一个大写的英文字母表示
*/
class Demo1<A>{
public void fun1(A a){
System.out.println(a);
}
}
public class FanXingDemo2 {
public static void main(String[] args) {
Demo1<Integer> stringDemo1 = new Demo1<>();
stringDemo1.fun1(100);
// stringDemo1.fun1("hello");
Demo1<String> d2 = new Demo1<>();
d2.fun1("hello");
// d2.fun1(100);
}
}
泛型场景2:
把泛型定义在方法上 格式:public <泛型类型> 返回类型 方法名(泛型类型 .)
class Demo2<Q>{
public <E> void fun1(E e){
System.out.println(e);
}
public <C> void fun3(C c){
System.out.println(c);
}
public void fun2(Q q){
System.out.println(q);
}
}
public class FanXingDemo3 {
public static void main(String[] args) {
Demo2<Double> demo2 = new Demo2<>();
demo2.fun1("hello");
demo2.fun1(100);
demo2.fun3("hello");
demo2.fun2(12.34);
}
}
泛型场景3:
泛型接口
把泛型定义在接口上
格式:public interface 接口名<泛型类型1>
interface Inter<W>{
void fun1(W w);
}
class InterImpl<W> implements Inter<W>{
@Override
public void fun1(W w) {
System.out.println(w);
}
}
public class FanXingDemo4 {
public static void main(String[] args) {
}
}
泛型通配符<?>
import java.util.ArrayList;
import java.util.Collection;
/*
泛型通配符<?>
任意类型,如果没有明确,那么就是Object以及任意的Java类了
? extends E
向下限定,E及其子类
? super E
向上限定,E及其父类
*/
class Animal {
}
class Dog extends Animal {
}
class Cat extends Animal {
}
class Demo3 {
public void fun1(Collection<?> c1) { // 可以接收元素是任意引用数据类型的Collection集合对象
System.out.println("c1: " + c1);
}
public void fun2(Collection<? extends Animal> c1) { // 可以接收元素是Animal类型或Animal的子类类型的的Collection集合对象
System.out.println("c1: " + c1);
}
public void fun3(Collection<? super Animal> c1) { // 可以接收元素是Animal类型或Animal的父类类型的的Collection集合对象
System.out.println("c1: " + c1);
}
// public void fun1(Xxxx<?> c1) { // 可以接收元素是任意引用数据类型的Xxxx对象
// System.out.println("c1: " + c1);
// }
}
public class FanXingDemo5 {
public static void main(String[] args) {
Demo3 d1 = new Demo3();
ArrayList<Animal> list1 = new ArrayList<>();
ArrayList<Dog> list2 = new ArrayList<>();
ArrayList<Cat> list3 = new ArrayList<>();
ArrayList<Object> list4 = new ArrayList<>();
//Collection<?> c1
d1.fun1(list1);
d1.fun1(list2);
d1.fun1(list3);
d1.fun1(list4);
d1.fun2(list1);
d1.fun2(list2);
d1.fun2(list3);
// d1.fun2(list4);
d1.fun3(list1);
// d1.fun3(list2);
// d1.fun3(list3);
d1.fun3(list4);
//public boolean addAll(Collection<? extends Animal> c)
ArrayList<Animal> animals = new ArrayList<>();
animals.addAll(list1);
animals.addAll(list2);
animals.addAll(list3);
// animals.addAll(list4);
}
}
list
Collection: - List: 元素有序【存储和取出的顺序一致】,允许元素发生重复,具有索引的概念 - Set: 元素唯一且无序
public class ListDemo1 {
public static void main(String[] args) {
//借助ArrayList实现子类创建List接口的对象
List list1 = new ArrayList();
list1.add("hello");
list1.add("world");
list1.add("apple");
list1.add("hadoop");
list1.add("redis");
list1.add("world");
System.out.println("list1: " + list1);
Iterator iterator = list1.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
List特有方法
集合具有索引的概念,根据索引提供了List集合特有的一些功能:
void add(int index,E element)
E remove(int index)
E get(int index)
E set(int index,E element)
ListIterator listIterator()
public class ListDemo2 {
public static void main(String[] args) {
//借助ArrayList实现子类创建List接口的对象
List list1 = new ArrayList();
list1.add("hello");
list1.add("world");
list1.add("apple");
list1.add("hadoop");
list1.add("redis");
list1.add("world"); // 集合的末尾处添加
System.out.println("list1: " + list1);
//[hello, world, apple, hadoop, redis, world]
// void add(int index,Object element)
list1.add(3,"数加");
System.out.println("list1: " + list1);
//[hello, world, apple, 数加, hadoop, redis, world]
//Object remove(int index) 根据索引移除元素
// System.out.println(list1.remove(10)); // 返回被删除的元素
// System.out.println("list1: " + list1);
// Object get(int index) 通过索引获取元素
// System.out.println(list1.get(3));
// System.out.println("list1: " + list1);
System.out.println("======");
System.out.println(list1.get(3));
// Object set(int index,E element) 根据索引修改值
// System.out.println(list1.set(3, "shujia")); //返回索引位置上的旧值
// System.out.println("list1: " + list1);
list1.set(1,"world");
System.out.println(list1);
// ListIterator listIterator() 是List集合专有的迭代器对象 要想倒着遍历,必须得先正着遍历一次
// ListIterator listIterator = list1.listIterator();
ListIterator listIterator = list1.listIterator();
while (listIterator.hasNext()){
System.out.println(listIterator.next());
}
System.out.println("--------------------------------------");
// while (listIterator.hasNext()){
// System.out.println(listIterator.next());
// }
while (listIterator.hasPrevious()){
System.out.println(listIterator.previous());
}
}
}
list插入练习
/*
需求:创建List集合对象,添加若干个字符串类型的元素,使用迭代器遍历,当遇到字符串
"java"的时候,向集合中添加一个元素"shujia"。
ConcurrentModificationException: 并发修改异常
结论:迭代器遍历,使用迭代器修改;集合遍历,集合修改
*/
public class ListDemo3 {
public static void main(String[] args) {
List list1 = new ArrayList();
list1.add("hello");
list1.add("world");
list1.add("java");
list1.add("hadoop");
list1.add("redis");
System.out.println("list1: " + list1);
System.out.println("-------------------------");
// Iterator iterator = list1.iterator();
// while (iterator.hasNext()){
// String s = (String) iterator.next();
// if("java".equals(s)){
// list1.add("shujia");
// }
// }
// System.out.println("list1: " + list1);
ListIterator listIterator = list1.listIterator();
while (listIterator.hasNext()) {
String s = (String) listIterator.next();
if ("java".equals(s)) {
listIterator.add("shujia");
}
}
System.out.println("list1: " + list1);
}
}
list集合的遍历方式
import java.util.ArrayList;
import java.util.List;
/*
List集合的遍历方式:
1、先转数组再遍历
2、获取迭代器遍历
3、根据索引和长度使用for循环遍历【List集合专属遍历方式】
*/
public class ListDemo4 {
public static void main(String[] args) {
List list1 = new ArrayList();
list1.add("hello");
list1.add("world");
list1.add("java");
list1.add("hadoop");
list1.add("redis");
System.out.println("list1: " + list1);
System.out.println("-------------------------");
//
for(int i=0;i<list1.size();i++){
Object o = list1.get(i);
System.out.println(o);
}
}
}
去除集合中字符串的重复值
public class ArrayListTest1 {
public static void main(String[] args) {
ArrayList list1 = new ArrayList();
list1.add("hello");
list1.add("world");
list1.add("hello");
list1.add("java");
list1.add("flink");
list1.add("hello");
list1.add("java");
list1.add("world");
list1.add("hello");
System.out.println("list1: "+list1);
System.out.println("----------------------------");
//创建一个新的集合,遍历旧集合
//如果新集合中有该元素,说明重复,不添加
//反之添加到新集合中,最后新集合中存储去重后的结果
ArrayList list2 = new ArrayList();
Iterator iterator = list1.iterator();
while (iterator.hasNext()){
String s = (String) iterator.next();
if(!list2.contains(s)){
list2.add(s);
}
}
System.out.println("list2: "+list2);
}
}
去除集合中自定义对象的重复值(对象的成员变量值都相同)
Student(name,age)
/*
去除集合中自定义对象的重复值(对象的成员变量值都相同)
Student(name,age)
*/
public class ArrayListTest2 {
public static void main(String[] args) {
ArrayList list1 = new ArrayList();
Student s1 = new Student("张成阳", 18);
Student s2 = new Student("方直", 19);
Student s3 = new Student("张成阳", 18);
Student s4 = new Student("方直", 19);
Student s5 = new Student("张成阳", 18);
Student s6 = new Student("方直", 19);
list1.add(s1);
list1.add(s2);
list1.add(s3);
list1.add(s4);
list1.add(s5);
list1.add(s6);
System.out.println("list1: "+list1);
System.out.println("----------------------------");
//创建一个新的集合,遍历旧集合
//如果新集合中有该元素,说明重复,不添加
//反之添加到新集合中,最后新集合中存储去重后的结果
ArrayList list2 = new ArrayList();
Iterator iterator = list1.iterator();
while (iterator.hasNext()){
Student s = (Student) iterator.next();
if(!list2.contains(s)){
list2.add(s);
}
}
/*
我们按照去重字符串的逻辑对学生对象进行去重,发现不太行
旧集合中的每一个学生对象都添加到了新集合中‘
从结果来看。每一个学生对象41行的判断【!list2.contains(s)】都是true
list2.contains(s) 一直都是false
要想知道为什么list2.contains(s)一直都是false的话,就应该去看contains的源码
public boolean contains(Object o) {
// o - new Student("张成阳", 18);
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
// o - new Student("张成阳", 18);
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < this.size; i++) // 遍历新集合,调用元素的equals方法挨个与新集合中的元素进行比较
if (o.equals(elementData[i]))
return i; // i的值一定是大于等于0
}
return -1;
}
从源码上来看,底层是调用了元素类型中的equals方法,而我们的元素是学生类,学生类中没有重写该方法,用的是父类Object类中的
equals方法,比较的是地址值,而每一学生都是new出来的,地址肯定不一样。
解决方案:元素类型中重写equals方法即可
*/
System.out.println("list2: "+list2);
}
}
Linkediist
Collection:
- List: 元素有序且允许发生重复,有索引
- ArrayList: 底层数据结构是数组,查询快,增删慢,线程不安全的,效率高
- Vector: 底层数据结构是数组,查询快,增删慢,线程安全的,效率低,即便是这样,我们以后也不用
- LinkedList: 底层数据结构是双链表,增删快,查询慢,线程不安全,效率高
- Set:
/*
LinkedList类特有功能【由于底层是链表】
public void addFirst(Object e)及addLast(Object e)
public Object getFirst()及getLast()
public Object removeFirst()及public Object removeLast()
*/
public class LinkedListDemo1 {
public static void main(String[] args) {
//LinkedList() 构造一个空列表。
LinkedList list1 = new LinkedList();
list1.add("hello");
list1.add("world");
list1.add("hello");
list1.add("clickhouse");
list1.add("redis");
System.out.println("list1: "+list1);
// Iterator iterator = list1.iterator();
// while (iterator.hasNext()){
// System.out.println(iterator.next());
// }
System.out.println("-----------------------------------------");
//public void addFirst(Object e)及addLast(Object e) 在头部添加一个元素或者尾部添加元素
list1.addFirst("kafka");
list1.addLast("cdh");
System.out.println("list1: "+list1);
//public Object getFirst()及getLast()
System.out.println(list1.getFirst());
System.out.println(list1.getLast());
System.out.println("list1: "+list1);
//public Object removeFirst()及public Object removeLast() 从集合中移除第一个元素或最后一个元素
System.out.println(list1.removeFirst());
System.out.println(list1.removeLast());
System.out.println("list1: "+list1);
}
}
Linkedlist练习
请用LinkedList模拟栈数据结构的集合,并测试
栈的特点:先进后出
题目的意思是:自己造一个类,底层封装LinkedList,自己定义方法
创建自己的类对象,调用自己定义的方法,来实现栈。
package shujia.day10;
import java.util.LinkedList;
public class MyStack {
private LinkedList list;
public MyStack(){
list = new LinkedList();
}
public void shuJiaAddElement(Object o){
list.addFirst(o);
}
public Object shuJiaGetElement(){
return list.removeFirst();
}
public int getSize(){
return list.size();
}
@Override
public String toString() {
return "MyStack{" +
"list=" + list +
'}';
}
}
public class LinkedListTest1 {
public static void main(String[] args) {
MyStack myStack = new MyStack();
myStack.shuJiaAddElement("hello");
myStack.shuJiaAddElement("world");
myStack.shuJiaAddElement("java");
myStack.shuJiaAddElement("hello");
myStack.shuJiaAddElement("hadoop");
System.out.println("myStack: " + myStack);
int size = myStack.getSize();
for (int i = 0; i < size; i++) {
System.out.println(myStack.shuJiaGetElement());
}
}
}
Vector
Collection:
- List: 元素有序且允许发生重复,有索引
- ArrayList: 底层数据结构是数组,查询快,增删慢,线程不安全的,效率高
- Vector: 底层数据结构是数组,查询快,增删慢,线程安全的,效率低,即便是这样,我们以后也不用
- LinkedList
- Set:
Vector类特有功能
public void addElement(E obj)
public E elementAt(int index)
public Enumeration elements()
public class VectorDemo1 {
public static void main(String[] args) {
// Vector() 构造一个空向量,使其内部数据数组的大小为 10 ,标准容量增量为零。
Vector v1 = new Vector();
v1.add("hello");
v1.add("world");
v1.add("hello");
v1.add("spark");
v1.add("hbase");
System.out.println("v1: "+v1);
System.out.println("---------------");
//public void addElement(Object obj) 向集合末尾添加一个元素效果和add方法一样,以后就用add方法替代
// v1.addElement("hive");
// System.out.println("v1: "+v1);
//public Object elementAt(int index) 根据索引获取元素 以后也不用,使用get方法进行替代
// System.out.println(v1.elementAt(3));
// System.out.println(v1.get(3));
//public Enumeration elements() 这个今后使用迭代器的方式进行替代
// Enumeration elements = v1.elements();
// while (elements.hasMoreElements()){
// System.out.println(elements.nextElement());
// }
}
}
Collcetions工具类
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/*
Collections工具类
*/
public class CollectionsDemo1 {
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<>(); // 线程不安全的集合类对象
List<String> list2 = Collections.synchronizedList(list1); // 线程安全的
list2.add("hello");
list2.add("world");
list2.add("java");
list2.add("hello");
list2.add("hadoop");
System.out.println("list2: " + list2);
}
}
Set
HashSet
Collection【接口】:
- List【接口】元素有序【添加和取出的顺序一致】,允许发生重复,有索引
- ArrayList: 底层数据结构是数组,查询快,增删慢,线程不安全,效率高
- Vector: 底层数据结构是数组,查询快,增删慢,线程安全,效率低,即便安全我们以后也不用
- LinkedList: 底层数据结构是双链表,增删快,查询慢,线程不安全,效率高
- Set【接口】元素无序【添加和取出的顺序不一致】,且唯一
- HashSet: 底层数据结构是哈希表,线程不安全,效率高,能够保证元素的唯一
- TreeSet
public class SetDemo1 {
public static void main(String[] args) {
// HashSet() 构造一个新的空集合; 背景HashMap实例具有默认初始容量(16)和负载因子(0.75)。
HashSet<String> set1 = new HashSet<>();
// 因为Set是Collection的子接口,HashSet是Set接口的实现类,里面必然也重写了父接口中所有的抽象方法
set1.add("hello");
set1.add("world");
set1.add("hello");
set1.add("java");
set1.add("hello");
set1.add("hadoop");
for (String s : set1) {
System.out.println(s);
}
}
}
需求:
当学生的姓名和年龄一样的时候,表示重复对象
import java.util.HashSet;
//需求:当学生的姓名和年龄一样的时候,表示重复对象
public class SetDemo2 {
public static void main(String[] args) {
HashSet<Student> set1 = new HashSet<>();
Student s1 = new Student("张成阳", 18);
Student s2 = new Student("方直", 18);
Student s3 = new Student("张成阳", 18);
Student s4 = new Student("黄沪生", 17);
Student s5 = new Student("黄涛", 19);
/*
1、HashSet中add方法底层实际上是调用了HashMap中的put方法
2、底层依赖于元素对象的类型中的hashCode()方法,但是我们Student类中并没有写,所以用的是Object类中的方法,
而父亲Object类中的hashCode()是根据对象的地址值计算出一个哈希值。说明这5个对象hashCode的结果都不一样
3、底层判断待插入的元素是否与已经在hashtable中的元素重复,是根据两个条件判断的
1)哈希值hashCode()结果是否一样
2)元素的equals方法结果是否一样
我们的Student元素类中这两个方法都没有进行重写,所以任意两个Student对象的结果都不一样
既然不一样,底层就认为不重复,不重复就添加到集合中,所以我们看到的结果是没有去重的
*/
set1.add(s1);
set1.add(s2);
set1.add(s3);
set1.add(s4);
set1.add(s5);
for (Student student : set1) {
System.out.println(student);
}
}
}
*
编写一个程序,获取10个1至20的随机数,要求随机数不能重复。
*/
public class SetTest1 {
public static void main(String[] args) {
HashSet<Integer> set1 = new HashSet<>();
Random random = new Random();
while (set1.size() != 10) {
set1.add(random.nextInt(20) + 1);
}
System.out.println("set1: " + set1);
}
}
Treeset
Collection【接口】:
- List【接口】元素有序【添加和取出的顺序一致】,允许发生重复,有索引
- ArrayList: 底层数据结构是数组,查询快,增删慢,线程不安全,效率高
- Vector: 底层数据结构是数组,查询快,增删慢,线程安全,效率低,即便安全我们以后也不用
- LinkedList: 底层数据结构是双链表,增删快,查询慢,线程不安全,效率高
- Set【接口】元素无序【添加和取出的顺序不一致】,且唯一
- HashSet: 底层数据结构是哈希表,线程不安全,效率高,能够保证元素的唯一
- LinkedHashSet: 底层数据结构是哈希表和链表,线程不安全,效率高,能够保证元素的唯一【哈希表】,有序【链表】!
- TreeSet: 底层数据结构是红黑树,具有可预测的迭代次序或自定义排序
排序方式:
1) 自然排序
2) 比较器排序
import java.util.TreeSet;
public class TreeSetDemo1 {
public static void main(String[] args) {
// 使用TreeSet集合存储字符串元素对象并遍历
//TreeSet() 构造一个新的,空的树组,根据其元素的自然排序进行排序
TreeSet<String> set1 = new TreeSet<>();
// TreeSet<String> set = new TreeSet();
/*
TreeSet中的add方法底层实际上是调用了TreeMap中的put方法
*/
set1.add("banana");
set1.add("peach");
set1.add("apple");
set1.add("watermelon");
set1.add("banana");
set1.add("apple");
set1.add("cherry");
System.out.println(set1);
for (String s : set1) {
System.out.println(s);
}
}
}
需求:
使用TreeSet存储自定义对象并遍历
当教师的姓名和年龄一样的时候,认为发生重复
需求:将教师对象添加到TreeSet集合中,去重的同时,按照年龄从小到大排序
自然排序要求元素类要实现Comparable接口,并重写compareTo方法
而compareTo方法中的结果返回值,取决于需求来编写代码逻辑
public class Teacher implements Comparable<Teacher>{
private String name;
private int age;
public Teacher() {
}
public Teacher(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 "Teacher{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Teacher o) {
//按照年龄从小到大排序
// o - 根的元素【已经存储在树中的元素】
// this - 待插入的元素
//显式条件:年龄从小到大排序
//隐藏条件:年龄一样,姓名不一定一样
// return this.getAge()-o.getAge();
int i1 = this.getAge()-o.getAge();
// 当年龄一样的时候,比较姓名
return (i1==0)?this.getName().compareTo(o.getName()):i1;
// return -10;
}
}
public class TreeSetDemo2 {
public static void main(String[] args) {
TreeSet<Teacher> set1 = new TreeSet<>();
Teacher t1 = new Teacher("小虎", 18);
Teacher t2 = new Teacher("杨老板", 16);
Teacher t3 = new Teacher("笑哥", 19);
Teacher t4 = new Teacher("旭哥", 15);
Teacher t5 = new Teacher("小虎", 18);
Teacher t6 = new Teacher("强哥", 18);
set1.add(t1);
set1.add(t2);
set1.add(t3);
set1.add(t4);
set1.add(t5);
set1.add(t6);
for (Teacher teacher : set1) {
System.out.println(teacher);
}
}
}
import java.util.Comparator;
import java.util.TreeSet;
/*
使用比较器的方式创建TreeSet集合
需求:书的名字和价格一样的话,重复,价格从小到大排序
*/
class MyComparator implements Comparator<Book> {
@Override
public int compare(Book o1, Book o2) {
// o1 - 待插入的元素’
// o2 - 已经在树中的元素
// return 0;
int i1 = o2.getPrice() - o1.getPrice();
return (i1 == 0) ? o1.getName().compareTo(o2.getName()) : i1;
}
}
// 按照书的名字从短到长排序
//class MyComparator2 implements Comparator<Book> {
// @Override
// public int compare(Book o1, Book o2) {
// int i1 = o1.getName().length() - o2.getName().length();
// int i2 = (i1 == 0) ? o1.getName().compareTo(o2.getName()) : i1;
// return (i2 == 0) ? o1.getPrice() - o2.getPrice() : i2;
// }
//}
public class TreeSetDemo3 {
public static void main(String[] args) {
// TreeSet<Book> books = new TreeSet<>(new MyComparator());
TreeSet<Book> books = new TreeSet<>(new Comparator<Book>() {
@Override
public int compare(Book o1, Book o2) {
int i1 = o1.getName().length() - o2.getName().length();
int i2 = (i1 == 0) ? o1.getName().compareTo(o2.getName()) : i1;
return (i2 == 0) ? o1.getPrice() - o2.getPrice() : i2;
}
});
Book b1 = new Book("面试指南", 100);
Book b2 = new Book("大数据spark", 98);
Book b3 = new Book("面试指南", 100);
Book b4 = new Book("flink开发指南", 100);
Book b5 = new Book("java从基础到入坑", 50);
books.add(b1);
books.add(b2);
books.add(b3);
books.add(b4);
books.add(b5);
for (Book book : books) {
System.out.println(book);
}
}
}
Map
Map【接口】:每一个元素是由键和值构成
1、在同一个Map集合中,键是唯一的,值可以发生重复
2、Map的子类HashMap,TreeMap,其中的唯一性是针对键来说的
Map接口中的方法:
V put(K key,V value)
V remove(Object key)
void clear()
boolean containsKey(Object key)
boolean containsValue(Object value)
boolean isEmpty()
int size()
public class MapDemo1 {
public static void main(String[] args) {
//借助Map子类HashMap来创建对象
Map<Integer, String> map1 = new HashMap<>();
//String put(Integer key,String value) // 返回被覆盖的旧值
map1.put(1001, "张三");
map1.put(1002, "李四");
map1.put(1001, "王五");
map1.put(1003, "赵六");
System.out.println("map1: " + map1);
// System.out.println("-----------------");
//map1: {1001=王五, 1002=李四, 1003=赵六}
//V remove(Object key) 根据键删除一个键值对, 返回对应的值
// System.out.println(map1.remove(1001));
// System.out.println("map1: " + map1);
// System.out.println("-----------------");
// //void clear() 清空map集合中所有的键值对元素
// map1.clear();
// System.out.println("map1: " + map1);
System.out.println("-----------------");
// boolean containsKey(Object key) 判断是否包含某个键
System.out.println(map1.containsKey(1001));
System.out.println("-----------------");
// boolean containsValue(Object value) 判断是否包含某个值
System.out.println(map1.containsValue("李四"));
System.out.println("-----------------");
//boolean isEmpty() 判断集合中是否有键值对
System.out.println(map1.isEmpty());
System.out.println("-----------------");
// int size() 获取键值对个数
System.out.println(map1.size());
}
}
map遍历
V get(Object key)
Set
Collection
Set<Map.Entry<K,V>> entrySet()
public class MapDemo2 {
public static void main(String[] args) {
//借助Map子类HashMap来创建对象
Map<Integer, String> map1 = new HashMap<>();
//String put(Integer key,String value) // 返回被覆盖的旧值
map1.put(1001, "张三");
map1.put(1002, "李四");
map1.put(1001, "王五");
map1.put(1003, "赵六");
map1.put(1004, "王二麻");
map1.put(1005, "王林");
System.out.println("map1: " + map1);
System.out.println("-----------------");
// V get(Object key) 根据键获取值
System.out.println(map1.get(1005));
System.out.println("map1: " + map1);
System.out.println("-----------------");
// Set<K> keySet() 将所有的键拿出来放到一个Set集合中
// Set<Integer> keys = map1.keySet();
Set<Integer> keys = map1.keySet();
for (Integer key : keys) {
System.out.println(key);
}
System.out.println("-----------------");
// Collection<V> values() 将所有的值拿出来放到一个Collection集合中
Collection<String> values = map1.values();
for (String value : values) {
System.out.println(value);
}
System.out.println("------------------");
// Set<Map.Entry<K,V>> entrySet() 将每个键值对拿出来放入到Set集合中
// map集合遍历方式1:一次获取所有的键值对,依次遍历得到每个键值对的键和值
Set<Map.Entry<Integer, String>> entries = map1.entrySet();
for (Map.Entry<Integer, String> entry : entries) {
// entry - 键值对
Integer key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "-" + value);
}
System.out.println("------------------");
// map集合遍历方式2:先获取所有的键,遍历键,根据键得到对应的值
for (Integer key : keys) {
// 根据键获取值
String value = map1.get(key);
System.out.println(key + "-" + value);
}
System.out.println("------------------");
}
}
需求
"aababcabcdabcde",获取字符串中每一个字母出现的次数要求结果:a(5)b(4)c(3)d(2)e(1)
public class MapTest1 {
public static void main(String[] args) {
String s1 = "aababcabcdabcde";
TreeMap<Character, Integer> map1 = new TreeMap<>();
char[] chars = s1.toCharArray();
for (char aChar : chars) {
//判断集合集合是否有该键
if(map1.containsKey(aChar)){
map1.put(aChar, map1.get(aChar)+1);
}else {
map1.put(aChar, 1);
}
}
StringBuilder sb = new StringBuilder();
Set<Map.Entry<Character, Integer>> entries = map1.entrySet();
for (Map.Entry<Character, Integer> entry : entries) {
Character c = entry.getKey();
Integer counts = entry.getValue();
sb.append(c).append("(").append(counts).append(")");
}
String res = sb.toString();
System.out.println(res);
}
}
LinkedHashMap
LinkedHashMap是HashMap的子类:底层数据结构是哈希表【唯一性】和双链表【有序】
public class LinkedHashMapDemo1 {
public static void main(String[] args) {
LinkedHashMap<Student3, String> map1 = new LinkedHashMap<>();
map1.put(new Student3("方直", 18), "打游戏");
map1.put(new Student3("张成阳", 16), "看动漫");
map1.put(new Student3("方直", 18), "看电影");
map1.put(new Student3("黄涛", 17), "看书");
map1.put(new Student3("康清宇", 14), "看美女");
Set<Map.Entry<Student3, String>> entries = map1.entrySet();
for (Map.Entry<Student3, String> entry : entries) {
Student3 key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "-" + value);
}
}
}
HashMap和Hashtable的区别
HashMap和Hashtable的区别
1、从源码上来看,HashMap有的方法,Hashtable中也有,只是单纯的使用没啥太大区别
2、HashMap的键和值都允许为null值, Hashtable的键和值都不允许为null
3、HashMap是不安全的集合类,Hashtable中的方法大部分都是被synchronized关键字修饰的,线程是安全的,效率比HashMap低一些。
public class HashtableDemo {
public static void main(String[] args) {
HashMap<String, String> map2 = new HashMap<>();
map2.put(null, "hello");
map2.put("java", null);
map2.put(null, null);
System.out.println("map2: " + map2);
Hashtable<String, String> map1 = new Hashtable<>();
map1.put(null, "hello");
map1.put("java", null);
map1.put(null, null);
System.out.println("map1: " + map1);
}
}
标签:java,list1,System,println,add,集合,public,out
From: https://www.cnblogs.com/lanzhi666/p/18677806