您可以在列表、集和映射中管理 sObject。
- sObjects 列表 列表可以包含其他类型的元素中的 sObjects
。sObject 列表可用于批量处理数据。 - 对 sObject 列表进行排序 使用该方法,可以对 sObject
列表进行排序。List.sort - 扩展 sObject 和列表表达式
- 对象
集 集可以包含 sObject 以及其他类型的元素。 - sObject 映射映射键和值可以是任何数据类型,包括 sObject
类型,例如 Account。
sObject 列表
列表可以包含其他类型的元素中的 sObjects。 sObject 列表可用于批量处理数据。
您可以使用列表来存储 sObjects。列表在工作时很有用 使用 SOQL 查询。SOQL 查询返回 sObject 数据和此数据 可以存储在 sObject 列表中。此外,您可以使用列表来执行 批量操作,例如通过一次调用插入 sObject 列表。
若要声明 sObject 列表,请使用关键字后跟 sObject 键入 <> 个字符以内。例如:List
// Create an empty list of Accounts
List<Account> myList = new List<Account>();
从 SOQL 查询自动填充列表
您可以将 List 变量直接分配给 SOQL 查询的结果。SOQL 查询返回一个 使用返回的记录填充的新列表。确保声明的 List 变量 包含正在查询的相同 sObject。或者,您可以使用通用的 sObject 数据 类型。
此示例演示如何声明和分配帐户列表 更改为 SOQL 查询的返回值。查询最多返回 1,000 个 返回包含 Id 和 Name 字段的帐户记录。
// Create a list of account records from a SOQL query
List<Account> accts = [SELECT Id, Name FROM Account LIMIT 1000];
添加和检索列表元素
与原始数据类型列表一样,您可以使用 Apex 提供的方法访问和设置 sObject 列表的元素。例如:List
List<Account> myList = new List<Account>(); // Define a new list
Account a = new Account(Name='Acme'); // Create the account first
myList.add(a); // Add the account sObject
Account a2 = myList.get(0); // Retrieve the element at index 0
批量处理
您可以通过将列表传递给 DML 操作来批量处理 sObject 列表。这 示例演示如何插入 帐户。
// Define the list
List<Account> acctList = new List<Account>();
// Create account sObjects
Account a1 = new Account(Name='Account1');
Account a2 = new Account(Name='Account2');
// Add accounts to the list
acctList.add(a1);
acctList.add(a2);
// Bulk insert the list
insert acctList;
注意
如果批量插入知识文章版本,请将所有 记录相同。
记录 ID 生成
Apex 会自动为插入或更新插入的 sObject 列表中的每个对象生成 ID 使用 DML。因此,包含多个 sObject 实例的列表不能 即使它有 ID,也会插入或更新插入。这种情况意味着需要将两个 ID 写入相同的 ID 内存中的结构,这是非法的。null
例如,以下代码块中的语句生成 a 因为它 尝试插入一个列表,其中包含对同一 sObject () 的两个引用:insertListExceptiona
try {
// Create a list with two references to the same sObject element
Account a = new Account();
List<Account> accs = new List<Account>{a, a};
// Attempt to insert it...
insert accs;
// Will not get here
System.assert(false);
} catch (ListException e) {
// But will get here
}
对 s对象
或者,您可以使用数组表示法(正方形 括号)来声明和引用 sObject 的列表。此示例使用数组声明帐户列表 表示法。
Account[] accts = new Account[1];
以下示例使用方括号向列表中添加一个元素。
accts[0] = new Account(Name='Acme2');
这些示例还将数组表示法用于 sObject 列表。
例 | 描述 |
---|---|
List<Account> accts = new Account[]{}; | 定义不带元素的帐户列表。 |
List<Account> accts = new Account[] {new Account(), null, new Account()}; | 定义一个 Account 列表,其中包含为三个 Account 分配的内存:一个新的 Account 对象 第一个位置,在第二个位置, 以及第三个中的另一个新 Account 对象。null |
List<Contact> contacts = new List<Contact> (otherList); | 使用新列表定义联系人列表。 |
对 sObject 列表进行排序
使用该方法,您可以排序 sObject 的列表。
List.sort
对于 sObjects,排序按升序排列,并使用一系列比较步骤 在下一节中概述。您可以通过以下方式为 sObjects 创建自定义排序顺序 将 sObject 包装在实现接口的 Apex 类中。您还可以创建自定义 通过将作为参数实现的类传递给 sort 方法来排序顺序。请参阅 sObject 的自定义排序顺序。ComparableComparator
sObjects 的默认排序顺序
该方法对 sObjects 进行排序 升序并使用有序的步骤序列比较 sObjects 指定使用的标签或字段。比较从第一步开始 序列,并在使用指定的标签或字段对两个 sObject 进行排序时结束。这 以下是使用的比较顺序:
List.sort
- sObject 类型的标签。例如,将显示 Account sObject 在联系人之前。
- “名称”字段(如果适用)。例如,如果列表包含两个 名为 Alpha 和 Beta 的帐户,帐户 Alpha 在帐户之前 试用版。
- 标准字段,从按字母顺序排在第一位的字段开始 顺序,但 Id 和 Name 字段除外。例如,如果两个帐户 具有相同的名称,用于排序的第一个标准字段是 帐号。
- 自定义字段,从按字母顺序排在第一位的字段开始 次序。例如,假设两个帐户具有相同的名称和 相同的标准字段,并且有两个自定义字段,FieldA 和 FieldB,首先使用 FieldA 的值进行排序。
并非必须执行此序列中的所有步骤。例如,列表 包含两个相同类型且具有唯一 Name 值的 sObject 的排序依据 ,排序在步骤 2 处停止。否则,如果名称相同 或者 sObject 没有 Name 字段,则排序将转到步骤 3 进行排序 按标准字段。
对于文本字段,排序算法使用 Unicode 排序 次序。此外,在排序顺序中,空字段位于非空字段之前。
下面是对帐户 sObject 列表进行排序的示例。此示例演示 Name 字段用于将 Acme 帐户置于 列表。由于有两个名为 sForce 的帐户,因此 Industry 字段用于排序 这些剩余的帐户,因为 “行业 ”字段位于 按字母顺序排列。
Account[] acctList = new List<Account>();
acctList.add( new Account(
Name='sForce',
Industry='Biotechnology',
Site='Austin'));
acctList.add(new Account(
Name='sForce',
Industry='Agriculture',
Site='New York'));
acctList.add(new Account(
Name='Acme'));
System.debug(acctList);
acctList.sort();
Assert.areEqual('Acme', acctList[0].Name);
Assert.areEqual('sForce', acctList[1].Name);
Assert.areEqual('Agriculture', acctList[1].Industry);
Assert.areEqual('sForce', acctList[2].Name);
Assert.areEqual('Biotechnology', acctList[2].Industry);
System.debug(acctList);
此示例与上一个示例类似,只不过它使用了 Merchandise__c 自定义对象。此示例演示如何使用 Name 字段来放置笔记本 商品在列表中排在钢笔之前。因为商品有两个 sObject 对于“笔”的“名称”字段值,“说明”字段用于对它们进行排序 剩余商品。“说明”字段用于排序,因为它 按字母顺序位于 Price 和 Total_Inventory 字段之前。
Merchandise__c[] merchList = new List<Merchandise__c>();
merchList.add( new Merchandise__c(
Name='Pens',
Description__c='Red pens',
Price__c=2,
Total_Inventory__c=1000));
merchList.add( new Merchandise__c(
Name='Notebooks',
Description__c='Cool notebooks',
Price__c=3.50,
Total_Inventory__c=2000));
merchList.add( new Merchandise__c(
Name='Pens',
Description__c='Blue pens',
Price__c=1.75,
Total_Inventory__c=800));
System.debug(merchList);
merchList.sort();
Assert.areEqual('Notebooks', merchList[0].Name);
Assert.areEqual('Pens', merchList[1].Name);
Assert.areEqual('Blue pens', merchList[1].Description__c);
Assert.areEqual('Pens', merchList[2].Name);
Assert.areEqual('Red pens', merchList[2].Description__c);
System.debug(merchList);
sObjects 的自定义排序顺序
若要为列表中的 sObject 创建自定义排序顺序,请实现接口并将其作为参数传递 到方法。ComparatorList.sort
或者,为 sObject 创建一个包装类并实现接口。包装类包含 所讨论的 sObject 并实现指定排序逻辑的方法。ComparableComparable.compareTo
例
此示例实现基于“金额”字段比较两个商机的接口。Comparator
public class OpportunityComparator implements Comparator<Opportunity> {
public Integer compare(Opportunity o1, Opportunity o2) {
// The return value of 0 indicates that both elements are equal.
Integer returnValue = 0;
if(o1 == null && o2 == null) {
returnValue = 0;
} else if(o1 == null) {
// nulls-first implementation
returnValue = -1;
} else if(o2 == null) {
// nulls-first implementation
returnValue = 1;
} else if ((o1.Amount == null) && (o2.Amount == null)) {
// both have null Amounts
returnValue = 0;
} else if (o1.Amount == null){
// nulls-first implementation
returnValue = -1;
} else if (o2.Amount == null){
// nulls-first implementation
returnValue = 1;
} else if (o1.Amount < o2.Amount) {
// Set return value to a negative value.
returnValue = -1;
} else if (o1.Amount > o2.Amount) {
// Set return value to a positive value.
returnValue = 1;
}
return returnValue;
}
}
此测试对对象列表进行排序 并验证列表元素是否按商机数量排序。Comparator
@isTest
private class OpportunityComparator_Test {
@isTest
static void sortViaComparator() {
// Add the opportunity wrapper objects to a list.
List<Opportunity> oppyList = new List<Opportunity>();
Date closeDate = Date.today().addDays(10);
oppyList.add( new Opportunity(
Name='Edge Installation',
CloseDate=closeDate,
StageName='Prospecting',
Amount=50000));
oppyList.add( new Opportunity(
Name='United Oil Installations',
CloseDate=closeDate,
StageName='Needs Analysis',
Amount=100000));
oppyList.add( new Opportunity(
Name='Grand Hotels SLA',
CloseDate=closeDate,
StageName='Prospecting',
Amount=25000));
oppyList.add(null);
// Sort the objects using the Comparator implementation
oppyList.sort(new OpportunityComparator());
// Verify the sort order
Assert.isNull(oppyList[0]);
Assert.areEqual('Grand Hotels SLA', oppyList[1].Name);
Assert.areEqual(25000, oppyList[1].Amount);
Assert.areEqual('Edge Installation', oppyList[2].Name);
Assert.areEqual(50000, oppyList[2].Amount);
Assert.areEqual('United Oil Installations', oppyList[3].Name);
Assert.areEqual(100000, oppyList[3].Amount);
// Write the sorted list contents to the debug log.
System.debug(oppyList);
}
}
例
此示例演示如何为 Opportunity 创建包装类。此类中该方法的实现比较了两个 基于“金额”字段的商机 – 包含的类成员变量 在本例中,将 Opportunity 对象传递到方法中。ComparablecompareTo
public class OpportunityWrapper implements Comparable {
public Opportunity oppy;
// Constructor
public OpportunityWrapper(Opportunity op) {
// Guard against wrapping a null
if(op == null) {
Exception ex = new NullPointerException();
ex.setMessage('Opportunity argument cannot be null');
throw ex;
}
oppy = op;
}
// Compare opportunities based on the opportunity amount.
public Integer compareTo(Object compareTo) {
// Cast argument to OpportunityWrapper
OpportunityWrapper compareToOppy = (OpportunityWrapper)compareTo;
// The return value of 0 indicates that both elements are equal.
Integer returnValue = 0;
if ((oppy.Amount == null) && (compareToOppy.oppy.Amount == null)) {
// both wrappers have null Amounts
returnValue = 0;
} else if ((oppy.Amount == null) && (compareToOppy.oppy.Amount != null)){
// nulls-first implementation
returnValue = -1;
} else if ((oppy.Amount != null) && (compareToOppy.oppy.Amount == null)){
// nulls-first implementation
returnValue = 1;
} else if (oppy.Amount > compareToOppy.oppy.Amount) {
// Set return value to a positive value.
returnValue = 1;
} else if (oppy.Amount < compareToOppy.oppy.Amount) {
// Set return value to a negative value.
returnValue = -1;
}
return returnValue;
}
}
此测试对对象列表进行排序,并验证列表元素是否按商机排序 量。OpportunityWrapper
@isTest
private class OpportunityWrapperTest {
static testmethod void test1() {
// Add the opportunity wrapper objects to a list.
OpportunityWrapper[] oppyList = new List<OpportunityWrapper>();
Date closeDate = Date.today().addDays(10);
oppyList.add( new OpportunityWrapper(new Opportunity(
Name='Edge Installation',
CloseDate=closeDate,
StageName='Prospecting',
Amount=50000)));
oppyList.add( new OpportunityWrapper(new Opportunity(
Name='United Oil Installations',
CloseDate=closeDate,
StageName='Needs Analysis',
Amount=100000)));
oppyList.add( new OpportunityWrapper(new Opportunity(
Name='Grand Hotels SLA',
CloseDate=closeDate,
StageName='Prospecting',
Amount=25000)));
// Sort the wrapper objects using the implementation of the
// compareTo method.
oppyList.sort();
// Verify the sort order
Assert.areEqual('Grand Hotels SLA', oppyList[0].oppy.Name);
Assert.areEqual(25000, oppyList[0].oppy.Amount);
Assert.areEqual('Edge Installation', oppyList[1].oppy.Name);
Assert.areEqual(50000, oppyList[1].oppy.Amount);
Assert.areEqual('United Oil Installations', oppyList[2].oppy.Name);
Assert.areEqual(100000, oppyList[2].oppy.Amount);
// Write the sorted list contents to the debug log.
System.debug(oppyList);
}
}
扩展 sObject 和列表表达式
与 Java 一样,可以使用 method 扩展 sObject 和 list 表达式 引用和列表表达式,以形成新的表达式。在以下示例中,包含长度的新变量 的新帐户名称分配给 。
acctNameLength
Integer acctNameLength = new Account[]{new Account(Name='Acme')}[0].Name.length();
在上面,生成一个列表。new Account[]
该列表由语句填充一个元素。new{new Account(name=’Acme’)}
项目 0 是列表中的第一项,然后由下一个项目访问 字符串的一部分。[0]
访问列表中 sObject 的名称,后跟 返回长度的方法。name.length()在以下示例中,已移至 lower 的名称 大小写。SOQL 语句返回一个列表,其中第一个元素(索引为 0) 可通过 访问。 接下来,访问“名称”字段并将其转换为小写 表达。
[0].Name.toLowerCase()
String nameChange = [SELECT Name FROM Account][0].Name.toLowerCase();
对象集
集合可以包含 sObject 以及其他类型的元素。集合包含独特的元素。确定 sObject 的唯一性 通过比较对象的字段。例如,如果您尝试 将两个同名的帐户添加到一个集合中,没有其他字段 set,则该集合中仅添加一个 sObject。
// Create two accounts, a1 and a2
Account a1 = new account(name='MyAccount');
Account a2 = new account(name='MyAccount');
// Add both accounts to the new set
Set<Account> accountSet = new Set<Account>{a1, a2};
// Verify that the set only contains one item
System.assertEquals(accountSet.size(), 1);
如果您向其中一个帐户添加描述,则会将其视为 唯一,并且两个帐户都会添加到集合中。
// Create two accounts, a1 and a2, and add a description to a2
Account a1 = new account(name='MyAccount');
Account a2 = new account(name='MyAccount', description='My test account');
// Add both accounts to the new set
Set<Account> accountSet = new Set<Account>{a1, a2};
// Verify that the set contains two items
System.assertEquals(accountSet.size(), 2);
警告
如果 set 元素是对象,并且这些对象 添加到集合后更改,则找不到它们 例如,在使用 OR 方法时,由于字段值已更改。containscontainsAll
sObjects 的映射
映射键和值可以是任何数据类型,包括 sObject 类型,例如 Account。映射可以在其键和值中保存 sObject。地图键 表示映射到映射值的唯一值。例如,一个 通用键是映射到帐户的 ID(特定的 sObject 类型)。此示例演示如何定义其键类型为 ID,其值的类型为 Account。
Map<ID, Account> m = new Map<ID, Account>();
与基元类型一样,您可以在以下情况下填充映射键值对 映射是使用大括号 () 语法声明的。在大括号内, 首先指定键,然后使用 指定该键的值。本示例创建 将整数添加到帐户列表,并使用帐户列表添加一个条目 创建时间较早。{}=>
Account[] accs = new Account[5]; // Account[] is synonymous with List<Account>
Map<Integer, List<Account>> m4 = new Map<Integer, List<Account>>{1 => accs};
映射允许在其键中使用 sObject。仅当 sObject 字段时,才必须在键中使用 sObject 值不会改变。
从 SOQL 查询自动填充映射条目
使用 SOQL 查询时,可以从 SOQL 返回的结果填充映射 查询。映射键必须使用 ID 或 String 数据类型声明,并且映射 value 必须声明为 sObject 数据类型。此示例演示如何从查询填充新映射。在 例如,SOQL 查询返回带有其 和 字段的帐户列表。操作员使用返回的帐户列表来创建地图。
IdNamenew
// Populate map from SOQL query
Map<ID, Account> m = new Map<ID, Account>([SELECT Id, Name FROM Account LIMIT 10]);
// After populating the map, iterate through the map entries
for (ID idKey : m.keyset()) {
Account a = m.get(idKey);
System.debug(a);
}
此映射类型的一种常见用法是内存中 两个表之间的“联接”。
注意
作为多个社区成员的用户的最近查看的记录不能是 通过 Apex 自动检索到地图中。这是因为用户的记录 不同的网络可能会导致地图不支持的重复 ID。查看更多 信息,请参阅最近查看。
使用 Map 方法
该类公开了各种方法 可用于处理地图元素,例如添加、删除、 或检索元素。此示例使用 Map 方法添加新元素 并从地图中检索现有元素。此示例还检查 对于键的存在,并获取所有键的集合。地图在 此示例有一个元素,其中包含一个整数键和一个帐户值。Map
Account myAcct = new Account(); //Define a new account
Map<Integer, Account> m = new Map<Integer, Account>(); // Define a new map
m.put(1, myAcct); // Insert a new key-value pair in the map
System.assert(!m.containsKey(3)); // Assert that the map contains a key
Account a = m.get(1); // Retrieve a value, given a particular key
Set<Integer> s = m.keySet(); // Return a set that contains all of the keys in the map
- sObject 映射注意事项
sObject 映射注意事项
使用 sObject 作为映射键时要小心。sObject 的键匹配 基于所有 sObject 字段值的比较。如果一个或 将 sObject 添加到地图后,更多字段值发生变化,尝试 要从映射中检索此 sObject,则返回 。这是因为修改后的 由于字段值不同,在映射中找不到 sObject。 如果显式更改 sObject 上的字段,或者 如果系统隐式更改了 sObject 字段;例如 插入 sObject 后,sObject 变量具有 ID 字段 自动填充。尝试从它所到的映射中获取此对象 在操作不会生成映射条目之前添加,如本示例所示。
nullinsert
// Create an account and add it to the map
Account a1 = new Account(Name='A1');
Map<sObject, Integer> m = new Map<sObject, Integer>{
a1 => 1};
// Get a1's value from the map.
// Returns the value of 1.
System.assertEquals(1, m.get(a1));
// Id field is null.
System.assertEquals(null, a1.Id);
// Insert a1.
// This causes the ID field on a1 to be auto-filled
insert a1;
// Id field is now populated.
System.assertNotEquals(null, a1.Id);
// Get a1's value from the map again.
// Returns null because Map.get(sObject) doesn't find
// the entry based on the sObject with an auto-filled ID.
// This is because when a1 was originally added to the map
// before the insert operation, the ID of a1 was null.
System.assertEquals(null, m.get(a1));
另一种情况 其中自动填充的 sObject 字段位于触发器中,例如,当 对 sObject 使用插入触发器之前和之后。如果这些触发器 共享一个类中定义的静态映射,并将 中的 sObject 添加到该映射中 在 before 触发器中,在 after 触发器中找不到 映射,因为两组 sObject 的字段不同 是自动填充的。after 触发器中的 sObjects 在插入后填充了系统字段, 即:ID、CreatedDate、CreatedById、LastModifiedDate、LastModifiedById、 和 SystemModStamp。
Trigger.NewTrigger.NewTrigger.New