在 Java 中搜索具有不同键的对象
Searching on an object with different keys in Java
我是一名从 C++ 过渡到 Java.So 的开发人员,我仍然不具备以 Java 方式完成工作的所有专业知识。
我有以下 class
class Site
{
String siteName;
Integer siteId;
Integer views;
Integer searches;
}
我维护了 2 个地图来搜索此 class(使用站点名称或站点 ID)
的对象
HashMap<String, Site> siteNameToSiteMap;
HashMap<Integer, Site> siteIdToSiteMap;
但是今后,我必须向 class 站点添加一个名为 parentBrand 的字段。这将迫使我创建另一个地图以便能够对其进行搜索。
HashMap<String, Site> parentBrandToSiteMap;
此类 "indexing" 变量可能会继续增加,因此也会增加我维护的地图数量。
我记得在用 C++ 开发时使用 Boost Multi-indexed container 解决了一个类似的问题。 Java 中是否有我可以使用的等效的支持良好、文档齐全的库。如果没有,有什么方法可以重构我的代码来解决我的问题。
您应该创建一个 Bean(容器),因为 Java 不需要代码优化,因为它无论如何都会被 JIT 编译器优化。
public class SiteMap {
String siteName;
Integer siteId;
String parentBrand;
.... Getters and setters ...
}
List<SiteMap> myList = new ArrayList<>();
如果您需要比较或排序,那么您可以在 SiteMap 上实现 Comparable 接口 class 允许您在需要时对细节进行排序。
你可以,如果使用 Java 8 那么也可以使用 Streams 来过滤或获取你想要的。因为有一个 fetchFirst
SiteMap mysite = myList.stream()
.filter(e -> e.siteName.equals("Amazon.com"))
.findFirst()
.get();
我想你可以通过列表搜索你的对象:
List<Site> sites;
for (Site s : sites) {
if (s.getSiteName().equal(siteName)) {
// do something
}
if (s.getSiteId().equal(siteId)) {
// do something
}
}
我很惊讶没有像 boost 多索引容器这样的版本可用。 (也许有什么地方......)但是在 Java 中连接你自己的版本并不难。
一个粗糙但有效的版本可能如下所示:
基本站点对象
为了简单起见,我使用了一个略有不同的 Site 对象(并且因为我无法在公共汽车上访问这个 post...)
public class Site {
Integer id;
String name;
String rating;
// .. Constructor and toString removed for brevity
}
封装版本
我稍后会介绍一些主力类,但它们有点丑。这只是为了展示最终界面的样子,一旦你把它包装了一点:
class SiteRepository {
private final MultiMap<Site> sites = new MultiMap<>();
public final AbstractMap<String, Site> byName = sites.addIndex((site)->site.name);
public final AbstractMap<Integer,Site> byId = sites.addIndex((site)->site.id);
public final AbstractMap<String,List<Site>> byRating = sites.addMultiIndex((Site site)->site.rating);
public void add(Site s) { sites.add(s); }
}
SiteRepository repo = new SiteRepository();
repo.add(...);
Site site = repo.byId.get(1234);
repo.byId.forEach((Integer id, Site s) -> System.err.printf(" %s => %s\n", id, s));
MultiMap 核心
可能应该称为 MultiIndex
,因为 MultiMap
有其他含义...
public static class MultiMap<V> {
public static class MultiMapIndex<K,V> extends AbstractMap<K,V> {
@Override
public Set<Entry<K, V>> entrySet() {
return map.entrySet();
}
HashMap<K,V> map = new HashMap<>();
}
public <K> MultiMapIndex<K,V> addIndex(Function<V, K> f) {
MultiMapIndex<K,V> result = new MultiMapIndex<>();
Consumer<V> e = (V v) -> result.map.put(f.apply(v), v);
mappers.add(e);
values.forEach(e);
return result;
}
public <K> MultiMapIndex<K,List<V>> addMultiIndex(Function<V, K> f) {
MultiMapIndex<K,List<V>> result = new MultiMapIndex<>();
Consumer<V> e = (V v) -> {
K key = f.apply(v);
List<V> list = result.map.get(key);
if (list == null) {
list = new ArrayList<>();
result.map.put(key, list);
}
list.add(v);
};
mappers.add(e);
values.forEach(e);
return result;
}
public void add(V v) {
values.add(v);
mappers.forEach( e -> e.accept(v));
}
private List<Consumer<V>> mappers = new ArrayList<>();
private List<V> values = new ArrayList<>();
}
更多底层示例
public static void main(String[] args) {
// Create a multi-map
MultiMap<Site> multiMap = new MultiMap<>();
// Add an index by Site.id
MultiMapIndex<Integer, Site> byId = multiMap.addIndex((site)->site.id);
// Add some entries to the map
multiMap.add(new Site(1234,"A Site","A"));
multiMap.add(new Site(4321,"Another Site","B"));
multiMap.add(new Site(7777,"My Site","A"));
// We can add a new index after the entries have been
// added - this time by name.
MultiMapIndex<String, Site> byName = multiMap.addIndex((site)->site.name);
// Get a value by Id.
System.err.printf("Get by id=7777 = %s\n", byId.get(7777));
// Get a value by Name
System.err.printf("Get by name='A Site' = %s\n", byName.get("A Site"));
// We can do usual mappy things with the indexes,
// such as list the keys, or iterate over all entries
System.err.printf("byId.keys() = %s\n", byId.keySet());
byId.forEach((Integer id, Site s) -> System.err.printf(" %s => %s\n", id, s));
// In some cases the map is not unique, so I provide a
// way to get all entries with the same value as a list.
// in this case by their rating value.
MultiMapIndex<String, List<Site>> byRating = multiMap.addMultiIndex((Site site)->site.rating);
System.err.printf("byRating('A') = %s\n", byRating.get("A"));
System.err.printf("byRating('B') = %s\n", byRating.get("B"));
// Adding stuff after creating the indices is fine.
multiMap.add(new Site(3333,"Last Site","B"));
System.err.printf("byRating('A') = %s\n", byRating.get("A"));
System.err.printf("byRating('B') = %s\n", byRating.get("B"));
}
}
我是一名从 C++ 过渡到 Java.So 的开发人员,我仍然不具备以 Java 方式完成工作的所有专业知识。 我有以下 class
class Site
{
String siteName;
Integer siteId;
Integer views;
Integer searches;
}
我维护了 2 个地图来搜索此 class(使用站点名称或站点 ID)
的对象HashMap<String, Site> siteNameToSiteMap;
HashMap<Integer, Site> siteIdToSiteMap;
但是今后,我必须向 class 站点添加一个名为 parentBrand 的字段。这将迫使我创建另一个地图以便能够对其进行搜索。
HashMap<String, Site> parentBrandToSiteMap;
此类 "indexing" 变量可能会继续增加,因此也会增加我维护的地图数量。
我记得在用 C++ 开发时使用 Boost Multi-indexed container 解决了一个类似的问题。 Java 中是否有我可以使用的等效的支持良好、文档齐全的库。如果没有,有什么方法可以重构我的代码来解决我的问题。
您应该创建一个 Bean(容器),因为 Java 不需要代码优化,因为它无论如何都会被 JIT 编译器优化。
public class SiteMap {
String siteName;
Integer siteId;
String parentBrand;
.... Getters and setters ...
}
List<SiteMap> myList = new ArrayList<>();
如果您需要比较或排序,那么您可以在 SiteMap 上实现 Comparable 接口 class 允许您在需要时对细节进行排序。
你可以,如果使用 Java 8 那么也可以使用 Streams 来过滤或获取你想要的。因为有一个 fetchFirst
SiteMap mysite = myList.stream()
.filter(e -> e.siteName.equals("Amazon.com"))
.findFirst()
.get();
我想你可以通过列表搜索你的对象:
List<Site> sites;
for (Site s : sites) {
if (s.getSiteName().equal(siteName)) {
// do something
}
if (s.getSiteId().equal(siteId)) {
// do something
}
}
我很惊讶没有像 boost 多索引容器这样的版本可用。 (也许有什么地方......)但是在 Java 中连接你自己的版本并不难。
一个粗糙但有效的版本可能如下所示:
基本站点对象
为了简单起见,我使用了一个略有不同的 Site 对象(并且因为我无法在公共汽车上访问这个 post...)
public class Site {
Integer id;
String name;
String rating;
// .. Constructor and toString removed for brevity
}
封装版本
我稍后会介绍一些主力类,但它们有点丑。这只是为了展示最终界面的样子,一旦你把它包装了一点:
class SiteRepository {
private final MultiMap<Site> sites = new MultiMap<>();
public final AbstractMap<String, Site> byName = sites.addIndex((site)->site.name);
public final AbstractMap<Integer,Site> byId = sites.addIndex((site)->site.id);
public final AbstractMap<String,List<Site>> byRating = sites.addMultiIndex((Site site)->site.rating);
public void add(Site s) { sites.add(s); }
}
SiteRepository repo = new SiteRepository();
repo.add(...);
Site site = repo.byId.get(1234);
repo.byId.forEach((Integer id, Site s) -> System.err.printf(" %s => %s\n", id, s));
MultiMap 核心
可能应该称为 MultiIndex
,因为 MultiMap
有其他含义...
public static class MultiMap<V> {
public static class MultiMapIndex<K,V> extends AbstractMap<K,V> {
@Override
public Set<Entry<K, V>> entrySet() {
return map.entrySet();
}
HashMap<K,V> map = new HashMap<>();
}
public <K> MultiMapIndex<K,V> addIndex(Function<V, K> f) {
MultiMapIndex<K,V> result = new MultiMapIndex<>();
Consumer<V> e = (V v) -> result.map.put(f.apply(v), v);
mappers.add(e);
values.forEach(e);
return result;
}
public <K> MultiMapIndex<K,List<V>> addMultiIndex(Function<V, K> f) {
MultiMapIndex<K,List<V>> result = new MultiMapIndex<>();
Consumer<V> e = (V v) -> {
K key = f.apply(v);
List<V> list = result.map.get(key);
if (list == null) {
list = new ArrayList<>();
result.map.put(key, list);
}
list.add(v);
};
mappers.add(e);
values.forEach(e);
return result;
}
public void add(V v) {
values.add(v);
mappers.forEach( e -> e.accept(v));
}
private List<Consumer<V>> mappers = new ArrayList<>();
private List<V> values = new ArrayList<>();
}
更多底层示例
public static void main(String[] args) {
// Create a multi-map
MultiMap<Site> multiMap = new MultiMap<>();
// Add an index by Site.id
MultiMapIndex<Integer, Site> byId = multiMap.addIndex((site)->site.id);
// Add some entries to the map
multiMap.add(new Site(1234,"A Site","A"));
multiMap.add(new Site(4321,"Another Site","B"));
multiMap.add(new Site(7777,"My Site","A"));
// We can add a new index after the entries have been
// added - this time by name.
MultiMapIndex<String, Site> byName = multiMap.addIndex((site)->site.name);
// Get a value by Id.
System.err.printf("Get by id=7777 = %s\n", byId.get(7777));
// Get a value by Name
System.err.printf("Get by name='A Site' = %s\n", byName.get("A Site"));
// We can do usual mappy things with the indexes,
// such as list the keys, or iterate over all entries
System.err.printf("byId.keys() = %s\n", byId.keySet());
byId.forEach((Integer id, Site s) -> System.err.printf(" %s => %s\n", id, s));
// In some cases the map is not unique, so I provide a
// way to get all entries with the same value as a list.
// in this case by their rating value.
MultiMapIndex<String, List<Site>> byRating = multiMap.addMultiIndex((Site site)->site.rating);
System.err.printf("byRating('A') = %s\n", byRating.get("A"));
System.err.printf("byRating('B') = %s\n", byRating.get("B"));
// Adding stuff after creating the indices is fine.
multiMap.add(new Site(3333,"Last Site","B"));
System.err.printf("byRating('A') = %s\n", byRating.get("A"));
System.err.printf("byRating('B') = %s\n", byRating.get("B"));
}
}