java基础(4)注解,集合,
注解
什么是注解(Annotation)?注解是放在Java源码的类、方法、字段、参数前的一种特殊“注释”
// this is a component:
@Resource("hello")
public class Hello {@Injectint n;@PostConstructpublic void hello(@Param String name) {System.out.println(name);}@Overridepublic String toString() {return "Hello";}
}
注释会被编译器直接忽略,注解则可以被编译器打包进入class文件,因此,注解是一种用作标注的“元数据”。
注解的作用
从JVM的角度看,注解本身对代码逻辑没有任何影响,如何使用注解完全由工具决定。
Java的注解可以分为三类:
- 由编译器使用的注解,
例如:@Override:让编译器检查该方法是否正确地实现了覆写;
@SuppressWarnings:告诉编译器忽略此处代码产生的警告
这类注解不会被编译进class文件中。
- 第二类是由工具处理.class文件使用的注解,比如有些工具会在加载class的时候,对class做动态修改,实现一些特殊的功能。一般只被底层库使用,不必自己处理。
- 第三类是在程序运行期能够读取的注解,它们在加载后一直存在于JVM中,这也是最常用的注解。例如,一个配置了@PostConstruct的方法会在调用构造方法后自动被调用(这是Java代码读取该注解实现的功能,JVM并不会识别该注解)
定义一个注解时,还可以定义配置参数。配置参数可以包括:
所有基本类型; String; 枚举类型; 基本类型、String、Class以及枚举的数组。
public class Hello {@Check(min=0, max=100, value=55)public int n;@Check(value=99)public int p;@Check(99) // @Check(value=99)public int x;@Checkpublic int y;
}
@Check就是一个注解。第一个@Check(min=0, max=100, value=55)明确定义了三个参数,第二个@Check(value=99)只定义了一个value参数,它实际上和@Check(99)是完全一样的。最后一个@Check表示所有参数都使用默认值。
定义注解
java 用@interface定义注解,然后添加参数、默认值:
public @interface Report {int type() default 0;String level() default "info";String value() default "";
}
接着有一些可以用于注解的注解,成为元注解。如
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Report {int type() default 0;String level() default "info";String value() default "";
}
@Target定义该注解能注解什么东西
类或接口:ElementType.TYPE;
字段:ElementType.FIELD;
方法:ElementType.METHOD;
构造方法:ElementType.CONSTRUCTOR;
方法参数:ElementType.PARAMETER。
@Retention定义该注解的生命周期,如上就是定义在runtime的时候运行,因为我们自己写的注解一般在runtime的时候执行。
仅编译期:RetentionPolicy.SOURCE;
仅class文件:RetentionPolicy.CLASS;
运行期:RetentionPolicy.RUNTIME。
其次还有@Repeatable定义该注解是否可以重复使用在同一个地方,@Inherited定义是否可以继承父类的注解。
处理注解
Java的注解本身对代码逻辑没有任何影响。根据@Retention的配置:
- SOURCE类型的注解在编译期就被丢掉了;
- CLASS类型的注解仅保存在class文件中,它们不会被加载进JVM;
- RUNTIME类型的注解会被加载进JVM,并且在运行期可以被程序读取。
使用注解
// 定义一个注解
@Retention(RetentionPolicy.RUNTIME) // runtime时运行,会打入jvm
@Target(ElementType.FIELD) // 作用于字段
@interface Range {int min() default 0;int max() default 255;
}class Person1 {@Range(min = 1, max = 20)public String name;@Range(max = 10)public String city;
}
定义了注解,本身对程序逻辑没有任何影响。我们必须自己编写代码来使用注解。
void check(Person person) throws IllegalArgumentException, ReflectiveOperationException {// 遍历所有Field:for (Field field : person.getClass().getFields()) {// 获取Field定义的@Range:Range range = field.getAnnotation(Range.class);// 如果@Range存在:if (range != null) {// 获取Field的值:Object value = field.get(person);// 如果值是String:if (value instanceof String s) {// 判断值是否满足@Range的min/max:if (s.length() < range.min() || s.length() > range.max()) {throw new IllegalArgumentException("Invalid field: " + field.getName());}}}}
}
集合
什么是集合(Collection)?集合就是“由若干个确定的元素所构成的整体”。
在Java中,如果一个Java对象可以在内部持有若干其他Java对象(注意是对象,而不是基础类型,比如Integer可以,但是int不行),并对外提供访问接口,我们把这种Java对象称为集合。很显然,Java的数组可以看作是一种集合:
String[] ss = new String[10]; // 可以持有10个String对象
ss[0] = "Hello"; // 可以放入String对象
String first = ss[0]; // 可以获取String对象
数组的限制,长度固定,数据只能通过索引获取。
我们需要各种不同类型的集合类来处理不同的数据,例如:
- 可变大小的顺序链表;
- 保证无重复元素的集合;
Collection
Java标准库自带的java.util包提供了集合类:Collection,它是除Map外所有其他集合类的根接口
ava的java.util包主要提供了以下三种类型的集合:
- List 一种有序列表的集合,例如,按索引排列的Student的List,如ArrayList, LinkedList
- Set 一种保证没有重复元素的集合,例如,所有无重复名称的Student的Set
- Map 一种通过键值(key-value)查找的映射表集合,例如,根据Student的name查找对应Student的Map
Java集合设计的特点
- 实现了 接口和 实现类 相分离,如有序表的接口是List,具体的实现类有ArrayList,LinkedList等
- 支持泛型 List list = new ArrayList<>(); // 只能放入String类型
- 访问集合都是通过迭代器(Iterator)的形式访问的,它最明显的好处在于无需知道集合内部元素是按什么方式存储的。(类似于js的实现Symbol.iterator,实现对象的迭代,外部无需知道他是怎么存储的。)
List
List是最基础的一种集合:它是一种有序列,数组和List非常相似。但是数组进行增删操作会非常麻烦。反观List
当我们需要增删操作的时候,可以使用ArrayList,
如 一个ArrayList拥有5个元素,实际数组大小为6(即有一个空位)
- 当添加一个元素并指定索引如2到ArrayList时,ArrayList自动移动需要移动的元素,把2及后面的元素移动。
- 然后,往内部指定索引的数组位置添加一个元素,然后把size加1
- 此时继续添加元素的时候,数组已满,没有空闲位置的时候,ArrayList先创建一个更大的新数组,然后把旧数组的所有元素复制到新数组,紧接着用新数组取代旧数组。
可见,ArrayList把添加和删除的操作封装起来,让我们操作List类似于操作数组,却不用关心内部元素如何移动。
List < T >主要的接口方法
在末尾添加一个元素:boolean add(E e)
在指定索引添加一个元素:boolean add(int index, E e)
删除指定索引的元素:E remove(int index)
删除某个元素:boolean remove(Object e)
获取指定索引的元素:E get(int index)
获取链表大小(包含元素的个数):int size()
上述是通过数组实现的List,实际上,使用链表也能实现。
在LinkedList中,它的内部每个元素都指向下一个元素:
┌───┬───┐ ┌───┬───┐ ┌───┬───┐ ┌───┬───┐
HEAD ──>│ A │ ●─┼──>│ B │ ●─┼──>│ C │ ●─┼──>│ D │ │└───┴───┘ └───┴───┘ └───┴───┘ └───┴───┘
我们来比较一下ArrayList和LinkedList:
123 | ArrayList | LinkedList |
---|---|---|
获取指定元素 | 速度很快 | 需要从头开始查找元素 |
添加元素到末尾 | 速度很快 | 速度很快 |
在指定位置添加/删除 | 需要移动元素 | 不需要移动元素 |
内存占用 | 少 | 较大 |
使用
List<String> list = new ArrayList<>();list.add("apple");list.add("pear");List<String> list1 = new LinkedList<>();list1.add("test1");list1.add("test2");// Java 9 List<Integer> list2 = List.of(1, 2, 5);System.out.println(list);System.out.println(list1);
遍历List,使用for循环
for (int i = 0; i < list.size(); i++) {String s = list.get(i);System.out.println(s);}
这种方法并不推荐
一是代码复杂,二是因为get(int)方法只有ArrayList的实现是高效的,换成LinkedList后,索引越大,访问速度越慢。
要始终坚持使用迭代器Iterator来访问List。Iterator本身也是一个对象,但它是由List的实例调用iterator()方法的时候创建的。
for (Iterator<String> it = list.iterator();it.hasNext();) {String s = it.next();System.out.println(s);}
调用iterator获取一个iterator对象,跟js的迭代器很类似。it.hasNext()判断是否还有下一个元素,it.next()返回下一个元素
以上是一种固定写法,第二个参数需要搭配it.hasNext()返回一个boolen判断是否继续循环。
Java的for each循环本身就可以帮我们使用Iterator遍历
for (String it1 : list1) {System.out.println(it1);}
可以直接遍历获取值。
实际上,只要实现了Iterable接口的集合类都可以直接用for each循环来遍历,Java编译器本身并不知道如何遍历集合对象,但它会自动把for each循环变成Iterator的调用
,原因就在于Iterable接口定义了一个Iterator<E> iterator()方法,强迫集合类必须返回一个Iterator实例。
有点像js中,只要实现了Symbol.iterator方法的对象,就可以使用for of进行遍历。因为Symbol.iteraotr方法返回了一个迭代器对象。
List和Array互相转换
- 把List变为Array有三种方法,第一种是调用toArray()方法直接返回一个Object[]数组,不常用,因为类型可能会丢失
- 第二种方式是给toArray(T[])传入一个类型相同的Array,List内部自动把元素复制到传入的Array中。这里的T和List< E>不一样,所以toArray可以传其他类型的数组,但不兼容会报错。
List<String> list1 = new LinkedList<>();list1.add("test1");list1.add("test2");String[] test2 = list1.toArray(new String[2]);Integer[] test3 = list1.toArray(new Integer[2]); //会报错
传入的数组如果不够大,那么List内部会创建一个新的数组,大小一样。
如果传入的数组过大,那么多余的元素全部填充null。
Array to List
// JAVA 9
Integer[] array = { 1, 2, 3 };
List<Integer> list = List.of(array);// JAVA 9 之前List<String> list3 = Arrays.asList(test2);
注意,这种返回的List不一定是ArrayList或者LinkedList,因为返回的List是只读的,在操作会报错。’
编写equals方法
List提供了contains方法,来判断当前list是否包含哪些内容。提供了indexOf(obj)方法来判断obj在当前list的位置
List<String> list3 = new ArrayList<>();list3.add("S");list3.add("C");System.out.println(list3.contains(new String("S"))); // trueSystem.out.println(list3.indexOf("S")); //0
这里contains我们传入了一个全新的String对象,但是结果还是为true,这是因为contains的实现是
public class ArrayList {Object[] elementData;public boolean contains(Object o) {for (int i = 0; i < elementData.length; i++) {if (o.equals(elementData[i])) {return true;}}return false;}
}
拿传入的对象,调用其equals方法,所以,要向正确的使用contains和indexOf,传入的对象就必须正确的实现equals方法。而String和Integer等对象,java已经帮我们实现了equals方法。
如何编写equals方法
- 先确定实例“相等”的逻辑,即哪些字段相等,就认为实例相等;
- 用instanceof判断传入的待比较的Object是不是当前类型,如果是,继续比较,否则,返回false;
- 对引用类型用Objects.equals()比较,对基本类型直接用==比较。
class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public boolean equals(Object o) {if (o instanceof Person) {Person p = (Person) o;boolean nameEquals = false;if (this.name == null && p.name == null) {nameEquals = true;}if (this.name != null) {nameEquals = this.name.equals(p.name);}return nameEquals && this.age == p.age;}return false;}// 使用Objects.equals()比较两个引用类型是否相等的目的是省去了判断null的麻烦。两个引用类型都是null时它们也是相等的。public boolean equals1(Object o) {if (o instanceof Person) {Person p = (Person) o;return Objects.equals(this.name, p.name) && this.age == p.age;}return false;}
}
Map
有个需求,在存放Students 集合 List中查找一个名为小明的,查看他的成绩。
这时候我们需要遍历List,然后判断到name微小明的,就取他的成绩。这样效率很低。
而Map这种key value的形式刚好可以满足。
Map<String, Person> map = new HashMap<>();map.put("lin", new Person("lin", 20));Person Target = map.get("lin");
和List类似,Map也是一个接口,最常用的实现类是HashMap。如果只是想查询某个key是否存在,可以调用boolean containsKey(K key)方法。
其他与js的Map类似。
遍历
Person Target = map.get("lin");for (String key : map.keySet()) {System.out.println(key);}for (Map.Entry<String, Person> entry : map.entrySet()) {String key = entry.getKey();Person value = entry.getValue();System.out.println(key);System.out.println(value);}
map.keySet()获取key,map.entrySet()获取key-value
- Map不保证顺序
编写equals 和 hashCode
Map之所以能很快通过key获取value,是因为内部通过空间换时间的处理。
Map内部用很大的数组存放数据,然后通过特殊的计算,比如f函数 f(key) = index,传入同一个key,index总是相同的,然后通过index存放value,取数据的时候也是通过f(key) = index计算出index,然后通过数组[index]去获取数据。但这样有个问题,如果传入的两个key是不同的对象呢。
Map<String, String> map2 = new HashMap<>();map2.put("2", "123");System.out.println(map2.get(new String("2"))); // 123
传入不同的String还是能拿到正确的值,因为跟List一样,key值得比对也是通过equals方法。
其次,通过key获取index是通过hashCode方法获取的,
正确使用Map必须保证:
- 作为key的对象必须正确覆写equals()方法,相等的两个key实例调用equals()必须返回true;
- 作为key的对象还必须正确覆写hashCode()方法,且hashCode()方法要严格遵循以下规范:
- 如果两个对象相等,则两个对象的hashCode()必须相等;
- 如果两个对象不相等,则两个对象的hashCode()尽量不要相等
public class Person {String firstName;String lastName;int age;@Overrideint hashCode() {int h = 0;h = 31 * h + firstName.hashCode();h = 31 * h + lastName.hashCode();h = 31 * h + age;return h;}
}
// 借助Objects.hash
int hashCode() {return Objects.hash(firstName, lastName, age);
}
equals()用到的用于比较的每一个字段,都必须在hashCode()中用于计算;
equals()中没有使用到的字段,绝不可放在hashCode()中计算。
这里注意,两个对象的hashCode尽量不要想等,也就是有可能相等。
我们把不同的key具有相同的hashCode()的情况称之为哈希冲突。在冲突的时候,一种最简单的解决办法是用List存储hashCode()相同的key-value。显然,如果冲突的概率越大,这个List就越长,Map的get()方法效率就越低,这就是为什么要尽量满足条件二。
hashCode()方法编写得越好,HashMap工作的效率就越高。
计算hash code,相同hash code用一个List存放。
EnumMap
HsahMap通过空间换时间,计算index从数组直接拿数据。
如果key是Enum的话,可以用EnumMap ,在内部以一个非常紧凑的数组存储value,并且根据enum类型的key直接定位到内部数组的索引,并不需要计算hashCode(),不但效率最高,而且没有额外的空间浪费。
public class Main {public static void main(String[] args) {Map<DayOfWeek, String> map = new EnumMap<>(DayOfWeek.class);map.put(DayOfWeek.MONDAY, "星期一");map.put(DayOfWeek.TUESDAY, "星期二");map.put(DayOfWeek.WEDNESDAY, "星期三");map.put(DayOfWeek.THURSDAY, "星期四");map.put(DayOfWeek.FRIDAY, "星期五");map.put(DayOfWeek.SATURDAY, "星期六");map.put(DayOfWeek.SUNDAY, "星期日");System.out.println(map);System.out.println(map.get(DayOfWeek.MONDAY));}
}
TreeMap
HashMap无序,还有一种Map,它在内部会对Key进行排序,这种Map就是SortedMap。注意到SortedMap是接口,它的实现类是TreeMap。
┌───┐│Map│└───┘▲┌────┴─────┐│ │
┌───────┐ ┌─────────┐
│HashMap│ │SortedMap│
└───────┘ └─────────┘▲│┌─────────┐│ TreeMap │└─────────┘
SortedMap保证遍历时以Key的顺序来进行排序。例如,放入的Key是"apple"、“pear”、“orange”,遍历的顺序一定是"apple"、“orange”、“pear”,因为String默认按字母排序
public static void main(String[] args) {Map<String, Integer> map = new TreeMap<>();map.put("orange", 1);map.put("apple", 2);map.put("pear", 3);for (String key : map.keySet()) {System.out.println(key);}// apple, orange, pear}
TreeMap要求实现Comparable方法
没有的话需要在使用TreeMap的时候指定排序方法,
public class Main {public static void main(String[] args) {Map<Person, Integer> map = new TreeMap<>(new Comparator<Person>() {public int compare(Person p1, Person p2) {return p1.name.compareTo(p2.name);}});map.put(new Person("Tom"), 1);map.put(new Person("Bob"), 2);map.put(new Person("Lily"), 3);for (Person key : map.keySet()) {System.out.println(key);}// {Person: Bob}, {Person: Lily}, {Person: Tom}System.out.println(map.get(new Person("Bob"))); // 2}
}
Comparator接口要求实现一个比较方法,它负责比较传入的两个元素a和b,如果a<b,则返回负数,通常是-1,如果a==b,则返回0,如果a>b,则返回正数,通常是1。TreeMap内部根据比较结果对Key进行排序
- SortedMap在遍历时严格按照Key的顺序遍历,最常用的实现类是TreeMap;
- 作为SortedMap的Key必须实现Comparable接口,或者传入Comparator;
- 要严格按照compare()规范实现比较逻辑,否则,TreeMap将不能正常工作。
Set
不重复的集合,且不需要value,类似于js的Set
Set<String> set = new HashSet<>();System.out.println(set.add("abc")); // trueSystem.out.println(set.add("xyz")); // trueSystem.out.println(set.add("xyz")); // false,添加失败,因为元素已存在System.out.println(set.contains("xyz")); // true,元素存在System.out.println(set.contains("XYZ")); // false,元素不存在System.out.println(set.remove("hello")); // false,删除失败,因为元素不存在System.out.println(set.size()); // 2,一共两个元素
set.add, set.contains, set.remove, set.resize
Set实际上相当于只存储key、不存储value的Map。我们经常用Set用于去除重复元素。
因为放入Set的元素和Map的key类似,都要正确实现equals()和hashCode()方法,否则该元素无法正确地放入Set
HashSet其实是封装HashMap
public class HashSet<E> implements Set<E> {// 持有一个HashMap:private HashMap<E, Object> map = new HashMap<>();// 放入HashMap的value:private static final Object PRESENT = new Object();public boolean add(E e) {return map.put(e, PRESENT) == null;}public boolean contains(Object o) {return map.containsKey(o);}public boolean remove(Object o) {return map.remove(o) == PRESENT;}
}
HasHSet是无序的,而SortedSet是有序的,但他只是个接口,实现的类是TreeSet
public class Main {public static void main(String[] args) {Set<String> set = new TreeSet<>();set.add("apple");set.add("banana");set.add("pear");set.add("orange");for (String s : set) {System.out.println(s);}}
}
使用TreeSet和使用TreeMap的要求一样,添加的元素必须正确实现Comparable接口,如果没有实现Comparable接口,那么创建TreeSet时必须传入一个Comparator对象。
Queue
队列,先进先出
它和List的区别在于,List可以在任意位置添加和删除元素,而Queue只有两个操作:
把元素添加到队列末尾;
从队列头部取出元素。
这些操作有两个方法,一个会抛出错误,一个返回false/null。
// 这是一个List:
List<String> list = new LinkedList<>();
// 这是一个Queue:
Queue<String> queue = new LinkedList<>();
LinkedList(链表)类实现了Queue接口和List接口
PriorityQueue
上述的Queue是先进先出,但是如果有优先级的处理,就像react中优先级高的渲染,就出现了优先级队列PriorityQueue,
PriorityQueue和Queue的区别在于,它的出队顺序与元素的优先级有关,对PriorityQueue调用remove()或poll()方法,返回的总是优先级最高的元素。(react-sceduler的实现类似)
public class Main {public static void main(String[] args) {Queue<String> q = new PriorityQueue<>();// 添加3个元素到队列:q.offer("apple");q.offer("pear");q.offer("banana");// 按照元素顺序System.out.println(q.poll()); // appleSystem.out.println(q.poll()); // bananaSystem.out.println(q.poll()); // pearSystem.out.println(q.poll()); // null,因为队列为空}
}
放入PriorityQueue的元素,必须实现Comparable接口,PriorityQueue会根据元素的排序顺序决定出队的优先级。
也可以在创建PriorityQeue的时候提供一个comparator对象来判断两个元素的顺序,跟TreeMap类似。
public class Main {public static void main(String[] args) {Queue<User> q = new PriorityQueue<>(new UserComparator());// 添加3个元素到队列:q.offer(new User("Bob", "A1"));q.offer(new User("Alice", "A2"));q.offer(new User("Boss", "V1"));System.out.println(q.poll()); // Boss/V1System.out.println(q.poll()); // Bob/A1System.out.println(q.poll()); // Alice/A2System.out.println(q.poll()); // null,因为队列为空}
}class UserComparator implements Comparator<User> {public int compare(User u1, User u2) {if (u1.number.charAt(0) == u2.number.charAt(0)) {// 如果两人的号都是A开头或者都是V开头,比较号的大小:return u1.number.compareTo(u2.number);}if (u1.number.charAt(0) == 'V') {// u1的号码是V开头,优先级高:return -1;} else {return 1;}}
}
Deque双端队列
,允许两头都进,两头都出,这种队列叫双端队列
Java集合提供了接口Deque来实现一个双端队列,它的功能是:
既可以添加到队尾,也可以添加到队首;
既可以从队首获取,又可以从队尾获取。
Deque是一个接口,它的实现类有ArrayDeque
和LinkedList
。
Stack 先进后出
将十进制转为16进制
public static void main(String[] args) {String hex = toHex(12500);System.out.println(hex);if (hex.equalsIgnoreCase("30D4")) {System.out.println("测试通过");} else {System.out.println("测试失败");}}static String toHex(int n) {Deque<String> dq = new LinkedList<>();int num = n % 16;int num1 = n / 16;while (num1 != 0) {dq.addFirst(Integer.toHexString(num));num = num1 % 16;num1 = num1 / 16;}dq.addFirst(Integer.toHexString(num));String result = "";String s = dq.pollFirst();System.out.println(s);while (s != null) {result += s;s = dq.pollFirst();}return result;}
循环求余数压入栈中,再循环取出。
Iterator
Java的集合类都可以使用for each循环,List、Set和Queue会迭代每个元素,Map会迭代每个key。因为他们都实现了Iterator对象
Java编译器并不知道如何遍历List。上述代码能够编译通过,只是因为编译器把for each循环通过Iterator改写为了普通的for循环:
for (Iterator<String> it = list.iterator(); it.hasNext(); ) {String s = it.next();System.out.println(s);
}
for (String s : list) {System.out.println(s);
}
类似于js的Symbol.Iterator迭代器,只要实现了Symbol.iterator,就可以用for of循环。
实现一个集合,需要实现他的Iterator方法,并且返回一个Iterator对象
class ReverseList<T> implements Iterable<T> {private List<T> list = new ArrayList<>();public void add(T t) {list.add(t);}@Overridepublic Iterator<T> iterator() {return new ReverseIterator(list.size());}class ReverseIterator implements Iterator<T> {int index;ReverseIterator(int index) {this.index = index;}@Overridepublic boolean hasNext() {return index > 0;}@Overridepublic T next() {index--;return ReverseList.this.list.get(index);}}
}
可以看到迭代器主要实现hasNext方法和next方法。js的Symbol.Iterator则是实现{done: ture, value: ‘’}来判断是否已经遍历结束。
Collections
Collections
是JDK提供的工具类,同样位于java.util包中。它提供了一系列静态方法,能更方便地操作各种集合。
// 空集合List<String> list2 = Collections.emptyList(); // 返回不可变List// 单元素集合List<String> list3 = Collections.singletonList("apple"); // 返回不可变ListList<String> list4 = Arrays.asList("1230", "456", "132"); // 返回不可变List// 排序Collections.sort(list4); // 排序回修改List元素的位置,所以需要传入可变ListSystem.out.println(list4);Collections.shuffle(list4); // 打乱List的顺序System.out.println(list4);// 变为不可变集合 假设List4是可变ListList<String> list5 = Collections.unmodifiableList(list4);// list5.add("orange"); // UnsupportedOperationException!list4.add("2222");list4 = null; // 转变后立马扔掉老的引用System.out.println(list5); // 老的List修改影响不可变List
小结
List
- Java集合使用统一的Iterator遍历
- 有序集合 List -> ArrayList(通过数组实现,) LinkedList(通过链表实现),可以用for each遍历。存放List的对象需要实现equals方法,便于调用List的contains和indexOf方法。
Map
-
key-value 映射表 -> Map , Map集合无序,可以通过for each遍历keySet(),也可以通过for each遍历entrySet(),直接获取key-value。最常用的Map就是HashMap
-
HashMap通过数组的形式存放,调用key.hashCode()获取索引,通过key.equals()判断是否相等,所以HashMap存放到对象需要实现hashCode和equals方法,而且equals方法返回true,则hashCode返回的索引一定相等,如果equals返回false,那么hashCode返回的值尽两不要相等。
- 但是万一相等了呢,HashMap的数组存放的是List<Entry<string, Object>>,当不同的key的HashCode返回的索引相等的时候,会拿到一个List,再通过key.equlas去获取到对应的值。
-
如果Map的key是enum类型,推荐使用EnumMap,既保证速度,也不浪费空间。使用EnumMap的时候,根据面向抽象编程的原则,应持有Map接口。
-
HashMap是无序的,但是Map下面还有一个SortedMap接口,TreeMap类实现了这个接口,他是有序的。
- TreeMap怎么排序呢,通过存放的每个对象实现一个comparable接口,或者在定义TreeMap的时候,指定一个自定义排序算法
-
Map<Person, Integer> map = new TreeMap<>(new Comparator<Person>() {public int compare(Person p1, Person p2) {return p1.name.compareTo(p2.name);}});
- Comparator接口要求实现一个比较方法,它负责比较传入的两个元素a和b,如果a<b,则返回负数,通常是-1,如果a==b,则返回0,如果a>b,则返回正数,通常是1。TreeMap内部根据比较结果对Key进行排序。
Set
- 只需要存储不重复的key,并不需要存储映射的value,那么就可以使用Set。
- 最常用的Set是HashSet,他是HashMap的封装
- 此外HashSet是无序的,但同时,SrotedSet是有序的,他是一个接口,TreeSet实现了这个接口
- 跟TreeMap一样,TreeSet也要求一个compator接口,来判断两个对象的顺序。
Queue 通过LinkedList类实现该接口
- 队列先进先出,通过add()/offer()方法将元素添加到队尾
- 通过remove/poll获取队首元素并挪去。
- 通过element()/peek()获取队首元素但不去掉。
- 前面的add/remove/eleemtn执行出错会报错,而offer/poll/peek只返回对应的false/null
- PriorityQueue优先级队列,优先级队列需要实现Comparable接口,priorityQueue根据元素的优先顺序决定谁先出队。
- PriorityQueue默认按元素比较的顺序排序(必须实现Comparable接口),也可以通过Comparator自定义排序算法(元素就不必实现Comparable接口)。
Deque 双头队列 也是LinkedList实现
- Deque其实也是继承于Queue的
- addLast/offerLast addFirst/offerFirst 从队头/队尾插入
- removeFirst/pollFirst removeLast/pollLast 从对头/队尾取出并删除
- getFirst/peekFirst getLast/peekLast 从对头/队尾取出但不删除
Stack 栈,先进后厨
- 利用Deque实现栈即可
- 只调用对应的offerFirst/peekFirst/getFirst三个方法。
- Stack作用很大,比如计算机调用方法通过执行栈维护等等
Iterator
Iterator是一种抽象的数据访问模型。使用Iterator模式进行迭代的好处有:
- 对任何集合都采用同一种访问模型;
- 调用者对集合内部结构一无所知;
- 集合类返回的Iterator对象知道如何迭代。
- Java提供了标准的迭代器模型,即集合类实现java.util.Iterable接口,返回java.util.Iterator实例。
Collections
Collections类提供了一组工具方法来方便使用集合类:
- Collections.emptyList()创建空集合 返回不可变集合
- Collections.singletonList(“test”)创建单个集合 返回不可变集合
- Collections.unmodifiableList(可变集合),将可变集合变成不可变集合
- Collections.sort()排序集合, Collections.shuffle() 将集合的顺序重新打乱(洗牌)
相关文章:

java基础(4)注解,集合,
注解 什么是注解(Annotation)?注解是放在Java源码的类、方法、字段、参数前的一种特殊“注释” // this is a component: Resource("hello") public class Hello {Injectint n;PostConstructpublic void hello(Param String name…...

基于springboot+vue的大学城水电管理系统(前后端分离)
博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战,欢迎高校老师\讲师\同行交流合作 主要内容:毕业设计(Javaweb项目|小程序|Pyt…...
代码随想录算法训练营第四十六天| 139.单词拆分、卡码网第56题
代码随想录算法训练营第四十六天| 139.单词拆分、卡码网第56题 139.单词拆分卡码网第56题 139.单词拆分 题目链接 文章讲解 class Solution { public:bool wordBreak(string s, vector<string>& wordDict) {int bagSize s.size(), n wordDict.size();vector<boo…...

Redis 在 Linux 系统下安装部署的两种方式详细说明
小伙伴们好,欢迎关注,一起学习,无限进步 Redis安装和配置 1、首先在官网下载好redis-6.0.9.tar.gzhttp://redis.io/ 或者使用 wget 命令下载:wget http://download.redis.io/releases/redis-6.0.9.tar.gz 2、下载使用上传到阿里…...

【茶话数据结构】查找最短路径——Dijkstra算法详解(保姆式详细图解,步步紧逼,保你学会)
💯 博客内容:【茶话数据结构】查找最短路径——Dijkstra算法详解 😀 作 者:陈大大陈 🦉所属专栏:数据结构笔记 🚀 个人简介:一个正在努力学技术的准前端,专注基础和实…...

Webserver解决segmentation fault(core dump)段错问问题
前言 在完成了整个项目后,我用make命令编译了server,当我运行./server文件时,出现了段错误 在大量的代码中找出错因并不是一件容易的事,尤其是对新手程序员来说。而寻找bug的过程就像是侦探调查线索追查凶手一样,我们…...

存储过程基本了解
文章目录 介绍存储过程示例1. 目的2. 输入参数3. 输出参数4. 执行逻辑5. 返回值6. 示例用法7. 注意事项 存储过程的关键字有哪些简单实操 介绍 存储过程是一组预编译的SQL语句,以及流程控制语句,封装在数据库服务器中并可以被重复调用。它们可以接收参数…...
『大模型笔记』RAG应用的12种调优策略指南
RAG应用的12种调优策略指南 文章目录 一. 概要二. 数据索引2.1. 数据清洗2.2. 分块2.3. 嵌入模型2.4. 元数据(或未向量化的数据)2.5. 多索引2.6. 索引算法三. 推理阶段(检索和生成)3.1. 检索参数3.2. 高级检索策略3.3. 重新排序模型3.5. 大语言模型(LLM)...

leedcode刷题--day7(字符串)
23 文章讲解 力扣地址 C class Solution { public:void reverseString(vector<char>& s) {int left 0;int right s.size() - 1; // right 应该初始化为 s.size() - 1while (left < right) {swap(s[left], s[right]); // 直接交换 s[left] 和 s[right] 的值lef…...

【蓝桥杯省赛真题31】python连续正整数之和 中小学青少年组蓝桥杯比赛python编程省赛真题解析
目录 python连续正整数之和 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 七、 推荐资料 1、蓝桥杯比赛 2、考级资料 3、其它资料 python连续正整数之和 第十二届蓝桥杯青少年组python比赛省赛真题 …...

【116个】网络安全测试相关面试真题
1、Burpsuite常用的功能是什么? 2、reverse_tcp和bind_tcp的区别? 3、拿到一个待检测的站或给你一个网站,你觉得应该先做什么? 4、你在渗透测试过程中是如何敏感信息收集的? 5、你平时去哪些网站进行学习、挖漏洞提…...

微服务day02-Ribbon负载均衡与Nacos安装与入门
一.Ribbon负载均衡 在上一节中,我们通过在RestTemplte实例中加上了注解 LoadBalanced,表示将来由RestTemplate发起的请求会被Ribbon拦截和处理,实现了访问服务时的负载均衡,那么他是如何实现的呢? 1.1 Ribbon负载均衡的原理 Rib…...

深度学习-神经网络原理
文章目录 神经网络原理1.单层神经网络1.1 回归单层神经网络:线性回归1.2 二分类单层神经网络:sigmoid与阶跃函数 1.3 多分类单层神经网络:softmax回归 神经网络原理 人工神经网络(Artificial Neural Network,ANN&…...
Chat GPT:智能对话的下一步
Chat GPT:智能对话的下一步 介绍 Chat GPT(Generative Pre-trained Transformer)是一种基于Transformer架构的强大对话模型,可以产生自然流畅的回答,并实现人机对话的感觉。本文将探讨Chat GPT在智能对话领域的影响和…...

[数据集][目标检测]鸡蛋破蛋数据集VOC+YOLO格式792张2类别
数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):792 标注数量(xml文件个数):792 标注数量(txt文件个数):792 标注类别…...

RabbitMQ实战学习
RabbitMQ实战学习 文章目录 RabbitMQ实战学习RabbitMQ常用资料1、安装教程2、使用安装包3、常用命令4、验证访问5、代码示例 一、RabbitMQ基本概念1.1. MQ概述1.2 MQ 的优势和劣势1.3 MQ 的优势1. 应用解耦2. 异步提速3. 削峰填谷 1.4 MQ 的劣势1.5 RabbitMQ 基础架构1.6 JMS 二…...

插混、油混、增程式、轻混、强混,啥区别
这里写自定义目录标题 随着我国新能源汽车的大力推进,电车可以说是世界未来的主流,只不过现在是处在一个过渡时代 这是个好时代,因为我们见证并体验着历史过渡的细节 这是个不好的时代,因为我们可能只是未来新新人类的试验品 帮他…...
React 模态框的设计(八)优化补充
在之前的弹窗的设计中,有两处地方现在做一点小小的优化,就是把_Draggable.jsx中的 onPointerEnter 事件 用 useLayoutEffect来规换,效果更佳,同样的,在_ModelContainer.jsx中也是一样。如下所示: _Draggabl…...
知识积累(三):深度学习相关概念(查看检索时看到)
文章目录 1. 知识蒸馏2. 可微搜索索引(DSI)参考资料 在找论文时,发现的相关概念。 1. 知识蒸馏 知识蒸馏(knowledge distillation)是模型压缩的一种常用的方法,不同于模型压缩中的剪枝和量化,知…...

计算机专业必看的几部电影
目录 编辑 1. 《第九区》(District 9,2009) 2. 《谍影重重》(The Bourne Identity,2002) 3. 《源代码》(Source Code,2011) 4. 《她》(Her,…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...

九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...