数组和集合的高级用法

转自: http://nshipster.com/kvc-collection-operators/



KVC Collection Operators

Written by Mattt Thompson on Dec 3rd, 2012

Rubyists laugh at Objective-C's bloated syntax.

Although we lost a few pounds over the summer with our sleek new object literals, those Red-headed bullies still taunt us with their map one-liners and their fancy Symbol#to_proc.

Really, a lot of how elegant (or clever) a language is comes down to how well it avoids loops. for,while; even fast enumeration expressions are a drag. No matter how you sugar-coat them, loops will be a block of code that does something that is much simpler to describe in natural language.

"get me the average salary of all of the employees in this array", versus...

double totalSalary = 0.0;
for (Employee *employee in employees) {
  totalSalary += [employee.salary doubleValue];
}
double averageSalary = totalSalary / [employees count];

Meh.

Fortunately, Key-Value Coding gives us a much more concise--almost Ruby-like--way to do this:

[employees valueForKeyPath:@"@avg.salary"];

KVC Collection Operators allows actions to be performed on a collection using key path notation invalueForKeyPath:. Any time you see @ in a key path, it denotes a particular aggregate function whose result can be returned or chained, just like any other key path.

Collection Operators fall into one of three different categories, according to the kind of value they return:

  • Simple Collection Operators return strings, numbers, or dates, depending on the operator.
  • Object Operators return an array.
  • Array and Set Operators return an array or set, depending on the operator.

The best way to understand how these work is to see them in action. Consider a Product class, and a products array with the following data:

@interface Product : NSObject
@property NSString *name;
@property double price;
@property NSDate *launchedOn;
@end

Key-Value Coding automatically boxes and un-boxes scalars into NSNumber or NSValue as necessary to make everything work.

Name Price Launch Date
iPhone 5$199September 21, 2012
iPad Mini$329November 2, 2012
MacBook Pro$1699June 11, 2012
iMac$1299November 2, 2012

Simple Collection Operators

  • @count: Returns the number of objects in the collection as an NSNumber.
  • @sum: Converts each object in the collection to a double, computes the sum, and returns the sum as an NSNumber.
  • @avg: Takes the double value of each object in the collection, and returns the average value as an NSNumber.
  • @max: Determines the maximum value using compare:. Objects must support comparison with one another for this to work.
  • @min: Same as @max, but returns the minimum value in the collection.

Example:

[products valueForKeyPath:@"@count"]; // 4
[products valueForKeyPath:@"@sum.price"]; // 3526.00
[products valueForKeyPath:@"@avg.price"]; // 881.50
[products valueForKeyPath:@"@max.price"]; // 1699.00
[products valueForKeyPath:@"@min.launchedOn"]; // June 11, 2012

Pro Tip: To get the aggregate value of an array or set of NSNumbers, you can simply pass selfas the key path after the operator, e.g. [@[@(1), @(2), @(3)] valueForKeyPath:@"@max.self"] (/via @davandermobile, citing Objective Sea)

Object Operators

Let's say we have an inventory array, representing the current stock of our local Apple store (which is running low on iPad Mini, and doesn't have the new iMac, which hasn't shipped yet):

NSArray *inventory = @[iPhone5, iPhone5, iPhone5, iPadMini, macBookPro, macBookPro];
  • @unionOfObjects / @distinctUnionOfObjects: Returns an array of the objects in the property specified in the key path to the right of the operator.@distinctUnionOfObjects removes duplicates, whereas @unionOfObjects does not.

Example:

[inventory valueForKeyPath:@"@unionOfObjects.name"]; // "iPhone 5", "iPhone 5", "iPhone 5", "iPad Mini", "MacBook Pro", "MacBook Pro"
[inventory valueForKeyPath:@"@distinctUnionOfObjects.name"]; // "iPhone 5", "iPad Mini", "MacBook Pro"

Array and Set Operators

Array and Set Operators are similar to Object Operators, but they work on collections of NSArrayand NSSet.

This would be useful if we were to, for example, compare the inventory of several stores, sayappleStoreInventory, (same as in the previous example) and verizonStoreInventory(which sells iPhone 5 and iPad Mini, and has both in stock).

  • @distinctUnionOfArrays / @unionOfArrays: Returns an array containing the combined values of each array in the collection, as specified by the key path to the right of the operator. As you'd expect, the distinct version removes duplicate values.
  • @distinctUnionOfSets: Similar to @distinctUnionOfArrays, but it expects an NSSetcontaining NSSet objects, and returns an NSSet. Because sets can't contain duplicate values anyway, there is only the distinct operator.

Example:

[@[appleStoreInventory, verizonStoreInventory] valueForKeyPath:@"@distinctUnionOfArrays.name"]; // "iPhone 5", "iPad Mini", "MacBook Pro"  

This is Probably a Terrible Idea

Curiously, Apple's documentation on KVC collection operators goes out of its way to make the following point:

Note: It is not currently possible to define your own collection operators.

This makes sense to spell out, since that's what most people are thinking about once they see collection operators for the first time.

However, as it turns out, it is actually possible, with a little help from our friend, objc/runtime.

Guy English has a pretty amazing post wherein he swizzles valueForKeyPath: to parse a custom-defined DSL, which extends the existing offerings to interesting effect:

NSArray *names = [allEmployees valueForKeyPath: @"[collect].{daysOff<10}.name"];

This code would get the names of anyone who has taken fewer than 10 days off (to remind them to take a vacation, no doubt!).

Or, taken to a ridiculous extreme:

NSArray *albumCovers = [records valueForKeyPath:@"[collect].{artist like 'Bon Iver'}.<NSUnarchiveFromDataTransformerName>.albumCoverImageData"];

Eat your heart out, Ruby. This one-liner filters a record collection for artists whose name matches "Bon Iver", and initializes an NSImage from the album cover image data of the matching albums.

Is this a good idea? Probably not. (NSPredicate is rad, and breaking complicated logic up is under-rated)

Is this insanely cool? You bet! This clever example has shown a possible direction for future Objective-C DSLs and meta-programming.


KVC Collection Operators are a must-know for anyone who wants to save a few extra lines of code and look cool in the process.

While scripting languages like Ruby boast considerably more flexibility in its one-liner capability, perhaps we should take a moment to celebrate the restraint built into Objective-C and Collection Operators. After all, Ruby is hella slow, amiright? </troll>


<think>首先,用户的问题是:“我想了解Java数组集合的区别 请问Java数组集合的区别”,这明确要求我解释Java中数组集合的区别。 我有系统级指令: - 所有行内数学表达式使用$...$格式(但这里可能不涉及数学表达式)。 - 独立公式使用$$...$$格式(同样,可能不适用)。 - LaTeX语法正确(主要是数学相关,但需注意)。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如[^1]。 用户提供了四个引用: - 引用[1]: 讨论Java数组集合的区别,包括存储方式、长度固定性等。 - 引用[2]: 详细比较数组集合的特性,如长度、存储类型、类型安全、内存分配、功能方法等。 - 引用[3]: 强调长度、数据类型等区别。 - 引用[4]: 提到集合类如ListMap。 我需要基于这些引用来回答,但要参考用户上一次的问题我的回答。用户上一次的问题是相同的:“我想了解Java数组集合的区别”,所以没有历史对话变化。我必须使用提供的引用来构建回答。 回答结构: - 结构清晰,帮助用户逐步解决问题。 - 保证真实可靠(基于引用)。 - 最后生成相关问题。 关键区别点(基于引用): 1. **长度(固定 vs 动态)**:数组长度固定,创建时指定;集合长度动态可变(例如ArrayList可通过扩容调整)。 2. **存储类型**:数组可以存储基本数据类型(如int, char)对象;集合只能存储对象引用(基本类型需用包装类如Integer)。 3. **类型安全**:数组在编译时不检查元素类型;集合使用泛型(Generics)保证类型安全(如List<String>)。 4. **内存分配访问**:数组是连续内存空间,索引访问O(1);集合如LinkedList可能需O(n)遍历。 5. **功能方法**:数组支持基础操作(索引访问),复杂操作需Arrays工具类;集合提供丰富方法(add, remove, sort等)。 6. **元素唯一性**:数组允许重复;集合如Set强制唯一性,List允许重复。 7. **转换**:数组集合可相互转换(如Arrays.asList(), list.toArray())。 组织回答: 1. **引言**:简要介绍数组集合是什么。 2. **主要区别**:列出关键点,逐步解释。 3. **源代码示例**:引用中提到给出源代码示例,我应该包括一些简单的Java代码来演示。 4. **总结**:简要概括。 5. **相关问题**:生成几个相关问题。 使用中文回答。 引用标识:在回答中,当引用具体内容时,在末尾添加[^n],例如基于引用[2]的内容添加[^2]。 生成相关问题:类似于示例,最后添加“相关问题”部分。 大纲: 1. **开头**:解释Java数组集合的基本概念。 2. **区别列表**: - 长度 - 存储类型 - 类型安全 - 内存访问效率 - 功能方法 - 元素唯一性 - 转换方式 3. **代码示例**:例如,创建一个数组一个ArrayList,展示添加元素等操作。 4. **总结**:何时使用数组,何时使用集合。 5. **相关问题**:生成2-3个相关问题。 相关问题想法: - Java中如何将数组转换为集合? - 哪些场景更适合使用数组而不是集合? - 集合框架中的主要接口有哪些? </think>### Java数组集合的区别 Java中的数组集合都是用于存储多个元素的容器,但它们在设计、功能用法上有显著差异。以下是基于核心概念的关键区别点,我将逐步解释并结合简单源代码示例说明。这些差异主要体现在长度动态性、存储类型、类型安全、性能功能方法上[^1][^2][^3]。 #### 1. **长度管理(固定 vs 动态)** - **数组**:长度在创建时固定,无法在运行时改变。例如,声明一个整数数组后,不能添加或删除元素。 ```java int[] arr = new int[3]; // 固定长度为3 arr[0] = 10; // 合法 // arr[3] = 20; // 错误:索引越界,数组长度无法扩展 ``` - **集合**:长度动态可变,支持自动扩容或缩容。例如,`ArrayList` 可在运行时添加元素。 ```java ArrayList<Integer> list = new ArrayList<>(); list.add(10); // 添加元素 list.add(20); // 动态扩容 list.remove(0); // 删除元素,长度自动调整 ``` **关键点**:数组适合已知固定大小的场景(如矩阵计算),而集合适用于元素数量不确定的应用(如用户输入处理)[^1][^2]。 #### 2. **存储类型(基本类型 vs 对象引用)** - **数组**:可直接存储基本数据类型(如 `int`, `char`)或对象引用(如 `String`)。 ```java int[] intArr = {1, 2, 3}; // 存储基本类型int String[] strArr = {"a", "b", "c"}; // 存储对象引用 ``` - **集合**:只能存储对象引用,基本类型需使用包装类(如 `Integer`, `Character`)。 ```java ArrayList<Integer> intList = new ArrayList<>(); intList.add(1); // 自动装箱为Integer对象 // ArrayList<int> invalid = ... // 错误:不能直接存储基本类型 ``` **关键点**:数组更高效用于数值计算(避免装箱开销),而集合强制面向对象设计[^2][^3]。 #### 3. **类型安全(编译时检查)** - **数组**:编译时不强制类型检查,可能导致运行时错误(如 `ClassCastException`)。 ```java Object[] objArr = new Integer[2]; objArr[0] = 10; // 合法 objArr[1] = "error"; // 运行时错误:ClassCastException ``` - **集合**:通过泛型(Generics)在编译时强制类型安全,减少错误。 ```java ArrayList<String> strList = new ArrayList<>(); strList.add("hello"); // 合法 // strList.add(100); // 编译错误:类型不匹配(Integer vs String) ``` **关键点**:集合的泛型机制提升代码健壮性,特别适合大型项目[^2]。 #### 4. **内存分配访问效率** - **数组**:元素存储在连续内存块中,索引访问时间复杂度为 $O(1)$(常量时间),适合高频随机访问。 ```java int[] arr = {10, 20, 30}; System.out.println(arr[1]); // 输出20,直接索引高效访问 ``` - **集合**:内存布局因实现而异: - `ArrayList` 基于数组,访问快($O(1)$),但插入/删除慢(需移动元素); - `LinkedList` 基于链表,访问慢($O(n)$),但插入/删除快; - `HashSet` 基于哈希表,访问平均 $O(1)$,但不保证顺序。 ```java LinkedList<Integer> linkedList = new LinkedList<>(); linkedList.add(10); linkedList.add(20); System.out.println(linkedList.get(1)); // 输出20,但需遍历链表(O(n)) ``` **关键点**:数组在性能敏感场景(如数值算法)更优;集合提供灵活的底层实现选择[^2][^4]。 #### 5. **功能方法与操作灵活性** - **数组**:仅支持基础操作(索引读写),复杂功能需借助工具类(如 `Arrays.sort()`)。 ```java int[] arr = {3, 1, 2}; Arrays.sort(arr); // 使用Arrays工具类排序 ``` - **集合**:内置丰富方法(添加、删除、排序、过滤等),通过接口(如 `List`, `Set`)扩展功能。 ```java ArrayList<Integer> list = new ArrayList<>(Arrays.asList(3, 1, 2)); list.add(4); // 直接添加元素 Collections.sort(list); // 使用Collections排序 list.removeIf(n -> n > 2); // 条件删除 ``` **关键点**:集合简化了数据操作(如批量处理),而数组需额外编码[^1][^4]。 #### 6. **元素唯一性与集合类型** - **数组**:始终允许重复元素。 ```java int[] arr = {1, 1, 2}; // 合法,元素可重复 ``` - **集合**:行为取决于具体子类: - `List`(如 `ArrayList`)允许重复; - `Set`(如 `HashSet`)强制唯一性。 ```java HashSet<Integer> set = new HashSet<>(); set.add(1); set.add(1); // 重复元素自动忽略 System.out.println(set); // 输出[1] ``` **关键点**:集合通过接口支持多样化需求(如去重使用 `Set`)[^2][^4]。 #### 7. **相互转换** 数组集合可互转,但需注意类型兼容: - **数组集合**:使用 `Arrays.asList()`(返回固定大小列表)。 ```java String[] arr = {"a", "b", "c"}; List<String> list = Arrays.asList(arr); // 数组转List list.set(0, "d"); // 修改列表会影响原数组 ``` - **集合数组**:使用 `toArray()` 方法。 ```java ArrayList<String> list = new ArrayList<>(); list.add("x"); String[] arr = list.toArray(new String[0]); // 集合数组 ``` **关键点**:转换涉及浅拷贝,适用于数据互通场景[^2][^3]。 ### 总结 - **使用数组**:当元素数量固定、需高性能访问(如数值计算)或存储基本类型时。 - **使用集合**:当元素数量动态变化、需高级操作(如排序、过滤)或类型安全时。集合框架(如 `List`, `Set`, `Map`)提供了更现代的API设计[^4]。 实际开发中,集合更常用(覆盖95%的场景),但数组在底层优化或遗留代码中仍有价值。 相关问题: 1. Java中如何高效地将数组转换为 `ArrayList`? 2. 在哪些场景下使用数组集合更有优势? 3. 集合框架中的 `List` `Set` 接口有何核心差异?
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值