如何在 Java 对象列表中搜索
Posted
技术标签:
【中文标题】如何在 Java 对象列表中搜索【英文标题】:How to search in a List of Java object 【发布时间】:2012-10-19 19:00:57 【问题描述】:我有一个对象列表,并且列表非常大。对象是
class Sample
String value1;
String value2;
String value3;
String value4;
String value5;
现在我必须在列表中搜索对象的特定值。说如果value3=='three'
我必须返回那些对象(我的搜索并不总是基于value3)
名单是
List<Sample> list = new ArrayList<Sample>();
什么是有效的方法?
谢谢。
【问题讨论】:
你自己有没有尝试过? 为什么不创建 ListList<Sample>
,这是一个列表
如果您正在寻找大量对象的效率,我会考虑排序对象的层次结构,然后是一种有效的算法来跳过不可能。
【参考方案1】:
如果您总是根据value3
进行搜索,您可以将对象存储在地图中:
Map<String, List<Sample>> map = new HashMap <>();
然后,您可以使用 key = value3
和 value = 具有相同 value3
属性的 Sample 对象列表填充地图。
然后就可以查询地图了:
List<Sample> allSamplesWhereValue3IsDog = map.get("Dog");
注意:如果没有 2 个Sample
实例可以具有相同的value3
,您可以简单地使用Map<String, Sample>
。
【讨论】:
No.. 我的搜索并不总是基于 value3.. 它可以基于 object 中的任何字段 @Sun 这是性能和内存占用量之间的折衷问题:每个字段维护一个映射会更快但使用更多内存,每次需要搜索时循环遍历列表会更慢但使用更少的内存。 或者,如果你的对象结构是扁平的(只有字符串字段),你可以看看 Guava 的表——不过我从来没有用过。【参考方案2】:我修改了这个列表并在示例中添加了一个列表试试这个
伪代码
Sample
List<String> values;
List<String> getList()
return values
for(Sample s : list)
if(s.getString.getList.contains("three")
return s;
【讨论】:
我的列表很大.. 我正在寻找有效的搜索。这是唯一可能的方法吗? 您可以将值组织为哈希图,以避免检查 Sample 中的每个条目...【参考方案3】:由于您的列表是ArrayList
,因此可以假定它是未排序的。因此,没有办法搜索比 O(n) 更快的元素。
如果可以,您应该考虑将您的列表更改为Set
(以HashSet
作为实现),并为您的示例类指定一个特定的Comparator
。
另一种可能性是使用HashMap
。您可以将数据添加为Sample
(请以大写字母开头类名)并使用您要搜索的字符串作为关键字。然后你可以简单地使用
Sample samp = myMap.get(myKey);
如果每个键可以有多个样本,请使用Map<String, List<Sample>>
,否则使用Map<String, Sample>
。 如果您使用多个键,则必须创建多个包含相同数据集的地图。因为它们都指向相同的对象,所以空间应该不是什么大问题。
【讨论】:
【参考方案4】:你可以试试Apache Commons Collections。
有一个类 CollectionUtils 允许您通过自定义 Predicate 选择或过滤项目。
你的代码应该是这样的:
Predicate condition = new Predicate()
boolean evaluate(Object sample)
return ((Sample)sample).value3.equals("three");
;
List result = CollectionUtils.select( list, condition );
更新:
在 java8 中,使用 Lambda 和 StreamAPI 这应该是:
List<Sample> result = list.stream()
.filter(item -> item.value3.equals("three"))
.collect(Collectors.toList());
好多了!
【讨论】:
CollectionUtils 方法给了我这个错误:java.lang.NoSuchMethodError: No interface method stream()Ljava/util/stream/Stream; in class Ljava/util/List; or its super classes (declaration of 'java.util.List' appears in /system/framework/core-libart.jar
,我不能使用 Lambdas,因为 android SDK 不支持 Java 8。
@Yankee 遇到了同样的问题。你让它工作了吗?
@ono 我使用了 Java8 方法,效果很好。但请记住,您必须在 Gradle 文件中添加一些 Java 8 特定的依赖项
@Yankee 我有 gradle 进口,但我仍然得到它。你有这两个对吗? compileOptions sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
@ono 是的。这些在我的 build.gradle(Module:app) 中,在我的 build.gradle(项目)中有classpath 'me.tatarka:gradle-retrolambda:3.4.0'
【参考方案5】:
使用 Java 8
使用Java 8,您可以简单地将您的列表转换为stream,以便您编写:
import java.util.List;
import java.util.stream.Collectors;
List<Sample> list = new ArrayList<Sample>();
List<Sample> result = list.stream()
.filter(a -> Objects.equals(a.value3, "three"))
.collect(Collectors.toList());
注意
a -> Objects.equals(a.value3, "three")
是 lambda expression
result
是 List
和 Sample
类型
速度非常快,每次迭代都不会强制转换
如果您的过滤器逻辑变得更重,您可以使用list.parallelStream()
而不是list.stream()
(read this)
Apache Commons
如果你不能使用Java 8,你可以使用Apache Commons库并写:
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
Collection result = CollectionUtils.select(list, new Predicate()
public boolean evaluate(Object a)
return Objects.equals(((Sample) a).value3, "three");
);
// If you need the results as a typed array:
Sample[] resultTyped = (Sample[]) result.toArray(new Sample[result.size()]);
注意:
每次迭代都会有一个从Object
到 Sample
的演员表
如果您需要将结果输入为Sample[]
,则需要额外的代码(如我的示例所示)
奖励:nice blog article 谈论如何在列表中查找元素。
【讨论】:
有点可悲的是,在大多数情况下未能完成给定任务的答案得到了 13 个赞。请重新考虑应该如何比较字符串。 @Tom 到底是什么失败了? @Toms 谢谢 :) Aswer 已相应编辑 好吧a.value3.equals("three")
看起来更自然,但你的方式也是“空安全”,所以请保持:D。感谢您的更新,但我仍然想知道为什么没有其他人注意到它。由于您更喜欢其他语言(根据您的标签),我不能/不会责怪您,因为字符串比较在那里可能会有所不同。不过没关系,现在已经修好了。
@Tom "three".equals(((Sample) a).value3) 也可以工作并且是“null safe”。以上是关于如何在 Java 对象列表中搜索的主要内容,如果未能解决你的问题,请参考以下文章